Updated server code with sparse GP implementation

This commit is contained in:
Radu C. Martin 2021-06-02 10:44:18 +02:00
parent 2cea33b73c
commit 3199f50051
5 changed files with 488 additions and 170 deletions

View file

@ -2,18 +2,30 @@ import casadi as cs
import numpy as np import numpy as np
import tensorflow as tf import tensorflow as tf
from helpers import get_combined_evaluator
# Package the resulting regression model in a CasADi callback # Package the resulting regression model in a CasADi callback
class GPR(cs.Callback): class GPR(cs.Callback):
def __init__(self, name, model, opts={}): def __init__(self, name, model, n_in, opts={}):
cs.Callback.__init__(self) cs.Callback.__init__(self)
self.model = model self.model = model
self.n_in = model.data[0].shape[1] self.combined_evaluator = get_combined_evaluator(model)
self.n_in = n_in
self.tf_var = tf.Variable(np.ones((1, self.n_in)), dtype = tf.float64)
self.grads = None
# Create a variable to keep all the gradient callback references # Create a variable to keep all the gradient callback references
self.refs = [] self.refs = []
self.construct(name, opts) self.construct(name, opts)
# Update tf_evaluator
def update_model(self, model):
self.model = model
self.combined_evaluator = get_combined_evaluator(model)
def uses_output(self): return True
# Number of inputs/outputs # Number of inputs/outputs
def get_n_in(self): return 1 def get_n_in(self): return 1
def get_n_out(self): return 1 def get_n_out(self): return 1
@ -27,14 +39,15 @@ class GPR(cs.Callback):
def eval(self, arg): def eval(self, arg):
inp = np.array(arg[0]) self.tf_var.assign(arg[0])
inp = tf.Variable(inp, dtype=tf.float64) preds, grads = self.combined_evaluator(self.tf_var)
[mean, _] = self.model.predict_f(inp) [mean, _] = preds
self.grads = grads
return [mean.numpy()] return [mean.numpy()]
def has_reverse(self, nadj): return nadj==1 def has_reverse(self, nadj): return nadj==1
def get_reverse(self, nadj, name, inames, onames, opts): def get_reverse(self, nadj, name, inames, onames, opts):
grad_callback = GPR_grad(name, self.model) grad_callback = GPR_grad(name, self.n_in, self.combined_evaluator)
self.refs.append(grad_callback) self.refs.append(grad_callback)
nominal_in = self.mx_in() nominal_in = self.mx_in()
@ -43,10 +56,12 @@ class GPR(cs.Callback):
return cs.Function(name, nominal_in+nominal_out+adj_seed, grad_callback.call(nominal_in), inames, onames) return cs.Function(name, nominal_in+nominal_out+adj_seed, grad_callback.call(nominal_in), inames, onames)
class GPR_grad(cs.Callback): class GPR_grad(cs.Callback):
def __init__(self, name, model, opts={}): def __init__(self, name, n_in, combined_evaluator, opts={}):
cs.Callback.__init__(self) cs.Callback.__init__(self)
self.model = model
self.n_in = model.data[0].shape[1] self.combined_evaluator = combined_evaluator
self.n_in = n_in
self.tf_var = tf.Variable(np.ones((1, self.n_in)), dtype = tf.float64)
self.construct(name, opts) self.construct(name, opts)
@ -61,11 +76,7 @@ class GPR_grad(cs.Callback):
def eval(self, arg): def eval(self, arg):
inp = np.array(arg[0]) self.tf_var.assign(arg[0])
inp = tf.Variable(inp, dtype=tf.float64) _, grads = self.combined_evaluator(self.tf_var)
with tf.GradientTape() as tape:
preds = self.model.predict_f(inp)
grads = tape.gradient(preds, inp)
return [grads.numpy()] return [grads.numpy()]

View file

