Skip to content

Commit a0610f4

Browse files
committed
refactor: AFHMM & AFHMM+SAC: split disaggregate_thread function into the constraints setup and the actual minimization
1 parent 4dab5d0 commit a0610f4

File tree

2 files changed

+91
-218
lines changed

2 files changed

+91
-218
lines changed

nilmtk_contrib/disaggregate/afhmm.py

+83-97
Original file line numberDiff line numberDiff line change
@@ -66,114 +66,100 @@ def partial_fit(self, train_main, train_appliances, **load_kwargs):
6666

6767
print ("{}: Finished training".format(self.MODEL_NAME))
6868

69-
def disaggregate_thread(self, test_mains,index,d):
70-
means_vector = self.means_vector
71-
pi_s_vector = self.pi_s_vector
72-
means_vector = self.means_vector
73-
transmat_vector = self.transmat_vector
74-
75-
sigma = 100*np.ones((len(test_mains),1))
76-
flag = 0
77-
78-
for epoch in range(6):
79-
# The alernative minimization
80-
if epoch%2==1:
81-
usage = np.zeros((len(test_mains)))
82-
for appliance_id in range(self.num_appliances):
83-
app_usage= np.sum(s_[appliance_id]@means_vector[appliance_id],axis=1)
84-
usage+=app_usage
85-
sigma = (test_mains.flatten() - usage.flatten()).reshape((-1,1))
86-
sigma = np.where(sigma<1,1,sigma)
69+
def setup_cvx_constraints(self, n_samples, n_appliances):
70+
cvx_state_vectors = [
71+
cvx.Variable(
72+
shape=( n_samples, self.default_num_states ),
73+
name="state_vec-{}".format(i)
74+
)
75+
for i in range(n_appliances)
76+
]
77+
constraints = []
78+
for stv in cvx_state_vectors:
79+
# State vector values are ranged.
80+
constraints += [ stv >= 0, stv <= 1 ]
81+
# Sum of states equals 1.
82+
for t in range(n_samples):
83+
constraints.append(cvx.sum(stv[t]) == 1)
84+
# Create variable matrices for each appliance, for each sample.
85+
cvx_variable_matrices = [
86+
[
87+
cvx.Variable(
88+
shape=( self.default_num_states, self.default_num_states ),
89+
name="variable_matrix-{}-{}".format(i, t)
90+
)
91+
for t in range(n_samples)
92+
]
93+
for i in range(n_appliances)
94+
]
95+
for i, appli_varmats in enumerate(cvx_variable_matrices):
96+
for t, varmat in enumerate(appli_varmats):
97+
# Assign range constraints to variable matrices.
98+
constraints += [ varmat >= 0, varmat <= 1 ]
99+
# Assign equality constraints with state vectors.
100+
constraints += [
101+
cvx.sum(varmat[l]) == cvx_state_vectors[i][t-1][l]
102+
for l in range(self.default_num_states)
103+
]
104+
constraints += [
105+
cvx.sum((varmat.T)[l]) == cvx_state_vectors[i][t][l]
106+
for l in range(self.default_num_states)
107+
]
108+
109+
return cvx_state_vectors, constraints, cvx_variable_matrices
110+
111+
def disaggregate_thread(self, test_mains):
112+
n_epochs = 6 # don't put in __init__, those are inference epochs!
113+
n_samples = len(test_mains)
114+
sigma = 100*np.ones(( n_samples, 1 ))
115+
cvx_state_vectors, constraints, cvx_varmats = self.setup_cvx_constraints(
116+
n_samples, len(self.means_vector))
117+
# Preparing first terms of the objective function.
118+
term_1 = 0
119+
term_2 = 0
120+
total_appli_energy = np.zeros_like(test_mains)
121+
for i, (appli_name, means) in enumerate(self.means_vector.items()):
122+
total_appli_energy += cvx_state_vectors[i]@means
123+
appli_varmats = cvx_varmats[i]
124+
transmat = self.transmat_vector[appli_name]
125+
for varmat in appli_varmats:
126+
term_1 -= cvx.sum(cvx.multiply(varmat, np.log(transmat)))
127+
128+
first_hot_state = cvx_state_vectors[i][0]
129+
transition_p = self.pi_s_vector[appli_name]
130+
term_2 -= cvx.sum(cvx.multiply(first_hot_state, np.log(transition_p)))
131+
132+
for epoch in range(n_epochs):
133+
if epoch % 2:
134+
# Alernative minimization on odd epochs.
135+
usage = np.zeros(( n_samples, ))
136+
for i, (appli_name, means) in enumerate(self.means_vector.items()):
137+
usage += np.sum(s_[i]@means, axis=1)
138+
sigma = (test_mains.flatten() - usage.flatten()).reshape(( -1, 1 ))
139+
sigma = np.where(sigma < 1, 1, sigma)
87140
else:
88-
if flag==0:
89-
constraints = []
90-
cvx_state_vectors = []
91-
cvx_variable_matrices = []
92-
delta = cvx.Variable(shape=(len(test_mains),1), name='delta_t')
93-
for appliance_id in range(self.num_appliances):
94-
state_vector = cvx.Variable(
95-
shape=(len(test_mains), self.default_num_states),
96-
name='state_vec-%s'%(appliance_id)
97-
)
98-
cvx_state_vectors.append(state_vector)
99-
# Enforcing that their values are ranged
100-
constraints+=[cvx_state_vectors[appliance_id]>=0]
101-
constraints+=[cvx_state_vectors[appliance_id]<=1]
102-
# Enforcing that sum of states equals 1
103-
for t in range(len(test_mains)): # 6c
104-
constraints+=[cvx.sum(cvx_state_vectors[appliance_id][t])==1]
105-
# Creating Variable matrices for every appliance
106-
appliance_variable_matrix = []
107-
for t in range(len(test_mains)):
108-
matrix = cvx.Variable(
109-
shape=(self.default_num_states, self.default_num_states),
110-
name='variable_matrix-%s-%d'%(appliance_id,t)
111-
)
112-
appliance_variable_matrix.append(matrix)
113-
cvx_variable_matrices.append(appliance_variable_matrix)
114-
# Enforcing that their values are ranged
115-
for t in range(len(test_mains)):
116-
constraints+=[cvx_variable_matrices[appliance_id][t]>=0]
117-
constraints+=[cvx_variable_matrices[appliance_id][t]<=1]
118-
# Constraint 6e
119-
for t in range(0,len(test_mains)): # 6e
120-
for i in range(self.default_num_states):
121-
constraints+=[cvx.sum(((cvx_variable_matrices[appliance_id][t]).T)[i]) == cvx_state_vectors[appliance_id][t][i]]
122-
# Constraint 6d
123-
for t in range(1,len(test_mains)): # 6d
124-
for i in range(self.default_num_states):
125-
constraints+=[
126-
cvx.sum(cvx_variable_matrices[appliance_id][t][i]) == cvx_state_vectors[appliance_id][t-1][i]
127-
]
128-
129-
total_observed_reading = np.zeros((test_mains.shape))
130-
# Total observed reading equals the sum of each appliance
131-
for appliance_id in range(self.num_appliances):
132-
total_observed_reading+=cvx_state_vectors[appliance_id]@means_vector[appliance_id]
133-
134-
# Loss function to be minimized
135-
term_1 = 0
136-
term_2 = 0
137-
for appliance_id in range(self.num_appliances):
138-
# First loop is over appliances
139-
variable_matrix = cvx_variable_matrices[appliance_id]
140-
transmat = transmat_vector[appliance_id]
141-
# Next loop is over different time-stamps
142-
for matrix in variable_matrix:
143-
term_1-=cvx.sum(cvx.multiply(matrix,np.log(transmat)))
144-
one_hot_states = cvx_state_vectors[appliance_id]
145-
pi = pi_s_vector[appliance_id]
146-
# The expression involving start states
147-
first_one_hot_states = one_hot_states[0]
148-
term_2-= cvx.sum(cvx.multiply(first_one_hot_states,np.log(pi)))
149-
flag = 1
150-
151-
expression = 0
141+
# Primary minimization on even epochs.
152142
term_3 = 0
153143
term_4 = 0
144+
for t in range(n_samples):
145+
term_3 += .5 * (np.log(sigma[t]**2))
146+
term_4 += .5 * ((test_mains[t][0] - total_appli_energy[t][0])**2 / (sigma[t]**2))
154147

