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:
ContinuousClockfor continuous-time process,- and
DiscreteClockfor discrete-time process.
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
endwhich 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
endIterate 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:
DynamicEntryfor variable which represents a "state" changing overtime,StaticEntryfor 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 arrayFor 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 arraystate 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 AThere are some methods to access the changes stored in entries:
gettsto get the time of each change,getvsto get the value of each change,tspanto get the time span of the entry,gettimeto get the value at given time(s).
Plotting
There is a two user recipes for Plots.jl:
timeseriesplots time series of given recorded variables,phaseportraitplots phase portrait of given recorded variables.
Both of them accept arguments with there types:
- a recorded variable, like
timeseries(v)wherevis a recorded variable; - a collection of entries, like
timeseries(es), whereesis a collection of entries; - multiple entries, like
timeseries(e1, e2), wheree1ande2are entries.
See example for more information.