@ -8,6 +8,10 @@ import pandas as pd
import gpflow import gpflow
import tensorflow as tf import tensorflow as tf
from gpflow.ci_utils import ci_niter
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler from sklearn.preprocessing import MinMaxScaler
import callbacks import callbacks
@ -43,44 +47,14 @@ class PIDcontroller:
return sig_P + sig_I + sig_D return sig_P + sig_I + sig_D
class Base_MPCcontroller(object):
class GP_MPCcontroller:
def __init__(self, dict_cols, model = None, scaler = None, N_horizon = 10, recover_from_crash = False): def __init__(self, dict_cols, model = None, scaler = None, N_horizon = 10, recover_from_crash = False):
self.recover_from_crash = recover_from_crash
if self.recover_from_crash:
self.model = pickle.load(open("controller_model.pkl", 'rb'))
self.scaler = pickle.load(open("controller_scaler.pkl", 'rb'))
self.X_log = pickle.load(open("controller_X_log.pkl", 'rb'))
df = pd.read_pickle("controller_df.pkl")
self.recovery_signal = iter(df['SimulatedHeat'])
if model is not None:
# Model is already trained. Using as is.
if scaler is None: raise ValueError("Not allowed to pass a model without a scaler")
self.model = model
self.cs_model = callbacks.GPR("gpr", self.model)
self.scaler = scaler
self.scaler_helper = ScalerHelper(self.scaler)
else:
# No model has been passed. Setting up model initialization
self.model = None
self.nb_data = 500 + 1
self.ident_signal = get_random_signal(self.nb_data, signal_type = 'analog')
self.Pel = 2 * 6300
self.COP = 5.0
self.ident_signal = iter(self.Pel * self.COP * self.ident_signal)
self.dict_cols = dict_cols self.dict_cols = dict_cols
self.max_lag = max([lag for lag,_ in self.dict_cols.values()]) self.max_lag = max([lag for lag,_ in self.dict_cols.values()])
self.N_horizon = N_horizon self.N_horizon = N_horizon
self.n_states = np.sum([len(cols) * lags for lags,cols in
self.dict_cols.values()])
self.X_log = [] self.X_log = []
@ -91,119 +65,50 @@ class GP_MPCcontroller:
self.data_cols += cols self.data_cols += cols
self.data = np.empty((0, len(self.data_cols))) self.data = np.empty((0, len(self.data_cols)))
# Dataset used for training
self.dataset_train_minsize = 500
self.dataset_train_maxsize = 500
self.dataset_train = np.empty((0, self.n_states))
# The current weather forcast # The current weather forcast
self.weather_forecast = None self.weather_forecast = None
# Current measurements # Current measurements
self.w, self.u, self.y = None, None, None self.w, self.u, self.y = None, None, None
# Recover from a previous crash with precomputed values and continue
self.recover_from_crash = recover_from_crash
if self.recover_from_crash:
self.model = pickle.load(open("controller_model.pkl", 'rb'))
self.scaler = pickle.load(open("controller_scaler.pkl", 'rb'))
self.scaler_helper = ScalerHelper(self.scaler)
self.X_log = pickle.load(open("controller_X_log.pkl", 'rb'))
df = pd.read_pickle("controller_df.pkl")
self.recovery_signal = iter(df['SimulatedHeat'])
return
### # Pre-existing model passed. Load all the necessary objects
# GPflow model training and update if model is not None:
### # Model is already trained. Using as is.
def _train_model(self): if scaler is None: raise ValueError("Not allowed to pass a model without a scaler")
""" Identify model from gathered data """ self.model = model
self.cs_model = callbacks.GPR("gpr", self.model, self.n_states)
self.scaler = scaler
self.scaler_helper = ScalerHelper(self.scaler)
# No pre-existing model. Set up data acquisition and model training
else:
# No model has been passed. Setting up model initialization
self.model = None
nb_train_pts = self.nb_data - 1 # Define an identification signal to be used first
### self.Pel = 2 * 6300
# Dataset self.COP = 5.0
###
df = pd.DataFrame(self.data[:nb_train_pts], columns = self.data_cols)
self.scaler = MinMaxScaler(feature_range = (-1, 1))
self.scaler_helper = ScalerHelper(self.scaler)
df_sc = get_scaled_df(df, self.dict_cols, self.scaler)
df_gpr_train = data_to_gpr(df_sc, self.dict_cols)
df_input_train = df_gpr_train.drop(columns = self.dict_cols['w'][1] + self.dict_cols['u'][1] + self.dict_cols['y'][1]) self.ident_signal = get_identification_signal(size = self.dataset_train_minsize)
df_output_train = df_gpr_train[self.dict_cols['y'][1]] self.ident_signal = iter(self.COP * self.Pel * self.ident_signal)
np_input_train = df_input_train.to_numpy() return
np_output_train = df_output_train.to_numpy().reshape(-1, 1)
data_train = (np_input_train, np_output_train)
df_test = pd.DataFrame(self.data[nb_train_pts:], columns = self.data_cols)
df_test_sc = get_scaled_df(df_test, self.dict_cols, self.scaler)
df_gpr_test = data_to_gpr(df_test_sc, self.dict_cols)
df_input_test = df_gpr_test.drop(columns = self.dict_cols['w'][1] + self.dict_cols['u'][1] + self.dict_cols['y'][1])
df_output_test = df_gpr_test[self.dict_cols['y'][1]]
np_input_test = df_input_test.to_numpy()
np_output_test = df_output_test.to_numpy()
###
# Kernel
###
nb_dims = np_input_train.shape[1]
rational_dims = np.arange(0, (self.dict_cols['t'][0] + 1) * len(self.dict_cols['t'][1]), 1)
nb_rational_dims = len(rational_dims)
squared_dims = np.arange(nb_rational_dims, nb_dims, 1)
nb_squared_dims = len(squared_dims)
default_lscale = 75
while True:
try:
squared_l = np.linspace(default_lscale, 2*default_lscale, nb_squared_dims)
rational_l = np.linspace(default_lscale, 2*default_lscale, nb_rational_dims)
variance = tf.math.reduce_variance(np_input_train)
k0 = gpflow.kernels.SquaredExponential(lengthscales = squared_l, active_dims = squared_dims, variance = variance)
k1 = gpflow.kernels.Constant(variance = variance)
k2 = gpflow.kernels.RationalQuadratic(lengthscales = rational_l, active_dims = rational_dims, variance = variance)
k3 = gpflow.kernels.Periodic(k2)
k = (k0 + k1) * k2
k = k0
###
# Model
###
m = gpflow.models.GPR(
data = data_train,
kernel = k,
mean_function = None,
)
###
# Training
###
print(f"Training a model with lscale:{default_lscale}")
opt = gpflow.optimizers.Scipy()
opt.minimize(m.training_loss, m.trainable_variables)
break
except:
print(f"Failed.Increasing lengthscale")
default_lscale += 5
###
# Save model
###
self.model = m
self.n_states = self.model.data[0].shape[1]
# # Manual model validation
# import matplotlib.pyplot as plt
#
# plt.figure()
# plt.plot(data_train[1], label = 'real')
# mean, var = self.model.predict_f(data_train[0])
# plt.plot(mean, label = 'model')
# plt.legend()
# plt.show()
#
# plt.figure()
# plt.plot(np_output_test, label = 'real')
# mean, var = self.model.predict_f(np_input_test)
# plt.plot(mean, label = 'model')
# plt.legend()
# plt.show()
#
# import pdb; pdb.set_trace()
pass
### ###
# Update measurements # Update measurements
@ -218,18 +123,16 @@ class GP_MPCcontroller:
def _add_input_measurement(self, u): def _add_input_measurement(self, u):
self.u = np.array(u).reshape(1, -1) self.u = np.array(u).reshape(1, -1)
def _add_measurement_set(self):
new_data = np.hstack([self.w, self.u, self.y])
self.data = np.vstack([self.data, new_data])
print(f"{self.data.shape[0]} data points. Newest: {new_data}")
self.w, self.u, self.y = None, None, None
def set_weather_forecast(self, W): def set_weather_forecast(self, W):
assert (W.shape[0] == self.N_horizon) assert (W.shape[0] == self.N_horizon)
self.weather_forecast = W self.weather_forecast = W
def update_model(self):
new_data = np.hstack([self.w, self.u, self.y])
print(f"Adding new data: {new_data}")
self.data = np.vstack([self.data, new_data])
print(f"Data size: {self.data.shape[0]}")
self.w, self.u, self.y = None, None, None
print("---")
### ###
# Set up optimal problem solver # Set up optimal problem solver
### ###
@ -239,10 +142,11 @@ class GP_MPCcontroller:
### ###
# Initialization # Initialization
### ###
self.cs_model = callbacks.GPR("gpr", self.model) self.cs_model = callbacks.GPR("gpr", self.model, self.n_states)
T_set = 21 T_set = 21
T_set_sc = self.scaler_helper.scale_output(T_set) T_set_sc = self.scaler_helper.scale_output(T_set)
self.T_set_sc = T_set_sc
X = cs.MX.sym("X", self.N_horizon + 1, self.n_states) X = cs.MX.sym("X", self.N_horizon + 1, self.n_states)
@ -311,7 +215,7 @@ class GP_MPCcontroller:
prob = {'f': J, 'x': cs.vec(X), 'g': cs.vertcat(*g), 'p': p} prob = {'f': J, 'x': cs.vec(X), 'g': cs.vertcat(*g), 'p': p}
options = {"ipopt": {"hessian_approximation": "limited-memory", "max_iter": 100, options = {"ipopt": {"hessian_approximation": "limited-memory", "max_iter": 100,
#"acceptable_tol": 1e-6, "tol": 1e-6, "acceptable_tol": 1e-4, "tol": 1e-4,
#"linear_solver": "ma57", #"linear_solver": "ma57",
#"acceptable_obj_change_tol": 1e-5, #"acceptable_obj_change_tol": 1e-5,
#"mu_strategy": "adaptive", #"mu_strategy": "adaptive",
@ -341,6 +245,7 @@ class GP_MPCcontroller:
except StopIteration: except StopIteration:
# Finished passing in the pre-existing control # Finished passing in the pre-existing control
# Switching to normal operation # Switching to normal operation
self._setup_solver()
self.recover_from_crash = False self.recover_from_crash = False
# Training pre-loop # Training pre-loop
@ -360,6 +265,10 @@ class GP_MPCcontroller:
data_scaled = self.scaler.transform(self.data) data_scaled = self.scaler.transform(self.data)
# Append a dummy row to data, to align data_to_gpr cols (which include measureemnt
# at time t, with the current measurements, that stop at t-1)
dummy_row = np.nan * np.ones((1, self.data.shape[1]))
data_scaled = np.vstack([data_scaled, dummy_row])
df = pd.DataFrame(data_scaled, columns = self.data_cols) df = pd.DataFrame(data_scaled, columns = self.data_cols)
x0 = data_to_gpr(df, self.dict_cols).drop( x0 = data_to_gpr(df, self.dict_cols).drop(
@ -384,6 +293,8 @@ class GP_MPCcontroller:
X = np.array(res['x'].reshape((self.N_horizon + 1, self.n_states))) X = np.array(res['x'].reshape((self.N_horizon + 1, self.n_states)))
self.X_log.append(X) self.X_log.append(X)
df_X = pd.DataFrame(X)
#import pdb; pdb.set_trace()
u_idx = self.dict_cols['w'][0] * len(self.dict_cols['w'][1]) u_idx = self.dict_cols['w'][0] * len(self.dict_cols['w'][1])
# Take the first control action and apply it # Take the first control action and apply it
u = X[1, u_idx] u = X[1, u_idx]
@ -403,6 +314,363 @@ class GP_MPCcontroller:
return return
class GP_MPCcontroller(Base_MPCcontroller):
def __init__(self, dict_cols, model = None, scaler = None, N_horizon = 10, recover_from_crash = False):
super().__init__(dict_cols, model, scaler, N_horizon, recover_from_crash)
###
# GPflow model training and update
###
def _train_model(self):
""" Identify model from gathered data """
nb_train_pts = self.dataset_train_minsize - 1
nb_train_pts = 500
###
# Dataset
###
df = pd.DataFrame(self.data[:nb_train_pts], columns = self.data_cols)
self.scaler = MinMaxScaler(feature_range = (-1, 1))
self.scaler_helper = ScalerHelper(self.scaler)
df_sc = get_scaled_df(df, self.dict_cols, self.scaler)
df_gpr_train = data_to_gpr(df_sc, self.dict_cols)
df_input_train = df_gpr_train.drop(columns = self.dict_cols['w'][1] + self.dict_cols['u'][1] + self.dict_cols['y'][1])
df_output_train = df_gpr_train[self.dict_cols['y'][1]]
np_input_train = df_input_train.to_numpy()
np_output_train = df_output_train.to_numpy().reshape(-1, 1)
data_train = (np_input_train, np_output_train)
df_test = pd.DataFrame(self.data[nb_train_pts:], columns = self.data_cols)
df_test_sc = get_scaled_df(df_test, self.dict_cols, self.scaler)
df_gpr_test = data_to_gpr(df_test_sc, self.dict_cols)
df_input_test = df_gpr_test.drop(columns = self.dict_cols['w'][1] + self.dict_cols['u'][1] + self.dict_cols['y'][1])
df_output_test = df_gpr_test[self.dict_cols['y'][1]]
np_input_test = df_input_test.to_numpy()
np_output_test = df_output_test.to_numpy()
###
# Kernel
###
nb_dims = np_input_train.shape[1]
rational_dims = np.arange(0, (self.dict_cols['t'][0] + 1) * len(self.dict_cols['t'][1]), 1)
nb_rational_dims = len(rational_dims)
squared_dims = np.arange(nb_rational_dims, nb_dims, 1)
nb_squared_dims = len(squared_dims)
default_lscale = 150
while True:
try:
squared_l = np.linspace(default_lscale, 2*default_lscale, nb_squared_dims)
rational_l = np.linspace(default_lscale, 2*default_lscale, nb_rational_dims)
variance = tf.math.reduce_variance(np_input_train)
k0 = gpflow.kernels.SquaredExponential(lengthscales = squared_l, active_dims = squared_dims, variance = variance)
k1 = gpflow.kernels.Constant(variance = variance)
k2 = gpflow.kernels.RationalQuadratic(lengthscales = rational_l, active_dims = rational_dims, variance = variance)
k3 = gpflow.kernels.Periodic(k2)
k = (k0 + k1) * k2
k = k0
###
# Model
###
m = gpflow.models.GPR(
data = data_train,
kernel = k,
mean_function = None,
)
###
# Training
###
print(f"Training a model with lscale:{default_lscale}")
opt = gpflow.optimizers.Scipy()
opt.minimize(m.training_loss, m.trainable_variables)
break
except:
print(f"Failed.Increasing lengthscale")
default_lscale += 10
###
# Save model
###
self.model = m
# plt.figure()
#
# # Testing on training data
# mean, var = m.predict_f(np_input_train)
#
# plt.plot(df_input_train.index, np_output_train[:, :], label = 'Measured data')
# plt.plot(df_input_train.index, mean[:, :], label = 'Gaussian Process Prediction')
# plt.fill_between(
# df_input_train.index,
# mean[:, 0] - 1.96 * np.sqrt(var[:, 0]),
# mean[:, 0] + 1.96 * np.sqrt(var[:, 0]),
# alpha = 0.2
# )
# plt.show()
#
# plt.figure()
# # Testing on testing data
# mean, var = m.predict_f(np_input_test)
#
# plt.plot(df_input_test.index, np_output_test[:, :], label = 'Measured data')
# plt.plot(df_input_test.index, mean[:, :], label = 'Gaussian Process Prediction')
# plt.fill_between(
# df_input_test.index,
# mean[:, 0] - 1.96 * np.sqrt(var[:, 0]),
# mean[:, 0] + 1.96 * np.sqrt(var[:, 0]),
# alpha = 0.2
# )
# plt.show()
#
#
# import pdb; pdb.set_trace()
# plt.figure()
#
# start_idx = 25
# nb_predictions = 25
# N_pred = 20
#
# plt.figure()
#
# y_name = self.dict_cols['y'][1][0]
# for idx in range(start_idx, start_idx + nb_predictions):
# df_iter = df_input_test.iloc[idx:(idx + N_pred)].copy()
# for idxx in range(N_pred - 1):
# idx_old = df_iter.index[idxx]
# idx_new = df_iter.index[idxx+1]
# mean, var = m.predict_f(df_iter.loc[idx_old, :].to_numpy().reshape(1, -1))
# df_iter.loc[idx_new, f'{y_name}_1'] = mean.numpy().flatten()
# for lag in range(2, self.dict_cols['y'][0] + 1):
# df_iter.loc[idx_new, f"{y_name}_{lag}"] = df_iter.loc[idx_old, f"{y_name}_{lag-1}"]
#
# mean_iter, var_iter = m.predict_f(df_iter.to_numpy())
# plt.plot(df_iter.index, mean_iter.numpy(), '.-', label = 'predicted', color = 'orange')
# plt.plot(df_output_test.iloc[start_idx:start_idx + nb_predictions + N_pred], 'o-', label = 'measured', color = 'darkblue')
# plt.title(f"Prediction over {N_pred} steps")
#
# plt.show()
return
def update_model(self):
self._add_measurement_set()
class SVGP_MPCcontroller(Base_MPCcontroller):
def __init__(self, dict_cols, model = None, scaler = None, N_horizon = 10, recover_from_crash = False):
super().__init__(dict_cols, model, scaler, N_horizon, recover_from_crash)
# Adaptive models update parameters
self.model_update_frequency = 100
self.pts_since_update = 0
# Model log
self.model_log = []
###
# GPflow model training and update
###
def _train_model(self):
""" Identify model from gathered data """
#nb_train_pts = self.dataset_train_minsize - 1
nb_train_pts = self.data.shape[0]
###
# Dataset
###
df = pd.DataFrame(self.data[:nb_train_pts], columns = self.data_cols)
self.scaler = MinMaxScaler(feature_range = (-1, 1))
self.scaler_helper = ScalerHelper(self.scaler)
df_sc = get_scaled_df(df, self.dict_cols, self.scaler)
df_gpr_train = data_to_gpr(df_sc, self.dict_cols)
df_input_train = df_gpr_train.drop(columns = self.dict_cols['w'][1] + self.dict_cols['u'][1] + self.dict_cols['y'][1])
df_output_train = df_gpr_train[self.dict_cols['y'][1]]
np_input_train = df_input_train.to_numpy()
np_output_train = df_output_train.to_numpy().reshape(-1, 1)
data_train = (np_input_train, np_output_train)
###
# Kernel
###
nb_dims = np_input_train.shape[1]
rational_dims = np.arange(0, (self.dict_cols['t'][0] + 1) * len(self.dict_cols['t'][1]), 1)
nb_rational_dims = len(rational_dims)
squared_dims = np.arange(nb_rational_dims, nb_dims, 1)
nb_squared_dims = len(squared_dims)
default_lscale = 1
squared_l = np.linspace(default_lscale, 2*default_lscale, nb_squared_dims)
rational_l = np.linspace(default_lscale, 2*default_lscale, nb_rational_dims)
variance = tf.math.reduce_variance(np_input_train)
k0 = gpflow.kernels.SquaredExponential(lengthscales = squared_l, active_dims = squared_dims, variance = variance)
k1 = gpflow.kernels.Constant(variance = variance)
k2 = gpflow.kernels.RationalQuadratic(lengthscales = rational_l, active_dims = rational_dims, variance = variance)
k3 = gpflow.kernels.Periodic(k2)
k = (k0 + k1) * k2
k = k0
###
# Model
###
N = data_train[0].shape[0]
M = 150 # Number of inducing locations
Z = data_train[0][:M, :].copy()
m = gpflow.models.SVGP(k, gpflow.likelihoods.Gaussian(), Z, num_data = N)
elbo = tf.function(m.elbo)
###
# Training
###
minibatch_size = 100
train_dataset = tf.data.Dataset.from_tensor_slices(data_train).repeat().shuffle(N)
# Turn off training for inducing point locations
gpflow.set_trainable(m.inducing_variable, False)
def run_adam(model, iterations):
"""
Utility function running the Adam optimizer
:param model: GPflow model
:param interations: number of iterations
"""
# Create an Adam Optimizer action
logf = []
train_iter = iter(train_dataset.batch(minibatch_size))
training_loss = model.training_loss_closure(train_iter, compile=True)
optimizer = tf.optimizers.Adam()
@tf.function
def optimization_step():
optimizer.minimize(training_loss, model.trainable_variables)
for step in range(iterations):
optimization_step()
if step % 10 == 0:
elbo = -training_loss().numpy()
logf.append(elbo)
return logf
maxiter = ci_niter(10000)
logf = run_adam(m, maxiter)
###
# Save model
###
self.model = m
# plt.figure()
#
# # Testing on training data
# mean, var = m.predict_f(np_input_train)
#
# plt.plot(df_input_train.index, np_output_train[:, :], label = 'Measured data')
# plt.plot(df_input_train.index, mean[:, :], label = 'Gaussian Process Prediction')
# plt.fill_between(
# df_input_train.index,
# mean[:, 0] - 1.96 * np.sqrt(var[:, 0]),
# mean[:, 0] + 1.96 * np.sqrt(var[:, 0]),
# alpha = 0.2
# )
# plt.show()
#
# plt.figure()
# # Testing on testing data
# mean, var = m.predict_f(np_input_test)
#
# plt.plot(df_input_test.index, np_output_test[:, :], label = 'Measured data')
# plt.plot(df_input_test.index, mean[:, :], label = 'Gaussian Process Prediction')
# plt.fill_between(
# df_input_test.index,
# mean[:, 0] - 1.96 * np.sqrt(var[:, 0]),
# mean[:, 0] + 1.96 * np.sqrt(var[:, 0]),
# alpha = 0.2
# )
# plt.show()
#
#
# plt.figure()
#
# start_idx = 25
# nb_predictions = 25
# N_pred = 20
#
# plt.figure()
#
# y_name = self.dict_cols['y'][1][0]
# for idx in range(start_idx, start_idx + nb_predictions):
# df_iter = df_input_test.iloc[idx:(idx + N_pred)].copy()
# for idxx in range(N_pred - 1):
# idx_old = df_iter.index[idxx]
# idx_new = df_iter.index[idxx+1]
# mean, var = m.predict_f(df_iter.loc[idx_old, :].to_numpy().reshape(1, -1))
# df_iter.loc[idx_new, f'{y_name}_1'] = mean.numpy().flatten()
# for lag in range(2, self.dict_cols['y'][0] + 1):
# df_iter.loc[idx_new, f"{y_name}_{lag}"] = df_iter.loc[idx_old, f"{y_name}_{lag-1}"]
#
# mean_iter, var_iter = m.predict_f(df_iter.to_numpy())
# plt.plot(df_iter.index, mean_iter.numpy(), '.-', label = 'predicted', color = 'orange')
# plt.plot(df_output_test.iloc[start_idx:start_idx + nb_predictions + N_pred], 'o-', label = 'measured', color = 'darkblue')
# plt.title(f"Prediction over {N_pred} steps")
#
# plt.show()
return
def update_model(self):
self._add_measurement_set()
if self.model is not None and not self.recover_from_crash:
print(f"Points since last update: {self.pts_since_update}")
if self.pts_since_update < self.model_update_frequency:
self.pts_since_update += 1
else:
print(f"Updating model after {self.pts_since_update} measurements")
# Append old model to log
self.model_log.append(self.model)
# Train new model
self._train_model()
# Re-initialize CasADi solver
self._setup_solver()
self.pts_since_update = 0
# Redefine save_data since now we're also saving the model log
def save_data(self):
df = pd.DataFrame(self.data, columns = self.data_cols)
df.to_pickle("controller_df.pkl")
pickle.dump(self.scaler, open(Path("controller_scaler.pkl"), 'wb'))
pickle.dump(self.model, open(Path("controller_model.pkl"), 'wb'))
pickle.dump(self.X_log, open(Path("controller_X_log.pkl"), 'wb'))
pickle.dump(self.model_log, open(Path("controller_model_log.pkl"), 'wb'))
return
class TestController: class TestController:
def __init__(self): def __init__(self):
return return

View file

@ -1,9 +1,36 @@
import pandas as pd import pandas as pd
import numpy as np import numpy as np
import gpflow import gpflow
import tensorflow as tf
from sklearn.exceptions import NotFittedError from sklearn.exceptions import NotFittedError
# Generator for tensorflow functions for a given model
def get_model_evaluator(model):
@tf.function
def model_evaluator(tf_input):
preds = model.predict_f(tf_input)
return preds
return model_evaluator
def get_grad_evaluator(model):
@tf.function
def grad_evaluator(tf_input):
with tf.GradientTape() as tape:
preds = model.predict_f(tf_input)
grads = tape.gradient(preds, tf_input)
return grads
return grad_evaluator
def get_combined_evaluator(model):
@tf.function
def combined_evaluator(tf_input):
with tf.GradientTape() as tape:
preds = model.predict_f(tf_input)
grads = tape.gradient(preds, tf_input)
return preds, grads
return combined_evaluator
def get_random_signal(nstep, a_range = (-1, 1), b_range = (2, 10), signal_type = 'analog'): def get_random_signal(nstep, a_range = (-1, 1), b_range = (2, 10), signal_type = 'analog'):
@ -47,6 +74,20 @@ def get_random_signal(nstep, a_range = (-1, 1), b_range = (2, 10), signal_type =
raise ValueError(signal_type) raise ValueError(signal_type)
def get_identification_signal(size):
# Base random signal
rand_signal = get_random_signal(size, signal_type = 'prbs')
# Integrator (cumulative sum)
cum_signal = 3/size * np.ones((1, size))
cum_signal = np.cumsum(cum_signal)
# Combine signals and clip signal to [-1, 1] range
ident_signal = rand_signal + cum_signal
ident_signal = np.where(ident_signal < -1, -1, ident_signal)
ident_signal = np.where(ident_signal > 1, 1, ident_signal)
return ident_signal
def get_scaled_df(df, dict_cols, scaler): def get_scaled_df(df, dict_cols, scaler):
""" """

View file

@ -6,11 +6,12 @@ import pickle
import numpy as np import numpy as np
from controllers import GP_MPCcontroller, TestController from controllers import *
clients = ['measure', 'control', 'weather'] clients = ['measure', 'control', 'weather']
N_horizon = 6 N_horizon = 8
print(f"[*] Controller Horizon {N_horizon}")
HOST = '127.0.0.1' HOST = '127.0.0.1'
PORT = { PORT = {
@ -55,7 +56,7 @@ dict_cols = {
'y': (y_lags, y_cols) 'y': (y_lags, y_cols)
} }
controller = GP_MPCcontroller(dict_cols = dict_cols, N_horizon = N_horizon, recover_from_crash = False) controller = SVGP_MPCcontroller(dict_cols = dict_cols, N_horizon = N_horizon, recover_from_crash = False)
# Enter TCP server loop # Enter TCP server loop
while True: while True:

View file

@ -17,7 +17,7 @@ import casadi as cs
import callbacks import callbacks
from helpers import * from helpers import *
from controllers import GP_MPCcontroller from controllers import *
from time import sleep from time import sleep
@ -40,8 +40,7 @@ dict_cols = {
N_horizon = 10 N_horizon = 10
controller = SVGP_MPCcontroller(dict_cols = dict_cols, N_horizon = N_horizon)
controller = GP_MPCcontroller(dict_cols = dict_cols, N_horizon = N_horizon)
@ -64,6 +63,4 @@ while True:
idx += 1 idx += 1
if idx > 502:
import pdb; pdb.set_trace()
pass pass