Skip to content

Commit c0e8ad2

Browse files
committed
refactor: split AFHMM+SAC disaggregate_thread function into the constraints setup and the actual minimization
1 parent a2c03a6 commit c0e8ad2

File tree

2 files changed

+176
-209
lines changed

2 files changed

+176
-209
lines changed

nilmtk_contrib/disaggregate/afhmm.py

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

6565
print ("{}: Finished training".format(self.MODEL_NAME))
6666

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

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

167155
prediction_dict = {}
168-
for appliance_id in range(self.num_appliances):
169-
app_name = self.appliances[appliance_id]
170-
app_usage= np.sum(s_[appliance_id]@means_vector[appliance_id],axis=1)
171-
prediction_dict[app_name] = app_usage.flatten()
156+
for i, (appli_name, means) in enumerate(self.means_vector.items()):
157+
app_usage = np.sum(s_[i]@means, axis=1)
158+
prediction_dict[appli_name] = app_usage.flatten()
172159

173-
# Store the result in the index corresponding to the thread.
174-
d[index] = pd.DataFrame(prediction_dict,dtype='float32')
160+
return pd.DataFrame(prediction_dict, dtype="float32")
175161

176162
def disaggregate_chunk(self, test_mains):
177163
# Distributes the test mains across multiple threads and disaggregate in parallel

0 commit comments

Comments
 (0)