155-
for t in range(len(test_mains)):
156-
term_4+= .5 * ((test_mains[t][0] - total_observed_reading[t][0])**2 / (sigma[t]**2))
157-
term_3+= .5 * (np.log(sigma[t]**2))
158-
159-
expression = term_1 + term_2 + term_3 + term_4
160-
expression = cvx.Minimize(expression)
161-
prob = cvx.Problem(expression, constraints,)
162-
prob.solve(solver=cvx.SCS,verbose=False,warm_start=True)
148+
objective = cvx.Minimize(term_1 + term_2 + term_3 + term_4)
149+
prob = cvx.Problem(objective, constraints)
150+
prob.solve(solver=cvx.SCS, verbose=False, warm_start=True)
163151
s_ = [
164-
np.zeros((len(test_mains), self.default_num_states)) if i.value is None
152+
np.zeros((n_samples, self.default_num_states)) if i.value is None
165153
else i.value
166154
for i in cvx_state_vectors
167155
]
168156

169157
prediction_dict = {}
170-
for appliance_id in range(self.num_appliances):
171-
app_name = self.appliances[appliance_id]
172-
app_usage= np.sum(s_[appliance_id]@means_vector[appliance_id],axis=1)
173-
prediction_dict[app_name] = app_usage.flatten()
158+
for i, (appli_name, means) in enumerate(self.means_vector.items()):
159+
app_usage = np.sum(s_[i]@means, axis=1)
160+
prediction_dict[appli_name] = app_usage.flatten()
174161

