Formula syntax
The operators and functions a formula node accepts, and how a formula reads other nodes.
A formula node computes its value from other nodes on the map. A formula is a single expression — not a script, and not a sequence of statements — and it always produces one number or one true/false value.
revenue * margin - fixed_costsReferring to other nodes
A bare name in a formula is a reference to another node's code. revenue means "the value of the
node whose reference code is revenue".
A node's reference code is shown on its card and in the inspector, under its title. Formulas read codes, not titles, so renaming a node does not break a formula.
If a formula names a code that no node has, that node is blocked with Formula node references unknown code "…", and so is anything that reads it. Nothing else on the map is affected.
If two formulas end up depending on each other, directly or through a chain, Radiant reports Dependency cycle detected and names the loop. Every node in the cycle is blocked; the rest of the map still evaluates.
Operators
Arithmetic, in the usual precedence, with parentheses to override it:
| Operator | Meaning |
|---|---|
+ - * / | Add, subtract, multiply, divide |
^ | Raise to a power, right-associative — 2 ^ 3 ^ 2 is 2 ^ (3 ^ 2) |
- (prefix) | Negate |
(revenue - fixed_costs) / months
base_rate ^ 2
-adjustmentComparisons produce true or false:
| Operator | Meaning |
|---|---|
< <= > >= | Ordering |
== != | Equal, not equal |
Logic combines them. These are words, not symbols:
| Operator | Meaning |
|---|---|
and or | Both, either |
not (prefix) | Negate a condition |
growth > 0 and margin >= 0.2
not (risk_flag or blocked)Comparisons allow for rounding
==, <, > and their relatives compare with a small tolerance, so two values that differ only
by floating-point noise count as equal rather than accidentally unequal.
Choosing between values
if(condition, then, otherwise) picks one of two values. There is no ternary ? : — the parser
rejects it and tells you to use if instead.
if(growth > 0, revenue * growth, 0)Only the branch that is taken is evaluated, so the untaken branch cannot cause an error.
Functions
Arithmetic
| Function | Arguments | Result |
|---|---|---|
min(a, b, …) | one or more | The smallest |
max(a, b, …) | one or more | The largest |
abs(x) | 1 | Distance from zero |
round(x) | 1 | Nearest whole number |
floor(x) | 1 | Rounded down |
ceil(x) | 1 | Rounded up |
clamp(x, low, high) | 3 | x held between low and high |
clamp fails if low is greater than high, rather than quietly swapping them.
clamp(forecast, 0, 1)
max(base_case, downside, upside)Probabilities and odds
These four convert between the ways a chance can be written. They exist because averaging or adjusting a probability is often better done in log-odds than directly.
| Function | Arguments | Result |
|---|---|---|
logit(p) | 1 | Log-odds of a probability |
inv_logit(x) | 1 | Back from log-odds to a probability |
odds(p) | 1 | Probability as odds |
prob(o) | 1 | Odds as a probability |
Each is strict about its input: logit requires a probability strictly between 0 and 1, odds
accepts 0 up to but not including 1, and prob requires non-negative odds. A value outside the
range is an error rather than an infinity that quietly spreads through the rest of the map.
inv_logit((logit(analyst_view) + logit(market_view)) / 2)Reading a distribution
These four ask a question about another node's whole range of simulated outcomes, rather than its single value. The first argument is a node reference, written directly — not an expression.
| Function | Arguments | Result |
|---|---|---|
cdf(node, x) | 2 | Share of outcomes at or below x |
prob_lt(node, x) | 2 | Share of outcomes below x |
prob_gt(node, x) | 2 | Share of outcomes above x |
quantile(node, p) | 2 | The value at probability p |
prob_gt(revenue, 1000000)
quantile(revenue, 0.9)These need a simulation
A distribution query reads a whole set of samples, which only a simulation produces. Outside a
run there is nothing to read, and the formula reports that it needs a simulation. quantile also
requires its probability to be between 0 and 1.
What a formula cannot do
The parser rejects these and says so, rather than interpreting them:
- Assignments and multiple statements. A formula is one expression.
- The ternary operator
a ? b : c. Useif(a, b, c). - Member access like
node.value. - Arrays, and text values. Numbers and true/false only.
Division by zero is an error, and so is a power with no real result — a negative base with a
fractional exponent, for example. Both stop the node rather than producing a silent NaN.