# Physics/dynamic system simulation with Deep Learning

I am interested in modelling physical/dynamic systems with deep learning, and I think that recurrent neural networks should be a good way to model systems that can be normally represented by state-space models. In short, you have system with a state that evolves following some equations, and I would like the model to learn these, in a non-parametric way. Deep learning is known to be a universal function approximator, so it should work, shouldn’t it? :-)

As first experiment I am trying to have a model to learn how a mobile object follows a path, given points of this path (like waypoints). As a synthetic example, I generated a 2D curve, and as first step I want to overfit to a single example. (I know that the curve generated this way is not following dynamic equations, but for the exercise I think it fits without problem. For a next step I would generate them from dynamic equations).

In the repo’s notebook I have the code to generate the synthetic example, a simple stacked RNN network in keras.

About the input, I am giving the initial point of the object, and the points that will be crossed by it. I repeat the input every time-step since I don’t know how to do it other way:

• The initial position: I cannot set an internal state of the RNN to it, because I don’t know how the network will store it’s position. Also, I don’t know how to give something once as initial data to a network and then let it run iteratively. Doing a one-to-many model would work, but eventually I will want to give inputs online to the model, like forces applied externally, and the like, so I have to use a many-to-many approach.
• The waypoints: actually is just as with the initial position. I don’t know a better way to provide them other than repeating them in the input.

The output is obviously the path that the object will follow.

This is an example of the path to be learnt and the network output (reproducible in the notebook):

My findings trying combinations of size (layers and units) of the network, and of the data sample, is that:

• If the path has a few points (for example 10 or 20) and 5 waypoints, the network will learn it consistently.
• With 100 points, I cannot make it learn the single sample, it always finds a local optimum by “going” to the centroid of all the path points.
• In the middle between those, it can converge sometimes, but it’s highly dependant on the random values from initialization. I tried different initializers (he and glorot, uniform and normal), and I have the impression that uniform tends to work better, but I need more experiments.

Has someone have tried to model this kind of system? Am I approaching this in a proper way? And, obviously, does it seems like what it’s implemented is what I describe? (I would say so, or it wouldn’t have converged in the easier cases … ). Why it doesn’t converte for long tracks, independently of network size?

Thank you for any help/idea/suggestion!

The network is defined as:

def get_model(cfg):
model = Sequential()
model.add(LSTM(units=cfg.lstm_units[0],
input_shape=(cfg.n_time_steps, cfg.n_waypoints * 2 + 2),
return_sequences=True,
kernel_initializer=cfg.kernel_initializer))
for n_cells in cfg.lstm_units[1:]:
model.add(LSTM(units=n_cells,
return_sequences=True,
kernel_initializer=cfg.kernel_initializer))
for n_cells in cfg.dense_units:
model.add(TimeDistributed(Dense(n_cells,
activation='relu',
kernel_initializer=cfg.kernel_initializer)))
model.add(TimeDistributed(Dense(2, kernel_initializer=cfg.kernel_initializer)))

model.compile(optimizer=Adam(lr=cfg.start_lr), loss=cfg.loss, metrics=[cfg.loss])

return model


And the example data, a single sample, is generated as follows:

def get_input_output_example(cfg, plot=False):
x, y = get_2D_path(cfg.n_time_steps + 1, [1.0, 1.0], [0.0, 4.0, -0.2, -0.02],
[0.0, -2.0, 0.5, 0.04])

wpts = np.array([(x[wp_t], y[wp_t]) for wp_t
in range(cfg.n_time_steps - 1, 0, -int(cfg.n_time_steps / cfg.n_waypoints))])

new_output = np.array([x[1:], y[1:]])  # first sample is where we start
new_output = new_output.swapaxes(1, 0)
new_output = np.expand_dims(new_output, 0)

new_input = np.hstack([np.array([x[0], y[0]]), wpts.flatten()])
new_input = np.expand_dims(new_input, axis=0)
new_input = np.stack([new_input] * cfg.n_time_steps, axis=1)

max_val = np.max([x, y])
min_val = np.min([x, y])
scale = max_val - min_val
bias = (max_val + min_val) / 2

return new_input, new_output, wpts, scale, bias


Finally, the training:

model = get_model(cfg)
new_input, new_output, wpts, scale, bias = get_input_output_example(cfg, plot=False)
X, y = get_repeated_batch(cfg.repetitions, new_input, new_output, scale, bias)

print('Training ...')
lr_log = []
hist = model.fit(x=X, y=y, batch_size=cfg.repetitions, epochs=250, verbose=0,
callbacks=get_callbacks(lr_log, cfg))


All the code and findings commented are in this repo:
https://github.com/erpacote/physics_simulation