175-
# Store the result in the index corresponding to the thread.
176-
d[index] = pd.DataFrame(prediction_dict,dtype='float32')
162+
return pd.DataFrame(prediction_dict, dtype="float32")
177163

178164
@nilmtk.docinherit.doc_inherit
179165
def disaggregate_chunk(self, test_mains):
+8-121
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import cvxpy as cvx
2-
import numpy as np
3-
import pandas as pd
42

53
from nilmtk_contrib.disaggregate import AFHMM
64

@@ -14,124 +12,13 @@ def __init__(self, params):
1412
super().__init__(params)
1513
self.MODEL_NAME = 'AFHMM_SAC'
1614

17-
def disaggregate_thread(self, test_mains,index,d):
18-
means_vector = self.means_vector
19-
pi_s_vector = self.pi_s_vector
20-
means_vector = self.means_vector
21-
transmat_vector = self.transmat_vector
22-
sigma = 100*np.ones((len(test_mains),1))
23-
flag = 0
24-
for epoch in range(6):
25-
if epoch%2==1:
26-
# The alernative Minimization
27-
usage = np.zeros((len(test_mains)))
28-
for appliance_id in range(self.num_appliances):
29-
app_usage= np.sum(s_[appliance_id]@means_vector[appliance_id],axis=1)
30-
usage+=app_usage
31-
sigma = (test_mains.flatten() - usage.flatten()).reshape((-1,1))
32-
sigma = np.where(sigma<1,1,sigma)
33-
else:
34-
if flag==0:
35-
constraints = []
36-
cvx_state_vectors = []
37-
cvx_variable_matrices = []
38-
delta = cvx.Variable(shape=(len(test_mains),1), name='delta_t')
15+
def setup_cvx_constraints(self, n_samples, n_appliances):
16+
cvx_state_vectors, constraints, cvx_variable_matrices = super().setup_cvx_constraints(n_samples, n_appliances)
17+
# Constraints on signal aggregates.
18+
for i, (appli_name, signal_aggregate) in enumerate(self.signal_aggregates.items()):
19+
appliance_usage = cvx_state_vectors[i]@self.means_vector[appli_name]
20+
total_appliance_usage = cvx.sum(appliance_usage)
21+
constraints.append(total_appliance_usage <= signal_aggregate)
3922

