Using formulas
How to use formulas
Base formulas
Formulas can be used in many fields, such as the values the parameters, reaction rates, etc.
In a formula, you can use MathJS syntax and a subset of the associated operators, as well as reference components using their ID. You can also precise units if needed (typically to use a constant value if your model is unit checked) with the following syntax: u(unit). See How-to for more details on units.
The most common operators are: (, ), +, -, *, ^, exp(), sin() (and most common math functions) and cond ? valIfTrue : valIfFalse (if-then-else syntax).
The precedence order of these operators is the following, from highest to lowest:
| Operators / syntax | Description |
|---|---|
(...), f(...) | Grouping, function calls |
! | Factorial |
^ | Exponentiation |
unary - | Unary operators |
*, / | Multiplication and division |
+, - | Addition and subtraction |
cond ? valIfTrue : valIfFalse | Conditional expression |
For instance
x + y * z
is equivalent to
x + (y * z)
because * has a higher precedence than +
💡 When in doubt, use parentheses to make the intended order explicit.
An example:
(k1 < 3 * u (mol/s)) ? exp(-x) : 0
which translates into: "if value of parameter k1 is lesser than 3 mol/s then value is exp(-x) else it is 0"
You can do random draws of values from a normal (respectively uniform) distribution by using rnorm(seed, index, mean, stdev)(respectively runif(seed, index, min, max)) where the seed and index are integers, used for reproducibility. All of these inputs (seed, index, mean, stdev, min, max) can be defined as formulas, using other component's IDs. Note that rnorm and runif are not supported by the conversion from jinko's format to SBML for the moment, so downloading such a model in SBML will trigger an error.
Or you can use casing to define a value as a condition of a categorical parameter ID:
case ID of { value1OfID -> value1; value2OfID -> value2; _ -> value3 }
which translates into:
- if ID has value1OfID, value1 will be returned
- if ID has value2OfID, value2 will be returned
- else, value3 will be returned.
Conditions
Conditions are used in the first part of the "if-then-else" syntax. They are also used as top-level values, for instance baseline check conditions, event triggers, etc. Conditions are defined using common operators: logical and, logical or, etc. The precedence order of these operators is described in the following table, from highest to lowest:
| Operators / syntax | Description |
|---|---|
(...) | Parentheses |
not | logical not |
xor | exclusive or |
==, !=, <, >, <=, >=, in | Comparisons and membership tests |
and | Logical and |
or | Logical or |
For instance
(x < 3) or (y <= 1) and (z == 0)
is equivalent to
(x < 3) or ((y <= 1) and (z == 0))
because and has a higher precedence than or
💡 When in doubt, use parentheses to make the intended order explicit.
You may also define a condition based on the membership test of a categorical parameter. Here is an example:
origin in {APAC, EU}
which translates into: categorical parameter origin has value APAC or EU
Advanced outputs syntax
When defining advanced outputs, you can use formulas that combine descriptors, arms, and time reduction functions.
All time values in formulas are given in seconds.
A word of caution: be careful to use scalar functions—functions that convert a time series into a single scalar value—so that your output is a scalar and not an entire time series.
This section provides both the syntax and examples to help you get started.
1. Accessing a Descriptor at a Specific Time
You can extract the value of a descriptor at a given time point using:
-
Single time point:
descriptorID[t]Example:
X[10]→ value of
Xat time 10s. -
Time slice:
descriptorID[a,b]Example:
X[0,50]→ time series of
Xsliced between t=0s and t=50s.
2. Time Reduction Functions
Since objectives often require a single scalar value rather than a full time series, you can apply time reduction functions:
gmax(descriptor)→ global maximum valuegmin(descriptor)→ global minimum valueavg(descriptor)→ average valueint(descriptor)→ integral over timeauc(descriptor)→ area under the curvelastTime(descriptor)→ last available timefirstTime(descriptor)→ first available timelastValue(descriptor)→ last available valuefirstValue(descriptor)→ first available valuetimeOfMax(descriptor)→ time when maximum occurstimeOfMin(descriptor)→ time when minimum occurs
Example:
gmax(X[0,100])
→ maximum value of X between time 0 and 100.
3. Other functions
In addition to time reduction functions, Jinkō provides functions for advanced analysis of time series:
ddt(descriptor)→ computes the time derivative of the descriptor at each time point. Useful to study rates of change.localMinima(descriptor)→ returns the local minima in the time series. Each minimum is reported with its value and corresponding time.localMaxima(descriptor)→ returns the local maxima in the time series. Each maximum is reported with its value and corresponding time.ifThenElse(condition, formulaIfTrue, formulaIfFalse)→ conditional formula that evaluates one formula if a condition is true, and another if false.
Examples:
-
Time derivative of
Xbetween 0 and 100 seconds:ddt(X[0,100]) -
All local minima of
Yin Arm B:localMinima(Y@ArmB[0,50]) -
All local maxima of biomarker
Z(all arms):localMaxima(Z) -
Conditional example using
ifThenElse:ifThenElse(X@ArmA[10] > 5, Y@ArmA[20], Y@ArmA[30])→ If
Xat time 10s in Arm A is greater than 5, useYat time 20s; otherwise, useYat time 30s.
4. Referring to Arms
You can specify the arm of a trial explicitly:
-
With arm:
descriptorID@armIDExample:
X@ArmA[10]→ value of
Xin Arm A at time 10. -
Without arm:
descriptorID[10]→ computed across all arms, each arm contributing equally to the objective.
⚠️ Important rule:
You cannot mix namespaced (descriptor@arm) and non-namespaced (descriptor) descriptors in the same formula.
5. Building Objectives
Objectives typically check whether a formula falls within a certain range.
For example:
-
Simple ratio check:
X@ArmA[10] / X@ArmA[20]You can then define an acceptable range, e.g. between
xminandxmax. -
Example objective:
X@ArmA[10] / X@ArmA[20] ∈ [0.8, 1.2]→ ensures the ratio of
Xat time 10 vs. time 20 in Arm A stays within 0.8–1.2.
Delay differential equations syntax
We support delay differential equations. To introduce a delay in a formula, the syntax is the following: delay(y,delta) which will evaluate to the arbitrary expression y at time - delta. Note that
- when (time - delta) < tmin, delay(y,delta) will evaluate to y's initial condition. For now, only constant initial conditions are supported, meaning that for t<tmin, y will evaluate to y(t=tmin)
- if delta evaluates to a negative value at any time, it will lead to a runtime error.