Skip to content
Romain Franceschini edited this page Apr 27, 2020 · 7 revisions

Quartz features a multiscale representation of simulated time, as described in the following paper. The approach addresses the drawbacks of fixed-point or floating-point numbers while having restrained performances penalty compared to full arbitrary-precision approaches.

Without going into too much details, the idea is that the simulator keeps a reference to a precise representation of the current simulated time while models perceive and express time relative to their associated precision level, with duration values.

A precise point in simulated time is implemented as an arbitrary-precision integer under the TimePoint class. Its main purpose is to describe event times as offsets from a common reference point and it is meant to be used internally. The modeller should not directly manipulate such values. Instead, modellers may perturbate simulated time through Duration values.

Expressing duration values

A Duration is a fixed-point time data type which encapsulates a multiplier and a precision level (a Scale value).

Models express time values relative to the current simulated time. If an event is measured from the most recent past event, a duration value can express the elapsed time since this past event. Similarly, a duration can represent a time value from the current point in simulated time to an imminent future event, a.k.a. as a planned duration.

Duration values are typically constructed with a number and a scale (Scale | Int):

Duration.new(30, Scale::MILLI)  # 30 milli time units
Duration.new(30, -2)            # 30 micro time units
Duration.new(2)                 # 2 time units

Scale

Scale values are used to represent the precision level at which a Model is able to process inputs, as well as express the duration of its internal state. It is an approximation of the degree to which Duration values are altered.

The multiscale time representation rely on a base-1000 SI time unit, which is used to represent one scale from the next. Scale values store precision levels with a Int8 integer, which means scales can go from 1000^-128 to 1000^127. The base scale is 1000^0.

Convenient constants are defined for common scales, ranging from zepto (1000^-7) to zetta (1000^7), e.g. Scale::ZEPTO, ..., Scale::MILLI, Scale::BASE, Scale::KILO, ..., Scale::ZETTA. Otherwise, scale values can be initialized with an integer, e.g. Scale.new(-3).

Representable values

The maximum representable duration for a chosen scale is 1000^5 - 1. If a duration represents microseconds, the representable range goes from 0us to 999,999,999.999999us (~ 31 years).

Infinity

A duration is considered infinite when the 1000^5 limit is reached.

Duration.new(1000**5).infinite? # => true

An infinite duration can also be constructed from infinite float constant value or via the .infinity static method.

Duration.new(Float64::INFINITY).infinite? # => true
Duration.infinity

By default, operations on durations will return a duration with a coarser or a refined scale:

Duration.new(4) + Duration.new(10, -1) # => Duration.new(4010, -1)
1000000 * Duration.new(1000000000, -5) # => Duration.new(1000000000000, -4)
(Duration(1000**5 - 1) + 1).infinite?  # => false, (value is 1_000_000_000_000 kilo time units)

In other words, is has wrapping arithmetic semantics.

Fixed durations

You may want to change the semantics of Duration to saturating arithmetic semantics.

This can be done via the constructor, e.g.

(Duration.new(1000**5-1, -4, true) + 1).infinite? # => true

or through the #fixed and #fixed_at methods:

(Duration.new(1000**5-1, -4).fixed + 1).infinite? # => true

Syntactic sugar

Quartz also extends numbers with convenient methods to build duration values:

30_milli_time_units  # 30 milli time units
30_micro_time_units  # 30 micro time units
2.time_units         # 2 time units

Finally, the Quartz.duration is provided, and must receive a number literal along with an optional scale unit. The scale unit can be specified with a constant expression (e.g. "kilo") or with a Scale value.

Durations and floats

If you use a floating-point value to initialize the multiplier of the duration, only the integer part is used to initialize the duration value:

Duration.new(10.84512, kilo) # => 10000 time units

As it is common to manipulate such values in models, it is possible to "guess" a scale from a float using Duration.from:

Duration.from(0.000000752) # => 752 nano time units

Implications of multiscale time

Allowing models to operate at disparate scales in the same simulation have several implications.

Explicit model precision

Models in a multiscale time environment requires an explicit precision to be defined.

This can be done via the precision macro for discrete event models:

class DiscreteEventModel < Quartz::AtomicModel
  precision nano
end

For discrete time models, the precision is included when you choose a time base using the delta macro:

class DiscreteTimeModel
  delta 10, milli
end

Perceiving input events

The virtual time at which a receiver model gets an input from a model with a finer precision is truncated to the receiver precision. For example, consider the two following models with different assigned precisions:

+---------+     +---------+
|         |     |         |
|    A    +---->+    B    |
|         |     |         |
|  micro  |     |  milli  |
+---------+     +---------+

Model 'A' generates values sent to the passive model 'B'. If the base time unit represents seconds, when model 'A' sends its first output value, say at time 5320μs, the model 'B', which operates at a greater scale will perceive the input event from 'A' in milliseconds. The elapsed time since last 'B' activation is then 5ms.

Note there is no going back in time since the simulator always maintains a precise value. However this value can appear truncated to match the chosen time abstraction of a model. Causality is still guaranteed.

To avoid such behavior and perceive finer elapsed times, the model precision has to be adjusted accordingly. As a rule of thumb, substract 3 from the scale at which your model operate autonomously.

Limit on planning future events

When choosing a precision level for your model, bear in mind that duration values cannot exceed 1000^5 - 1.

As such, the next planned internal event of a model cannot exceed this limit.

Infinite elapsed durations

When coupling models with extremely disparate scales, it is possible that a model perceive an infinite elapsed duration.

This means a model with a coarser precision sends an output event that is perceived as an input event by a model with a finer precision. The latter model was inactive for a duration that exceeds the 1000**5 - 1 limit when the duration is converted to the finer precision.


Next: Defining models

Clone this wiki locally