40-
for appliance_id in range(self.num_appliances):
41-
state_vector = cvx.Variable(
42-
shape=(len(test_mains),
43-
self.default_num_states),
44-
name='state_vec-%s'%(appliance_id)
45-
)
46-
cvx_state_vectors.append(state_vector)
47-
# Enforcing that their values are ranged
48-
constraints+=[cvx_state_vectors[appliance_id]>=0]
49-
constraints+=[cvx_state_vectors[appliance_id]<=1]
50-
# Enforcing that sum of states equals 1
51-
for t in range(len(test_mains)): # 6c
52-
constraints+=[cvx.sum(cvx_state_vectors[appliance_id][t])==1]
53-
54-
# Creating Variable matrices for every appliance
55-
appliance_variable_matrix = []
56-
for t in range(len(test_mains)):
57-
matrix = cvx.Variable(
58-
shape=(self.default_num_states, self.default_num_states),
59-
name='variable_matrix-%s-%d'%(appliance_id,t)
60-
)
61-
appliance_variable_matrix.append(matrix)
62-
63-
cvx_variable_matrices.append(appliance_variable_matrix)
64-
# Enforcing that their values are ranged
65-
for t in range(len(test_mains)):
66-
constraints+=[cvx_variable_matrices[appliance_id][t]>=0]
67-
constraints+=[cvx_variable_matrices[appliance_id][t]<=1]
68-
69-
# Constraint 6e
70-
for t in range(0,len(test_mains)): # 6e
71-
for i in range(self.default_num_states):
72-
constraints += [
73-
cvx.sum(((cvx_variable_matrices[appliance_id][t]).T)[i]) == cvx_state_vectors[appliance_id][t][i]
74-
]
75-
76-
# Constraint 6d
77-
for t in range(1,len(test_mains)): # 6d
78-
for i in range(self.default_num_states):
79-
constraints += [
80-
cvx.sum(cvx_variable_matrices[appliance_id][t][i]) == cvx_state_vectors[appliance_id][t-1][i]
81-
]
82-
83-
for appliance_id in range(self.num_appliances):
84-
appliance_usage = cvx_state_vectors[appliance_id]@means_vector[appliance_id]
85-
total_appliance_usage = cvx.sum(appliance_usage)
86-
constraints += [
87-
total_appliance_usage <= self.signal_aggregates[self.appliances[appliance_id]]
88-
]
89-
90-
# Second order cone constraints
91-
total_observed_reading = np.zeros((test_mains.shape))
92-
for appliance_id in range(self.num_appliances):
93-
total_observed_reading += cvx_state_vectors[appliance_id]@means_vector[appliance_id]
94-
flag=1
95-
term_1 = 0
96-
term_2 = 0
97-
98-
for appliance_id in range(self.num_appliances):
99-
# First loop is over appliances
100-
variable_matrix = cvx_variable_matrices[appliance_id]
101-
transmat = transmat_vector[appliance_id]
102-
# Next loop is over different time-stamps
103-
for matrix in variable_matrix:
104-
term_1-=cvx.sum(cvx.multiply(matrix,np.log(transmat)))
105-
one_hot_states = cvx_state_vectors[appliance_id]
106-
pi = pi_s_vector[appliance_id]
107-
# The expression involving start states
108-
first_one_hot_states = one_hot_states[0]
109-
term_2-= cvx.sum(cvx.multiply(first_one_hot_states,np.log(pi)))
110-
111-
flag = 1
112-
113-
expression = 0
114-
term_3 = 0
115-
term_4 = 0
116-
for t in range(len(test_mains)):
117-
term_4+= .5 * ((test_mains[t][0] - total_observed_reading[t][0])**2 / (sigma[t]**2))
118-
term_3+= .5 * (np.log(sigma[t]**2))
119-
120-
expression = term_1 + term_2 + term_3 + term_4
121-
expression = cvx.Minimize(expression)
122-
prob = cvx.Problem(expression, constraints)
123-
prob.solve(solver=cvx.SCS,verbose=False, warm_start=True)
124-
s_ = [
125-
np.zeros((len(test_mains), self.default_num_states)) if i.value is None
126-
else i.value
127-
for i in cvx_state_vectors
128-
]
129-
130-
prediction_dict = {}
131-
for appliance_id in range(self.num_appliances):
132-
app_name = self.appliances[appliance_id]
133-
app_usage= np.sum(s_[appliance_id]@means_vector[appliance_id],axis=1)
134-
prediction_dict[app_name] = app_usage.flatten()
135-
136-
d[index] = pd.DataFrame(prediction_dict,dtype='float32')
23+
return cvx_state_vectors, constraints, cvx_variable_matrices
13724

0 commit comments

Comments
 (0)