Skip to content

Rules as text

Operator overloading is the natural way to write a rule in Python:

sys.rule(score["poor"] | dti["high"], premium["high"])

It is not the natural way to write one in a config file, a spreadsheet column, a web form, or an LLM's reply. For those, every engine also accepts the form everybody already writes on paper:

sys.rule_from_text("IF score IS poor OR dti IS high THEN premium IS high",
                   [score, dti, premium])

The two produce exactly the same rule object.

Grammar

IF <expression> THEN <consequent> [WITH <weight>]

Expression — <variable> IS [NOT] <term> atoms combined with AND, OR, NOT and parentheses. &, | and ~ work too. Precedence is the usual one: NOT binds tightest, then AND, then OR.

IF x IS small OR y IS large AND NOT x IS large THEN out IS low
→ (x is small) OR ((y is large) AND NOT (x is large))

Keywords are case-insensitive. Names containing spaces go in quotes:

IF 'credit score' IS "very good" THEN premium IS low

Consequent — one of three forms, matching the three kinds of engine:

Form Engine Example
<variable> IS <term> Mamdani, IT2 Mamdani THEN premium IS high
<name> = <number> TSK (zero-order) THEN out = 3.5
<name> = <affine> TSK (first-order) THEN out = 2 + 3*x - 0.5*y
<name> = <label> classifier THEN class = default

Weight — an optional WITH <number> suffix, which is the rule weight (and, for a classifier, its certainty factor):

IF x IS small AND NOT y IS large THEN out = 3.5 WITH 0.8

Where the variables come from

rule_from_text resolves names against the variables the system already knows — everything its existing rules reference — plus whatever you pass in. So only the rules that introduce a new variable need the list:

sys = fz.Mamdani()
sys.rule_from_text("IF score IS poor THEN premium IS high", [score, premium])
sys.rule_from_text("IF score IS good THEN premium IS low")     # already known
sys.rule_from_text("IF dti IS high THEN premium IS high", [dti])

An unknown variable or term is an error naming what is known, rather than a silent misparse:

ValueError: unknown variable 'dti'; known: ['premium', 'score']

Parsing without an engine

from fuzzytool.dsl import parse_rule

antecedent, consequent, weight = parse_rule(
    "IF NOT x IS small THEN out = 2 + 3*x WITH 0.5", [x, out])

The result splats straight into any engine's rule method, which is what rule_from_text does internally.

Why this exists

Three concrete reasons:

  • Non-programmers can author rules. A domain expert writing rules in a spreadsheet is the normal way a real rule base gets built, and a column of text is a far better interface than a Python file.
  • Rules survive round trips. Text rules go in configuration, in a database, in a form — anywhere a Python expression cannot go.
  • LLM agents can write them. Paired with the agents integration, a model can propose a rule as text and you parse it into a real, checkable object — then run audit on the result before you trust it.

See also: Variables & rules, Auditing a rule base.