Skip to main content

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 / syntaxDescription
(...), f(...)Grouping, function calls
!Factorial
^Exponentiation
unary -Unary operators
*, /Multiplication and division
+, -Addition and subtraction
cond ? valIfTrue : valIfFalseConditional 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 / syntaxDescription
(...)Parentheses
notlogical not
xorexclusive or
==, !=, <, >, <=, >=, inComparisons and membership tests
andLogical and
orLogical 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 X at time 10s.

  • Time slice:

    descriptorID[a,b]

    Example:

    X[0,50]

    → time series of X sliced 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 value
  • gmin(descriptor) → global minimum value
  • avg(descriptor) → average value
  • int(descriptor) → integral over time
  • auc(descriptor) → area under the curve
  • lastTime(descriptor) → last available time
  • firstTime(descriptor) → first available time
  • lastValue(descriptor) → last available value
  • firstValue(descriptor) → first available value
  • timeOfMax(descriptor) → time when maximum occurs
  • timeOfMin(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 X between 0 and 100 seconds:

    ddt(X[0,100])
  • All local minima of Y in 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 X at time 10s in Arm A is greater than 5, use Y at time 20s; otherwise, use Y at time 30s.

4. Referring to Arms

You can specify the arm of a trial explicitly:

  • With arm:

    descriptorID@armID

    Example:

    X@ArmA[10]

    → value of X in 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 xmin and xmax.

  • Example objective:

    X@ArmA[10] / X@ArmA[20] ∈ [0.8, 1.2]

    → ensures the ratio of X at 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.