Manual

Define a Clock

To record changes of recorded variables automatically, those variables must refer to the time variable of model, in this package which called as a "Clock".

There are different types of clocks:

A clock can be created like:

c = ContinuousClock(10.0)

which create a ContinuousClock start at t=0 and end at t=10.

The current time of clock is accessed by currenttime. Obviously, the current time of clock will change at each epoch. For DiscreteClock, the time will update automatically during iteration, more about iteration, see below. For ContinuousClock, the time must be update manually by increase!.

Clocks can be iterated, for example, iterating a ContinuousClock:

for epoch in c
    # do something calculating the time step τ
    increase!(c, τ)
    # do something mutating states
end

which is equivalent to

t = 0.0
epoch = 0
while t <= 10.0
    global epoch
    epoch += 1
    # do something calculating the time step τ
    t += τ
    # do something mutating states
end

Iterate clocks by for loop is recommended, and any operations on recorded variable should be avoided out of loop. During the iteration, some states of Clock were updated automatically, and if iteration finished when reach to end, Clock will be initialized by init! automatically. Besides, iteration is the only way to update the current time of DiscreteClock.

Entry type

In this package, an entry stores changes of a single variable, a number or an element of an array. There are two types of entries:

  • DynamicEntry for variable which represents a "state" changing overtime,
  • StaticEntry for variable which represents a "trait" which not changes overtime and only assigned once, but may be added or deleted with "mutation" and "extinction".

The different entry types determine how the variable is recorded, and how the changes is accessed.

For example, a "dynamic" variable $d$ firstly assigned to $1$ at time $t_1$ and changed to $2$ at time $t_2$, thus the value of it is $0$ for $t < t_1$, $1$ for $t_1 \leq t < t_2$, and $2$ for $t_2 \leq t$.

But, for a "static" variable $s$ added with value $1$ at time $t_1$ and deleted at time $t_2$, the value of it is $1$ for $t_1 \leq t \leq t_2$, and $0$ for $t \leq t_1$ or $t_2 \leq t$.

Recorded variable

With a clock c and an entry type E recorded variables can be created by recorded:

recorded(E, c, 1) # create a recorded number
recorded(E, c, [1, 2, 3]) # create a recorded array

For recorded arrays, most operation and functions for array, like linear algebra operation and broadcasting, works the same as normal Array. Besides, with the support of my another package ResizingTools, recorded arrays can be resized by resize! at each dimensions. See its documentation for more details.

For recorded numbers, most operation for number works the same as normal Number as well. Besides, because a recorded number type is not a subtype of the specific number type, for example, typeof(recorded(E, c, 1)) is not a subtype of Int, there are two function to test type of recorded number:

  • issubtype: test if a recorded number type is a subtype of given normal number type,
  • isnum: test if a recorded number type is the given type number type.

For example, for a record number n = recorded(E, c, 1) isnum(Integer, n) and issubtype(Integer, typeof(n)) return true, and isnum(AbstractFloat, n) and issubtype(AbstractFloat, typeof(n)) return false.

If there are any commonly used functions were not implemented or there are any performance loss comparing to the normal number and array, leave me an issue or create a pull request on GitHub.

However, for custom functions. Warping recorded variables with state or convert it to a normal type by convert are common solution: like:

state(recorded(E, c, 1)) # warp a recorded number to a state
convert(Array, recorded(E, c, [1, 2, 3])) # convert a recorded array to an array
Note

state don't allocate but may be unsafe for matrix and higher dimension array because of Base.unsafe_wrap and Base.unsafe_convert. I'm not sure if it would cause any problem or not. If anybody know more about it, please leave me an issue.

Accessing changes

As shown above, the changes of recorded variables are stored in entries. To access the changes, use getentries to get the entries:

e = getentries(n) # get the entry of a recorded number n
es = getentries(A) # get entries of a recorded array A

There are some methods to access the changes stored in entries:

  • getts to get the time of each change,
  • getvs to get the value of each change,
  • tspan to get the time span of the entry,
  • gettime to get the value at given time(s).

Plotting

There is a two user recipes for Plots.jl:

  • timeseries plots time series of given recorded variables,
  • phaseportrait plots phase portrait of given recorded variables.

Both of them accept arguments with there types:

  • a recorded variable, like timeseries(v) where v is a recorded variable;
  • a collection of entries, like timeseries(es), where es is a collection of entries;
  • multiple entries, like timeseries(e1, e2), where e1 and e2 are entries.

See example for more information.