Skip to content

Latest commit

 

History

History
180 lines (138 loc) · 6.43 KB

File metadata and controls

180 lines (138 loc) · 6.43 KB

Continuous-Time Models

Some models of computation treat time as continuous. These kinds of models often define "equations of motion" that describe the dynamics of the system, and often consist of one or more differential equations. These kinds of systems can be described within the Netomaton framework, and they can thus be thought of as Network Automata. Because Network Automata are discrete-time systems, a continuous-time model must first be discretized.

Consider the following automaton with a single node that behaves like a single spring. In this single-node Network Automaton, the node's state consists of two continuous values, which represent position and velocity. The state changes according to the following equations of motion:

dx/dt = v
dv/dt = −k/m x − b/m v

where x is position, v is velocity, k is stiffness, b is damping, m is mass, t is time.

This makes the node's state change as if it had the mechanics of a single spring. Since a Network Automaton exists in discrete time, the state will be updated with difference equations, using the Euler method. For k = 3.0, m = 0.5, b = 0.0, the equations of motion reduce to:

dx/dt = v
dv/dt = −6 x

and the difference equations are:

x[n+1] = x[n] + Δt*v[n]
v[n+1] = v[n] + Δt*(−6x[n])

The following code snippet demonstrates this automaton:

import netomaton as ntm

network = ntm.topology.from_adjacency_matrix([[1]])

dt = 0.025000

def activity_rule(ctx):
    x_n, v_n = ctx.current_activity
    x_new = x_n + (dt * v_n)
    v_new = v_n + (dt * (-6 * x_new))
    return x_new, v_new

initial_conditions = [(-2.00000, 0.00000)]

trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
                        activity_rule=activity_rule, timesteps=1000)

activities = ntm.get_activities_over_time_as_list(trajectory)

# plot the position and velocity as a function of time
positions = [a[0][0] for a in activities]
velocities = [a[0][1] for a in activities]
ntm.plot1D(x=[i for i in range(1000)],
           y=[positions, velocities],
           color=["blue", "orange"],
           xlabel="t", ylabel=["x(t)", "v(t)"],
           ylim=[(-6, 6), (-6, 6)], twinx=True)

The full source code for this example can be found here.

A partial differential equation can also be modelled. The following automaton models the Diffusion (or Heat) Equation: ∂u/∂t = α ∂²u/∂x². In this Network Automaton, each of the 120 nodes represents a body that can contain some amount of heat. The following code snippet demonstrates this automaton:

import numpy as np
import netomaton as ntm

space = np.linspace(25, -25, 120)
initial_conditions = [np.exp(-x ** 2) for x in space]

network = ntm.topology.cellular_automaton(120)

a = 0.25
dt = .5
dx = .5
F = a * dt / dx ** 2

def activity_rule(ctx):
    current = ctx.current_activity
    left = ctx.neighbourhood_activities[0]
    right = ctx.neighbourhood_activities[2]
    return current + F * (right - 2 * current + left)

trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
                        activity_rule=activity_rule, timesteps=75)

ntm.plot_activities(trajectory)

The full source code for this example can be found here.

Note that in the automaton above, a node's neighbourhood influences the state of the node, and the system can be thought of as a 1D Cellular Automaton with continuous state values evolving in continuous time. (The automaton above also closely resembles the plot at the top of Wolfram's NKS, page 163.)

The examples above involve working with the first derivative of a variable with respect to time. However, it is also possible to implement models with higher-order derivatives of a variable with respect to time. The following example demonstrates an implementation of the 1D Wave Equation, ∂²u/∂t² = ∂²u/∂x²:

import netomaton as ntm
import numpy as np

nx = 401  # the number of nodes (i.e. the number of points in the grid)
nt = 255  # the number of timesteps
dx = 0.1  # the distance between any pair of adjacent points
dt = .05  # the amount of time each timestep covers

space = np.linspace(20, -20, nx)
initial_conditions = [np.exp(-x ** 2) for x in space]

network = ntm.topology.cellular_automaton(nx)

def activity_rule(ctx):
    un_i = ctx.current_activity
    left_label = (ctx.node_label - 1) % nx
    un_i_m1 = ctx.activity_of(left_label)
    right_label = (ctx.node_label + 1) % nx
    un_i_p1 = ctx.activity_of(right_label)
    # the activity not at the previous timestep, but the timestep before that
    un_m1_i = ctx.past_activity_of(ctx.node_label)
    return ((dt ** 2 * (un_i_p1 - 2 * un_i + un_i_m1)) / dx ** 2) + (2 * un_i - un_m1_i)

trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
                        activity_rule=activity_rule, timesteps=nt, 
                        past_conditions=[initial_conditions])

ntm.plot_activities(trajectory)

The full source code for this example can be found here.

Note the use of the past_conditions parameter of the evolve function. Setting the past conditions makes part of the history of the evolution available in the NodeContext. (The automaton above also closely resembles the plot in the middle of Wolfram's NKS, page 163.)

The Gray-Scott Reaction-Diffusion model and the Hopfield-Tank Neural Network are also examples of continuous-time models that have been implemented in this project as Network Automata.

Finally, a good resource for learning more about working with partial differential equations computationally is CFD Python. Several examples from that resource are implemented with Netomaton here:

See the following for more information:

https://www.myphysicslab.com/explain/numerical-solution-en.html

http://hplgit.github.io/num-methods-for-PDEs/doc/pub/diffu/sphinx/._main_diffu001.html

https://www.wolframscience.com/nks/p163--partial-differential-equations/