Skip to content

Toolkit

Provide the building blocks of Sage scripts that assemble Lean proofs.

A script sees the Lean goal as a Problem, finds what the proof needs with the toolkit's search primitives, and writes the proof with its Lean emitters. Importing this package imports SageMath.

Condition(text, expr, kind, discharged_by=None, bound=None) dataclass

Hold a domain condition a symbolic expression of the problem needs.

Parameters:

Name Type Description Default
text str

The Lean text of the condition, such as y ≠ 0.

required
expr Expression | Matrix | None

The constrained expression, in Sage's symbolic ring, or None when it does not parse.

required
kind str

ne (e ≠ 0), nonneg (0 ≤ e), pos (0 < e) or le (e ≤ bound).

required
discharged_by str | None

The hypothesis that states it, or None.

None
bound Expression | Matrix | None

The upper bound of an le condition, in Sage's symbolic ring.

None

Coordinates(n) dataclass

The coordinates operators act in: n Sage variables, rendered in Lean as y (one) or y i (several).

Parameters:

Name Type Description Default
n int

The number of coordinates.

required

symbols property

Return the Sage coordinates x0, x1, ….

multiplication(e)

Return the operator of multiplication by a polynomial in the coordinates.

Parameters:

Name Type Description Default
e object

The polynomial (a Sage expression in x0, x1, …).

required

Returns:

Type Description
Operator

The operator.

Source code in src/veriphy/toolkit/_operators.py
103
104
105
106
107
108
109
110
111
112
113
def multiplication(self, e: object) -> Operator:
    """Return the operator of multiplication by a polynomial in the coordinates.

    Args:
        e: The polynomial (a Sage expression in `x0, x1, …`).

    Returns:
        The operator.
    """
    text = self.render(e)
    return Operator(lambda f: SR(e) * f, lambda t: f'fun y => ({text}) * {_paren(t)} y', 0)

partial(i)

Return the partial derivative along the coordinate i (deriv in one variable).

Parameters:

Name Type Description Default
i int

The coordinate.

required

Returns:

Type Description
Operator

The operator.

Source code in src/veriphy/toolkit/_operators.py
115
116
117
118
119
120
121
122
123
124
125
126
127
def partial(self, i: int) -> Operator:
    """Return the partial derivative along the coordinate `i` (`deriv` in one variable).

    Args:
        i: The coordinate.

    Returns:
        The operator.
    """
    x = self.symbols[i]
    if self.n == 1:
        return Operator(lambda f: diff(f, x), lambda t: f'deriv {_paren(t)}', 1)
    return Operator(lambda f: diff(f, x), lambda t: f'fun y => fderiv ℝ {_paren(t)} y (Pi.single {i} 1)', 1)

render(e)

Return a polynomial in the coordinates as Lean text in the bound variable y.

Parameters:

Name Type Description Default
e object

A polynomial with rational coefficients in the coordinates.

required

Returns:

Type Description
str

The text.

Source code in src/veriphy/toolkit/_operators.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def render(self, e: object) -> str:
    """Return a polynomial in the coordinates as Lean text in the bound variable `y`.

    Args:
        e: A polynomial with rational coefficients in the coordinates.

    Returns:
        The text.
    """
    ring = PolynomialRing(QQ, self.n, [f'x{i}' for i in range(self.n)])
    p = ring(SR(e))
    terms = []
    for coefficient, monomial in zip(p.coefficients(), p.monomials(), strict=True):
        factors = []
        for i, k in enumerate(monomial.exponents()[0]):
            if k:
                base = 'y' if self.n == 1 else f'y {i}'
                factors.append(base if k == 1 else f'({base}) ^ {k}' if self.n > 1 else f'y ^ {k}')
        q = QQ(coefficient)
        scalar = str(q) if q.denominator() == 1 else f'({q.numerator()} / {q.denominator()})'
        leading = [] if factors and q == 1 else [f'({scalar})']
        terms.append(' * '.join([*leading, *factors]))
    return ' + '.join(terms) if terms else '0'

scalar(c)

Return multiplication by a rational constant.

Parameters:

Name Type Description Default
c object

The constant.

required

Returns:

Type Description
Operator

The operator.

Source code in src/veriphy/toolkit/_operators.py
129
130
131
132
133
134
135
136
137
138
def scalar(self, c: object) -> Operator:
    """Return multiplication by a rational constant.

    Args:
        c: The constant.

    Returns:
        The operator.
    """
    return self.multiplication(QQ(c))

Definition(function, var, body, hyp, text) dataclass

Hold an explicit definition of an unknown function, from a hypothesis x = fun s => body.

Parameters:

Name Type Description Default
function str

The function's Sage name.

required
var Expression

The variable of the body, a Sage symbol.

required
body Expression | None

The body, in var and the problem's variables.

required
hyp str

The hypothesis stating the definition.

required
text str

The Lean text of the function (the lambda).

required

Derivative(var, body, point, function, at) dataclass

Hold a derivative the translation met, deriv f point.

Parameters:

Name Type Description Default
var Expression

The variable of differentiation, a Sage symbol.

required
body Expression | None

The function's body, in var and the problem's variables.

required
point Expression | None

The point, over the problem's variables.

required
function str

The Lean text of the function.

required
at str

The Lean text of the point.

required

ExistsFunction(function, lean, conjuncts) dataclass

Hold a goal ∃ x, C₁ ∧ C₂ ∧ … over an unknown function.

Parameters:

Name Type Description Default
function str

The function's Sage name.

required
lean str

The binder's Lean name.

required
conjuncts tuple[tuple[Expression | None, Expression | None], ...]

Each conjunct as its universally quantified variable (None when none) and its symbolic form lhs - rhs, in the function and the problem's variables.

required

FieldContext(generator, modulus, fact, setup) dataclass

How a field's generator appears in Lean.

Parameters:

Name Type Description Default
generator str

The generator's Lean text, such as Real.sqrt 2, ζ or α.

required
modulus Polynomial | None

Its minimal polynomial over ℚ, in x; None for a transcendental parameter.

required
fact str | None

The name of the Lean fact modulus(generator) = 0 (or Real.sqrt d ^ 2 = d), when there is one.

required
setup Lean | None

The tactic establishing the fact, or nothing when the goal's hypotheses already state it.

required

Fusion(labels, dual, rules, f_values=field(default_factory=dict), r_values=field(default_factory=dict), field='rational') dataclass

Multiplicity-free fusion data on labels 0 … n-1, the unit first.

Parameters:

Name Type Description Default
labels list[str]

Names of the simple objects.

required
dual list[int]

The dual of each label.

required
rules dict[tuple[int, int], list[int]]

(a, b) → [c, …], the labels in a ⊗ b.

required
f_values dict[Key, FieldElement]

(a, b, c, d, e, f) → value where (F^{abc}_d)_{ef} is not 1.

field(default_factory=dict)
r_values dict[Triple, FieldElement]

(a, b, c) → value where R^{ab}_c is not 1.

field(default_factory=dict)
field str

'sqrt5', 'zeta16' or 'rational': where the values live.

'rational'

size property

Return the number of labels.

f(*labels)

Return (F^{abc}_d)_{ef} for the labels a, b, c, d, e, f, zero when inadmissible.

Source code in src/veriphy/toolkit/_fusion.py
59
60
61
62
63
64
def f(self, *labels: int) -> FieldElement:
    """Return `(F^{abc}_d)_{ef}` for the labels `a, b, c, d, e, f`, zero when inadmissible."""
    a, b, c, d, e, f = labels
    if not (self.n(a, b, e) and self.n(e, c, d) and self.n(b, c, f) and self.n(a, f, d)):
        return QQ(0)
    return self.f_values.get((a, b, c, d, e, f), QQ(1))

n(a, b, c)

Return the multiplicity of c in a ⊗ b.

Source code in src/veriphy/toolkit/_fusion.py
55
56
57
def n(self, a: int, b: int, c: int) -> int:
    """Return the multiplicity of `c` in `a ⊗ b`."""
    return 1 if c in self.rules.get((a, b), []) else 0

r(a, b, c)

Return R^{ab}_c, zero when inadmissible.

Source code in src/veriphy/toolkit/_fusion.py
66
67
68
def r(self, a: int, b: int, c: int) -> FieldElement:
    """Return `R^{ab}_c`, zero when inadmissible."""
    return self.r_values.get((a, b, c), QQ(1)) if self.n(a, b, c) else QQ(0)

Hyp(name, poly, rel, text='', expr=None) dataclass

Hold one hypothesis or goal of a problem, as scripts see it.

Parameters:

Name Type Description Default
name str | None

The Lean hypothesis name, or None for the goal and existential body.

required
poly MPolynomial | None

The polynomial p of the constraint p rel 0, or None when it has no polynomial form.

required
rel str | None

The relation tag, one of eq, le, lt, ne, or None without a polynomial form.

required
text str

The Lean text of the proposition.

''
expr Expression | Matrix | None

The symbolic form e of e rel 0 (or of the proposition's sides' difference), or None.

None

Lean(text, kind) dataclass

Hold a piece of Lean source text and its syntactic category.

Parameters:

Name Type Description Default
text str

The Lean source.

required
kind str

term or tactic.

required

Examples:

>>> Lean('ring1', 'tactic')
Lean(text='ring1', kind='tactic')

__post_init__()

Reject an unknown syntactic category.

Source code in src/veriphy/toolkit/_lean.py
32
33
34
35
36
def __post_init__(self) -> None:
    """Reject an unknown syntactic category."""
    if self.kind not in KINDS:
        message = f'Lean text kind must be one of {list(KINDS)}, got {self.kind!r}'
        raise VeriphyError(message)

__str__()

Return the Lean source.

Source code in src/veriphy/toolkit/_lean.py
38
39
40
def __str__(self) -> str:
    """Return the Lean source."""
    return self.text

MatrixIndex(rows, cols, text) dataclass

Hold the index type of a matrix indexed by sums of Fin types, such as Fin 1 ⊕ Fin 3.

Parameters:

Name Type Description Default
rows tuple[int, ...]

The sizes of the row index's summands, in order.

required
cols tuple[int, ...]

The sizes of the column index's summands, in order.

required
text str

The matrix type's Lean text.

required

Operator(act, lean, order) dataclass

A linear differential operator, with its action on Sage expressions and on Lean function texts.

Parameters:

Name Type Description Default
act Callable[[Expression], Expression]

The action on a Sage expression in the coordinates.

required
lean Callable[[str], str]

The action on the Lean text of a function, returning the Lean text of a function.

required
order int

The highest derivative order it applies.

required

__add__(other)

Return the sum.

Source code in src/veriphy/toolkit/_operators.py
42
43
44
45
46
47
48
def __add__(self, other: 'Operator') -> 'Operator':
    """Return the sum."""
    return Operator(
        lambda f: self.act(f) + other.act(f),
        lambda t: f'fun y => {_paren(self.lean(t))} y + {_paren(other.lean(t))} y',
        max(self.order, other.order),
    )

__mul__(other)

Return the composition self ∘ other.

Source code in src/veriphy/toolkit/_operators.py
38
39
40
def __mul__(self, other: 'Operator') -> 'Operator':
    """Return the composition `self ∘ other`."""
    return Operator(lambda f: self.act(other.act(f)), lambda t: self.lean(other.lean(t)), self.order + other.order)

__sub__(other)

Return the difference.

Source code in src/veriphy/toolkit/_operators.py
50
51
52
53
54
55
56
def __sub__(self, other: 'Operator') -> 'Operator':
    """Return the difference."""
    return Operator(
        lambda f: self.act(f) - other.act(f),
        lambda t: f'fun y => {_paren(self.lean(t))} y - {_paren(other.lean(t))} y',
        max(self.order, other.order),
    )

Problem(ring, hyps, goal_kind, goal=None, binders=list(), body=list(), atoms=dict(), carrier='', lhs=None, rhs=None, atom_types=dict(), domain=list(), symbols=dict(), tables=(lambda: (load_core_table(),))(), typed=list(), derivatives=list(), functions=dict(), definitions=list(), exists_function=None, matrix_indices=list()) dataclass

Hold a serialized Lean problem bound to Sage objects.

Parameters:

Name Type Description Default
ring MPolynomialRing_base

The polynomial ring over the problem's atoms.

required
hyps list[Hyp]

The hypotheses.

required
goal_kind str

One of rel, false, exists and other.

required
goal Hyp | None

The goal of a rel or other goal, with its Lean text.

None
binders list[MPolynomial]

The generators bound by an exists goal.

list()
body list[Hyp]

The constraints of an exists goal.

list()
atoms dict[str, str]

The Lean text of each variable, by variable name.

dict()
carrier str

The Lean text of the type the problem's polynomials live in (such as ℚ), empty when unknown.

''
lhs Side | None

The left side of a relation goal, when the goal has a polynomial form.

None
rhs Side | None

The right side of a relation goal, when the goal has a polynomial form.

None
atom_types dict[str, str]

The Lean type of each variable (ℕ, ℤ, ℚ, ℝ, ℂ, matrix or other), by name.

dict()
domain list[Condition]

The domain conditions the problem's symbolic expressions need.

list()
symbols dict[str, Expression]

The variables as symbolic-ring variables, by name, for symbolic work.

dict()
tables tuple[Table, ...]

The translation tables in force, core first.

(lambda: (load_core_table(),))()
typed list[tuple[Expression, str]]

The sub-expressions Lean computes at another type (ℕ, ℤ) than their context, with that type.

list()
derivatives list[Derivative]

The derivatives the translation met, in the order it met them.

list()
functions dict[str, tuple[str, int]]

The unknown functions, by Sage name: their Lean name and arity.

dict()
definitions list[Definition]

Explicit definitions of unknown functions from the hypotheses.

list()
exists_function ExistsFunction | None

The goal's existential over an unknown function, when it is one.

None
matrix_indices list[MatrixIndex]

The index types of the matrices indexed by sums of Fin types, for rendering them back.

list()

find_hyp(name, allowed, family)

Find the hypothesis named name, checking its relation is usable by family.

Parameters:

Name Type Description Default
name str

The hypothesis name.

required
allowed tuple[str, ...]

The relation tags family accepts.

required
family str

The proof shape asking, named in errors.

required

Returns:

Type Description
Hyp

The hypothesis.

Raises:

Type Description
CertificateError

If no hypothesis has that name, or its relation is not in allowed.

Source code in src/veriphy/toolkit/_problem.py
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
def find_hyp(self, name: str, allowed: tuple[str, ...], family: str) -> Hyp:
    """Find the hypothesis named `name`, checking its relation is usable by `family`.

    Args:
        name: The hypothesis name.
        allowed: The relation tags `family` accepts.
        family: The proof shape asking, named in errors.

    Returns:
        The hypothesis.

    Raises:
        CertificateError: If no hypothesis has that name, or its relation is not in `allowed`.
    """
    by_name = {h.name: h for h in self.hyps}
    if name not in by_name:
        message = f"certificate names unknown hypothesis '{name}' (known: {sorted(map(str, by_name))})"
        raise CertificateError(message)
    hyp = by_name[name]
    if hyp.rel not in allowed:
        message = f"hypothesis '{name}' has relation '{hyp.rel}', not usable by {family}"
        raise CertificateError(message)
    return hyp

to_namespace()

Return the names a script sees.

Returns:

Type Description
dict[str, object]

problem, R, the generators by name, hyps, goal, goal_kind, binders, body, atoms, lhs,

dict[str, object]

rhs, domain, symbols and Hyp.

Source code in src/veriphy/toolkit/_problem.py
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
def to_namespace(self) -> dict[str, object]:
    """Return the names a script sees.

    Returns:
        `problem`, `R`, the generators by name, `hyps`, `goal`, `goal_kind`, `binders`, `body`, `atoms`, `lhs`,
        `rhs`, `domain`, `symbols` and `Hyp`.
    """
    names: dict[str, object] = {
        'problem': self,
        'R': self.ring,
        'Hyp': Hyp,
        'hyps': self.hyps,
        'goal': self.goal,
        'goal_kind': self.goal_kind,
        'binders': self.binders,
        'body': self.body,
        'atoms': self.atoms,
        'lhs': self.lhs,
        'rhs': self.rhs,
        'domain': self.domain,
        'symbols': self.symbols,
    }
    names.update({str(g): g for g in self.ring.gens()})
    return names

RewriteCandidate(lemma, reverse, side, namespace) dataclass

Hold a rewrite the problem's tables offer for its goal, with side conditions filled in where they could be.

Parameters:

Name Type Description Default
lemma str

The lemma name.

required
reverse bool

Rewrite right to left.

required
side tuple[str, ...]

The side conditions as Lean text over the goal's atoms.

required
namespace str

The table the entry came from.

required

emit(problem, proofs=None, at=None)

Return the rewrite step, passing each side condition as (by assumption) or a supplied proof.

Sage orders the arguments of a commutative pattern its own way, so a stated side condition is passed as (by assumption), which Lean runs after unifying the lemma with the goal and which therefore finds the right hypothesis whatever the order.

Parameters:

Name Type Description Default
problem Problem

The problem.

required
proofs Mapping[str, Lean | str] | None

A tactic proving a side condition no hypothesis states, by its text.

None
at str | None

A hypothesis to rewrite instead of the goal.

None

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If a side condition is neither stated nor supplied, naming it.

Source code in src/veriphy/toolkit/_symbolic_emit.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
def emit(self, problem: Problem, proofs: Mapping[str, Lean | str] | None = None, at: str | None = None) -> Lean:
    """Return the rewrite step, passing each side condition as `(by assumption)` or a supplied proof.

    Sage orders the arguments of a commutative pattern its own way, so a stated side condition is passed as
    `(by assumption)`, which Lean runs after unifying the lemma with the goal and which therefore finds the
    right hypothesis whatever the order.

    Args:
        problem: The problem.
        proofs: A tactic proving a side condition no hypothesis states, by its text.
        at: A hypothesis to rewrite instead of the goal.

    Returns:
        The tactic.

    Raises:
        CertificateError: If a side condition is neither stated nor supplied, naming it.
    """
    proofs = proofs or {}
    stated = {h.text for h in problem.hyps if h.name} | {c.text for c in problem.domain if c.discharged_by}
    args = []
    for condition in self.side:
        if condition in proofs:
            args.append(f'(by {proofs[condition]})')
        elif condition in stated:
            args.append('(by assumption)')
        else:
            message = f"side condition '{condition}' of {self.lemma} is not stated; pass its proof"
            raise CertificateError(message)
    rule = ' '.join([f'{"← " if self.reverse else ""}{self.lemma}', *args])
    return tactic(f'rw [{rule}]' + (f' at {at}' if at else ''))

Side(poly, text) dataclass

Hold one side of a relation goal, as scripts see it.

Parameters:

Name Type Description Default
poly MPolynomial

The side as a polynomial of the problem's ring.

required
text str

The side's Lean text, exactly as the goal writes it.

required

Table(namespace, functions=(), constants=(), coercions=(), rewrites=(), conventions=()) dataclass

Hold one namespace's entries.

Parameters:

Name Type Description Default
namespace str

mathlib, a library pack's name, or project.

required
functions tuple[FunctionEntry, ...]

The function entries.

()
constants tuple[ConstantEntry, ...]

The constant entries.

()
coercions tuple[str, ...]

The coercions translated as their argument.

()
rewrites tuple[RewriteEntry, ...]

The rewrite entries.

()
conventions tuple[tuple[str, str], ...]

The conventions the namespace fixes, as (key, value) pairs.

()

adjoint_matrices(table)

Return the matrices of ad eᵢ in the basis: column l holds the coordinates of ⁅eᵢ, eₗ⁆.

Parameters:

Name Type Description Default
table Table

Structure constants, as structure_constants returns them.

required

Returns:

Type Description
list[Matrix]

One matrix per basis element.

Source code in src/veriphy/toolkit/_lie.py
73
74
75
76
77
78
79
80
81
82
83
84
def adjoint_matrices(table: Table) -> list[Matrix]:
    """Return the matrices of `ad eᵢ` in the basis: column `l` holds the coordinates of `⁅eᵢ, eₗ⁆`.

    Args:
        table: Structure constants, as `structure_constants` returns them.

    Returns:
        One matrix per basis element.
    """
    n = len(table)
    ring = SageSequence([x for plane in table for row in plane for x in row]).universe()
    return [matrix(ring, n, n, lambda k, ell, i=i: table[i][ell][k]) for i in range(n)]

associativity_violations(data)

Return the label tuples where the fusion rules fail associativity.

Parameters:

Name Type Description Default
data Fusion

The fusion data.

required

Returns:

Type Description
list[tuple[int, int, int, int]]

The failing (a, b, c, d).

Source code in src/veriphy/toolkit/_fusion.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def associativity_violations(data: Fusion) -> list[tuple[int, int, int, int]]:
    """Return the label tuples where the fusion rules fail associativity.

    Args:
        data: The fusion data.

    Returns:
        The failing `(a, b, c, d)`.
    """
    labels = range(data.size)
    return [
        (a, b, c, d)
        for a, b, c, d in itertools.product(labels, repeat=4)
        if sum(data.n(a, b, e) * data.n(e, c, d) for e in labels)
        != sum(data.n(b, c, f) * data.n(a, f, d) for f in labels)
    ]

block_matrix_rows(blocks)

Stack matrices of the same width vertically.

Parameters:

Name Type Description Default
blocks Sequence[Matrix]

The matrices.

required

Returns:

Type Description
Matrix

The stacked matrix.

Source code in src/veriphy/toolkit/_representation.py
193
194
195
196
197
198
199
200
201
202
def block_matrix_rows(blocks: Sequence[Matrix]) -> Matrix:
    """Stack matrices of the same width vertically.

    Args:
        blocks: The matrices.

    Returns:
        The stacked matrix.
    """
    return matrix(QQ, [row for b in blocks for row in b.rows()])

bound_proof(function, point, bound, *, upper=True)

Return a proof of f(point) < bound (or bound < f(point) with upper=False).

Parameters:

Name Type Description Default
function str

exp, log, sin, cos or pi (the point is ignored for pi).

required
point Rational | int

A rational point (|point| ≤ 1 for exp, sin, cos; 0 < point < 2 for log).

required
bound Rational | int

The rational bound.

required
upper bool

Whether the bound is above the value.

True

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the function has no remainder bound in mathlib, the point is out of range, or the bound does not hold (with the enclosure Sage computed).

Examples:

>>> print(bound_proof('exp', 1, QQ(27183) / 10000))
exact lt_trans Real.exp_one_lt_d9 (by norm_num)
>>> try:
...     bound_proof('gamma', QQ(1) / 3, 3)
... except CertificateError as error:
...     print(str(error).split(';')[0])
no remainder bound is available for gamma in mathlib
Source code in src/veriphy/toolkit/_numerics.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
def bound_proof(function: str, point: Rational | int, bound: Rational | int, *, upper: bool = True) -> Lean:
    """Return a proof of `f(point) < bound` (or `bound < f(point)` with `upper=False`).

    Args:
        function: `exp`, `log`, `sin`, `cos` or `pi` (the point is ignored for `pi`).
        point: A rational point (`|point| ≤ 1` for `exp`, `sin`, `cos`; `0 < point < 2` for `log`).
        bound: The rational bound.
        upper: Whether the bound is above the value.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the function has no remainder bound in mathlib, the point is out of range, or the bound
            does not hold (with the enclosure Sage computed).

    Examples:
        >>> print(bound_proof('exp', 1, QQ(27183) / 10000))
        exact lt_trans Real.exp_one_lt_d9 (by norm_num)
        >>> try:
        ...     bound_proof('gamma', QQ(1) / 3, 3)
        ... except CertificateError as error:
        ...     print(str(error).split(';')[0])
        no remainder bound is available for gamma in mathlib
    """
    if function not in SUPPORTED:
        message = f'no remainder bound is available for {function} in mathlib; supported: {", ".join(SUPPORTED)}'
        raise CertificateError(message)
    point, bound = QQ(point), QQ(bound)
    value = _value(function, point)
    holds = (value < RIF(bound)) if upper else (value > RIF(bound))
    if not holds:
        message = f'the bound fails or is too tight: {function}({point}) ∈ {value.str(style="brackets")}'
        raise CertificateError(message)
    table = {'pi': (PI_UPPER if upper else PI_LOWER), 'exp': (E_UPPER if upper else E_LOWER) if point == 1 else []}
    direct = _from_table(table.get(function, []), bound, upper=upper)
    if direct is not None:
        return tactic(f'exact {direct}')
    side = 'h.2' if upper else 'h.1'
    if function == 'exp':
        if abs(point) > 1:
            message = 'the exponential series bound needs |x| ≤ 1; write exp x as a power of exp (x / m)'
            raise CertificateError(message)
        n = _series_order('exp', point, bound, upper=upper)
        lines = [
            f'have h := Real.exp_bound (x := {_decimal(point)}) (by norm_num [abs_le]) (n := {n}) (by norm_num)',
            'norm_num [Finset.sum_range_succ, Nat.factorial, abs_le] at h',
            f'linarith [{side}]',
        ]
    elif function == 'log':
        if not 0 < point < LOG_RADIUS:
            message = 'the logarithm series bound needs 0 < x < 2; use log (a * b) = log a + log b first'
            raise CertificateError(message)
        n = _series_order('log', point, bound, upper=upper)
        lines = [
            f'have h := Real.abs_log_sub_add_sum_range_le (x := 1 - {_decimal(point)}) (by norm_num [abs_lt]) {n}',
            'norm_num [Finset.sum_range_succ, abs_le] at h',
            f'linarith [{side}]',
        ]
    else:
        if abs(point) > 1:
            message = f'the {function} bound needs |x| ≤ 1'
            raise CertificateError(message)
        lemma = 'Real.sin_bound' if function == 'sin' else 'Real.cos_bound'
        lines = [
            f'have h := {lemma} (x := {_decimal(point)}) (by norm_num [abs_le])',
            'norm_num [abs_le] at h',
            f'linarith [{side}]',
        ]
    return tactic('\n'.join(lines))

bracket_relations(basis, names, *, carrier='ℚ', definitions=True, pairs=None, entrywise_limit=0)

Return have facts ⁅eᵢ, eⱼ⁆ = Σ cᵢⱼₖ • eₖ for the basis, each proved in Lean.

Over ℤ or ℚ the kernel checker VeriphyCheck.bracket_eq_of_check proves each relation from the matrices (which it unfolds); otherwise the proof is entrywise. The coefficients must be rational.

Parameters:

Name Type Description Default
basis Sequence[Matrix]

The basis matrices.

required
names Sequence[str]

The Lean terms naming them, in the same order (definitions, or matrix literals).

required
carrier str

The Lean type of the entries.

'ℚ'
definitions bool

Whether the names are definitions an entrywise proof must unfold.

True
pairs Sequence[tuple[int, int]] | None

The pairs (i, j) to state; every pair i < j when None.

None
entrywise_limit int

Prove entrywise up to this dimension even over ℤ or ℚ.

0

Returns:

Type Description
Lean

The facts, named hb_i_j.

Raises:

Type Description
CertificateError

If a structure constant is not rational.

Examples:

>>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
>>> print(bracket_relations([e, f, h], ['e', 'f', 'h'], pairs=[(2, 0)]))
have hb_2_0 : ⁅h, e⁆ = 2 • e := VeriphyCheck.bracket_eq_of_check (by decide +kernel)
Source code in src/veriphy/toolkit/_lie.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
def bracket_relations(
    basis: Sequence[Matrix],
    names: Sequence[str],
    *,
    carrier: str = 'ℚ',
    definitions: bool = True,
    pairs: Sequence[tuple[int, int]] | None = None,
    entrywise_limit: int = 0,
) -> Lean:
    """Return `have` facts `⁅eᵢ, eⱼ⁆ = Σ cᵢⱼₖ • eₖ` for the basis, each proved in Lean.

    Over ℤ or ℚ the kernel checker `VeriphyCheck.bracket_eq_of_check` proves each relation from the matrices (which
    it unfolds); otherwise the proof is entrywise. The coefficients must be rational.

    Args:
        basis: The basis matrices.
        names: The Lean terms naming them, in the same order (definitions, or matrix literals).
        carrier: The Lean type of the entries.
        definitions: Whether the names are definitions an entrywise proof must unfold.
        pairs: The pairs `(i, j)` to state; every pair `i < j` when `None`.
        entrywise_limit: Prove entrywise up to this dimension even over ℤ or ℚ.

    Returns:
        The facts, named `hb_i_j`.

    Raises:
        CertificateError: If a structure constant is not rational.

    Examples:
        >>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
        >>> print(bracket_relations([e, f, h], ['e', 'f', 'h'], pairs=[(2, 0)]))
        have hb_2_0 : ⁅h, e⁆ = 2 • e := VeriphyCheck.bracket_eq_of_check (by decide +kernel)
    """
    table = structure_constants(basis)
    chosen = pairs if pairs is not None else [(i, j) for i in range(len(basis)) for j in range(i + 1, len(basis))]
    size = basis[0].nrows()
    lines = []
    for i, j in chosen:
        coefficients = table[i][j]
        if not all(c in QQ for c in coefficients):
            message = f'⁅{names[i]}, {names[j]}⁆ has non-rational structure constants {coefficients}'
            raise CertificateError(message)
        statement = f'⁅{names[i]}, {names[j]}⁆ = {smul_combination(coefficients, names)}'
        used = [names[i], names[j], *(n for n, c in zip(names, coefficients, strict=True) if QQ(c) != 0)]
        unfold = list(dict.fromkeys(used)) if definitions else []
        proof = _identity_proof(
            'VeriphyCheck.bracket_eq_of_check',
            bracket=True,
            carrier=carrier,
            unfold=unfold,
            size=size,
            limit=entrywise_limit,
        )
        lines.append(f'have hb_{i}_{j} : {statement} := {proof}')
    return tactic('\n'.join(lines))

by_cases(name, proposition, positive, negative)

Split on a proposition, with one tactic block per branch.

Parameters:

Name Type Description Default
name str

The hypothesis name in each branch (use fresh_name).

required
proposition Lean

The proposition split on, a term.

required
positive Lean

The block for the branch where it holds.

required
negative Lean

The block for the branch where it fails.

required

Returns:

Type Description
Lean

The tactic.

Examples:

>>> print(by_cases('hx', term('x ≤ 0'), tactic('nlinarith'), tactic('positivity')))
by_cases hx : x ≤ 0
· nlinarith
· positivity
Source code in src/veriphy/toolkit/_emit.py
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
def by_cases(name: str, proposition: Lean, positive: Lean, negative: Lean) -> Lean:
    """Split on a proposition, with one tactic block per branch.

    Args:
        name: The hypothesis name in each branch (use `fresh_name`).
        proposition: The proposition split on, a term.
        positive: The block for the branch where it holds.
        negative: The block for the branch where it fails.

    Returns:
        The tactic.

    Examples:
        >>> print(by_cases('hx', term('x ≤ 0'), tactic('nlinarith'), tactic('positivity')))
        by_cases hx : x ≤ 0
        · nlinarith
        · positivity
    """
    _expect(proposition, 'term', 'a split proposition')
    blocks = [_expect(b, 'tactic', 'a branch') for b in (positive, negative)]
    return tactic(f'by_cases {name} : {proposition}\n' + '\n'.join(f'· {_indent_block(b)}' for b in blocks))

calc_chain(start, steps, problem)

Build a calc chain from start through each step's expression, every step with its own tactic.

Parameters:

Name Type Description Default
start object

The first expression, a Lean term or a polynomial of the problem.

required
steps Sequence[tuple[str, object, Lean]]

Triples of a relation (=, ≤, <, or eq, le, lt), the next expression and the tactic proving the step.

required
problem Problem

The problem.

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
VeriphyError

If there are no steps.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['x'], 'goal': {'kind': 'false'}})
>>> print(calc_chain('(x0 + 1)^2', [('=', 'x0^2 + 2*x0 + 1', tactic('ring'))], prob))
calc x ^ 2 + 2 * x + 1
    _ = x ^ 2 + 2 * x + 1 := by ring
Source code in src/veriphy/toolkit/_emit.py
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
def calc_chain(start: object, steps: Sequence[tuple[str, object, Lean]], problem: Problem) -> Lean:
    """Build a `calc` chain from `start` through each step's expression, every step with its own tactic.

    Args:
        start: The first expression, a Lean term or a polynomial of the problem.
        steps: Triples of a relation (`=`, `≤`, `<`, or `eq`, `le`, `lt`), the next expression and the tactic proving
            the step.
        problem: The problem.

    Returns:
        The tactic.

    Raises:
        VeriphyError: If there are no steps.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['x'], 'goal': {'kind': 'false'}})
        >>> print(calc_chain('(x0 + 1)^2', [('=', 'x0^2 + 2*x0 + 1', tactic('ring'))], prob))
        calc x ^ 2 + 2 * x + 1
            _ = x ^ 2 + 2 * x + 1 := by ring
    """
    if not steps:
        message = 'a calc chain needs at least one step'
        raise VeriphyError(message)
    lines = [f'calc {_as_term(start, problem)}']
    for relation, expression, proof in steps:
        symbol = RELATIONS_TEXT.get(relation, relation)
        tac = _expect(proof, 'tactic', 'a calc step proof')
        lines.append(f'    _ {symbol} {_as_term(expression, problem)} := by {tac}')
    return tactic('\n'.join(lines))

canonical_commutator(left, right, hbar=None)

Return [A, B] as a multiple of the identity, for combinations of one coordinate's x and p.

With [x, x] = [p, p] = 0 and [x, p] = iℏ, [αₓ x + αₚ p, βₓ x + βₚ p] = iℏ (αₓ βₚ - αₚ βₓ).

Parameters:

Name Type Description Default
left dict[str, object]

The coefficients of 'x' and 'p' in A (missing ones zero).

required
right dict[str, object]

Those of B.

required
hbar object

The reduced Planck constant; the symbol hbar when None.

None

Returns:

Type Description
object

The scalar, simplified.

Examples:

>>> lowering, raising = ladder_operators()
>>> canonical_commutator(lowering, raising)
1
>>> canonical_commutator(lowering, {'x': 1})
1/2*sqrt(2)*xi
Source code in src/veriphy/toolkit/_operators.py
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
def canonical_commutator(left: dict[str, object], right: dict[str, object], hbar: object = None) -> object:
    """Return `[A, B]` as a multiple of the identity, for combinations of one coordinate's `x` and `p`.

    With `[x, x] = [p, p] = 0` and `[x, p] = iℏ`, `[αₓ x + αₚ p, βₓ x + βₚ p] = iℏ (αₓ βₚ - αₚ βₓ)`.

    Args:
        left: The coefficients of `'x'` and `'p'` in `A` (missing ones zero).
        right: Those of `B`.
        hbar: The reduced Planck constant; the symbol `hbar` when `None`.

    Returns:
        The scalar, simplified.

    Examples:
        >>> lowering, raising = ladder_operators()
        >>> canonical_commutator(lowering, raising)
        1
        >>> canonical_commutator(lowering, {'x': 1})
        1/2*sqrt(2)*xi
    """
    hbar = var('hbar') if hbar is None else hbar
    alpha_x, alpha_p = SR(left.get('x', 0)), SR(left.get('p', 0))
    beta_x, beta_p = SR(right.get('x', 0)), SR(right.get('p', 0))
    return (SR.I() * hbar * (alpha_x * beta_p - alpha_p * beta_x)).simplify_full()

cartan_matrix(cartan_type)

Return the Cartan matrix of a type in mathlib's convention (CartanMatrix.E 8, CartanMatrix.F₄, …).

Parameters:

Name Type Description Default
cartan_type str

The type, as 'E8', 'F4' or 'B3' (anything else is refused with a CertificateError).

required

Returns:

Type Description
Matrix

The integer matrix.

Examples:

>>> cartan_matrix('F4')
[ 2 -1  0  0]
[-1  2 -2  0]
[ 0 -1  2 -1]
[ 0  0 -1  2]
Source code in src/veriphy/toolkit/_lie.py
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
def cartan_matrix(cartan_type: str) -> Matrix:
    """Return the Cartan matrix of a type in mathlib's convention (`CartanMatrix.E 8`, `CartanMatrix.F₄`, …).

    Args:
        cartan_type: The type, as `'E8'`, `'F4'` or `'B3'` (anything else is refused with a `CertificateError`).

    Returns:
        The integer matrix.

    Examples:
        >>> cartan_matrix('F4')
        [ 2 -1  0  0]
        [-1  2 -2  0]
        [ 0 -1  2 -1]
        [ 0  0 -1  2]
    """
    letter, rank = _cartan_type(cartan_type)
    m = matrix(ZZ, CartanMatrix([letter, rank]))
    return m.transpose() if letter in TRANSPOSED_TYPES else m

casimir(basis, form=None)

Return the quadratic Casimir Σᵢⱼ (B⁻¹)ᵢⱼ eᵢ eⱼ of the representation, for an invariant form B.

Parameters:

Name Type Description Default
basis Sequence[Matrix]

The representation matrices, a basis of the Lie algebra.

required
form Matrix | None

The invariant form's matrix in that basis; the Killing form when None.

None

Returns:

Type Description
Matrix

The Casimir matrix.

Raises:

Type Description
CertificateError

If the form is degenerate (the Killing form of an algebra that is not semisimple).

Examples:

>>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
>>> casimir([e, f, h])
[3/8   0]
[  0 3/8]
Source code in src/veriphy/toolkit/_lie.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def casimir(basis: Sequence[Matrix], form: Matrix | None = None) -> Matrix:
    """Return the quadratic Casimir `Σᵢⱼ (B⁻¹)ᵢⱼ eᵢ eⱼ` of the representation, for an invariant form `B`.

    Args:
        basis: The representation matrices, a basis of the Lie algebra.
        form: The invariant form's matrix in that basis; the Killing form when `None`.

    Returns:
        The Casimir matrix.

    Raises:
        CertificateError: If the form is degenerate (the Killing form of an algebra that is not semisimple).

    Examples:
        >>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
        >>> casimir([e, f, h])
        [3/8   0]
        [  0 3/8]
    """
    b = killing_form(structure_constants(basis)) if form is None else form
    if b.det() == 0:
        message = 'the invariant form is degenerate; pass a nondegenerate one (the algebra may not be semisimple)'
        raise CertificateError(message)
    inverse = b.inverse()
    n = len(basis)
    return sum((inverse[i, j] * basis[i] * basis[j] for i in range(n) for j in range(n)), 0 * basis[0])

change_of_basis_facts(p, rho, blocks)

Return facts ρ(x) * P = P * B(x) (block diagonal B) and P * P⁻¹ = 1.

Parameters:

Name Type Description Default
p Matrix

The change of basis.

required
rho Rep

The representation.

required
blocks Rep

The block-diagonal matrices.

required

Returns:

Type Description
Lean

The facts hb_k and hinv.

Source code in src/veriphy/toolkit/_representation.py
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
def change_of_basis_facts(p: Matrix, rho: Rep, blocks: Rep) -> Lean:
    """Return facts `ρ(x) * P = P * B(x)` (block diagonal `B`) and `P * P⁻¹ = 1`.

    Args:
        p: The change of basis.
        rho: The representation.
        blocks: The block-diagonal matrices.

    Returns:
        The facts `hb_k` and `hinv`.
    """
    term = rational_matrix_term(p)
    lines = [
        f'have hb_{k} : {rational_matrix_term(x)} * {term} = {term} * {rational_matrix_term(b)} := {PRODUCTS}'
        for k, (x, b) in enumerate(zip(rho, blocks, strict=True))
    ]
    lines.append(
        f'have hinv : {term} * {rational_matrix_term(p.inverse())} = 1 := {PRODUCT}',
    )
    return tactic('\n'.join(lines))

characteristic_polynomial(m)

Return det(x I - M) of a rational matrix, whose roots are its eigenvalues.

Parameters:

Name Type Description Default
m Matrix

The matrix.

required

Returns:

Type Description
Polynomial

The polynomial in x.

Source code in src/veriphy/toolkit/_numerics.py
242
243
244
245
246
247
248
249
250
251
def characteristic_polynomial(m: Matrix) -> Polynomial:
    """Return `det(x I - M)` of a rational matrix, whose roots are its eigenvalues.

    Args:
        m: The matrix.

    Returns:
        The polynomial in `x`.
    """
    return matrix(QQ, m).charpoly('x')

check_matrix_identity(lhs, rhs)

Check in Sage that two matrices over a field agree, before Lean does.

Parameters:

Name Type Description Default
lhs Matrix

The left side.

required
rhs Matrix

The right side.

required

Raises:

Type Description
CertificateError

If they differ, showing the difference.

Source code in src/veriphy/toolkit/_number_field.py
311
312
313
314
315
316
317
318
319
320
321
322
323
def check_matrix_identity(lhs: Matrix, rhs: Matrix) -> None:
    """Check in Sage that two matrices over a field agree, before Lean does.

    Args:
        lhs: The left side.
        rhs: The right side.

    Raises:
        CertificateError: If they differ, showing the difference.
    """
    if lhs != rhs:
        message = f'the identity fails in Sage; the difference is\n{lhs - rhs}'
        raise CertificateError(message)

christoffel_proof(g, *, metric, unfold=())

Return a proof of a goal christoffel G k i j x = Γ for an explicit metric G at a point.

A constant metric's symbols vanish: the derivatives of its components are zero. Otherwise the proof states the inverse metric, rewrites with it, expands the partial derivatives and closes by field_simp and ring; the hypotheses that the inverse needs (a component nonzero) must be in the context.

Parameters:

Name Type Description Default
g Matrix

The metric's matrix of expressions in the coordinates.

required
metric str

The Lean term of the metric as the goal writes it (a definition name or metric_term(g)).

required
unfold Sequence[str]

Definitions to unfold (the metric's name).

()

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the metric is degenerate.

Source code in src/veriphy/toolkit/_geometry.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def christoffel_proof(g: Matrix, *, metric: str, unfold: Sequence[str] = ()) -> Lean:
    """Return a proof of a goal `christoffel G k i j x = Γ` for an explicit metric `G` at a point.

    A constant metric's symbols vanish: the derivatives of its components are zero. Otherwise the proof states the
    inverse metric, rewrites with it, expands the partial derivatives and closes by `field_simp` and `ring`; the
    hypotheses that the inverse needs (a component nonzero) must be in the context.

    Args:
        g: The metric's matrix of expressions in the coordinates.
        metric: The Lean term of the metric as the goal writes it (a definition name or `metric_term(g)`).
        unfold: Definitions to unfold (the metric's name).

    Returns:
        The tactic.

    Raises:
        CertificateError: If the metric is degenerate.
    """
    n = g.nrows()
    if all(SR(e).is_constant() for e in g.list()):
        names = ', '.join(['CoordinateGeometry.christoffel', 'CoordinateGeometry.pd', *unfold])
        return tactic(f'simp [{names}]')
    if g.det().simplify_full() == 0:
        message = 'the metric is degenerate (its determinant vanishes)'
        raise CertificateError(message)
    inverse = matrix(SR, g).inverse().simplify_full()
    inverse_text = '!![' + '; '.join(', '.join(_render(e, n) for e in row) for row in inverse.rows()) + ']'
    names = ', '.join([*unfold, 'CoordinateGeometry.metricAt', 'Matrix.mul_apply', 'Fin.sum_univ_succ'])
    lines = [
        f'have hinv : CoordinateGeometry.invMetric {metric} x = {inverse_text} := by',
        '  refine Matrix.inv_eq_right_inv ?_',
        f'  ext a b\n  fin_cases a <;> fin_cases b <;> simp [{names}] <;> field_simp',
        'rw [CoordinateGeometry.christoffel, hinv]',
        f'simp (disch := fun_prop) [CoordinateGeometry.pd, {", ".join([*unfold, DERIVATIVE_SIMP])}]',
        'all_goals first | ring1 | (field_simp; ring1) | (field_simp; done)',
    ]
    return tactic('\n'.join(lines))

christoffel_symbols(g)

Return the nonzero Christoffel symbols Γᵏᵢⱼ of a metric, computed by SageManifolds.

Parameters:

Name Type Description Default
g Matrix

The metric's matrix of expressions in the coordinates x0, ….

required

Returns:

Type Description
dict[tuple[int, int, int], object]

The symbols by (k, i, j), simplified.

Source code in src/veriphy/toolkit/_geometry.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def christoffel_symbols(g: Matrix) -> dict[tuple[int, int, int], object]:
    """Return the nonzero Christoffel symbols `Γᵏᵢⱼ` of a metric, computed by SageManifolds.

    Args:
        g: The metric's matrix of expressions in the coordinates `x0, …`.

    Returns:
        The symbols by `(k, i, j)`, simplified.
    """
    n = g.nrows()
    manifold = Manifold(n, 'M', structure='pseudo-Riemannian', signature=None)
    chart = manifold.chart(' '.join(f'x{i}' for i in range(n)))
    metric = manifold.metric('g')
    for i in range(n):
        for j in range(n):
            metric[i, j] = SR(g[i, j])
    gamma = metric.christoffel_symbols(chart)
    symbols = {}
    for k in range(n):
        for i in range(n):
            for j in range(n):
                value = gamma[k, i, j].expr().simplify_full()
                if value != 0:
                    symbols[(k, i, j)] = value
    return symbols

clebsch_gordan(two_j1, two_j2)

Return the Clebsch-Gordan change of basis T : Vⱼ₁ ⊗ Vⱼ₂ → ⊕ⱼ Vⱼ, in the Chevalley normalization.

The rows of each block J (from j1 + j2 down to |j1 - j2|) are the coefficients of the basis vectors fᵏ v_J / k! of V_J on the product basis, so T intertwines the product representation with the direct sum of spin representations.

Parameters:

Name Type Description Default
two_j1 int

Twice the first spin.

required
two_j2 int

Twice the second spin.

required

Returns:

Type Description
tuple[Matrix, list[int]]

T and the doubled spins of the blocks, in order.

Raises:

Type Description
CertificateError

If the matrix built does not intertwine (a defect in its construction, checked in Sage).

Examples:

>>> t, spins = clebsch_gordan(2, 1)
>>> spins, t.dimensions()
([3, 1], (6, 6))
Source code in src/veriphy/toolkit/_representation.py
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
def clebsch_gordan(two_j1: int, two_j2: int) -> tuple[Matrix, list[int]]:
    """Return the Clebsch-Gordan change of basis `T : Vⱼ₁ ⊗ Vⱼ₂ → ⊕ⱼ Vⱼ`, in the Chevalley normalization.

    The rows of each block `J` (from `j1 + j2` down to `|j1 - j2|`) are the coefficients of the basis vectors
    `fᵏ v_J / k!` of `V_J` on the product basis, so `T` intertwines the product representation with the direct sum
    of spin representations.

    Args:
        two_j1: Twice the first spin.
        two_j2: Twice the second spin.

    Returns:
        `T` and the doubled spins of the blocks, in order.

    Raises:
        CertificateError: If the matrix built does not intertwine (a defect in its construction, checked in Sage).

    Examples:
        >>> t, spins = clebsch_gordan(2, 1)
        >>> spins, t.dimensions()
        ([3, 1], (6, 6))
    """
    product = tensor_representation(spin_representation(two_j1), spin_representation(two_j2))
    spins = list(range(two_j1 + two_j2, abs(two_j1 - two_j2) - 1, -2))
    target = [block_diagonal_matrix([spin_representation(s)[k] for s in spins]) for k in range(3)]
    # T⁻¹ maps the direct sum into the product: its columns are the vectors fᵏ v_J / k! of each highest weight v_J
    columns = []
    e, f, h = product
    for s in spins:
        kernel = e.right_kernel().intersection((h - s * identity_matrix(QQ, h.nrows())).right_kernel())
        v = kernel.basis()[0]
        w = v
        for k in range(s + 1):
            columns.append(w)
            w = f * w / (k + 1)
    inverse = matrix(QQ, columns).transpose()
    t = inverse.inverse()
    for a, b in zip(product, target, strict=True):
        if t * a != b * t:
            message = 'the Clebsch-Gordan matrix does not intertwine'
            raise CertificateError(message)
    return t, spins

clifford_facts(gammas, eta)

Return kernel-checked facts: the Clifford relation, four-gamma traces and two contractions.

The contractions are γ^μ γ_μ = 4 and γ^μ γ^ν γ_μ = -2 γ^ν.

Parameters:

Name Type Description Default
gammas Sequence[Matrix]

The gamma matrices (Sage checks the Clifford relation first).

required
eta Matrix

The metric.

required

Returns:

Type Description
Lean

The facts hclifford, htrace4, hcontract, hcontract3.

Raises:

Type Description
CertificateError

If the Clifford relation fails.

Source code in src/veriphy/toolkit/_clifford.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def clifford_facts(gammas: Sequence[Matrix], eta: Matrix) -> Lean:
    """Return kernel-checked facts: the Clifford relation, four-gamma traces and two contractions.

    The contractions are `γ^μ γ_μ = 4` and `γ^μ γ^ν γ_μ = -2 γ^ν`.

    Args:
        gammas: The gamma matrices (Sage checks the Clifford relation first).
        eta: The metric.

    Returns:
        The facts `hclifford`, `htrace4`, `hcontract`, `hcontract3`.

    Raises:
        CertificateError: If the Clifford relation fails.
    """
    failures = clifford_violations(gammas, eta)
    if failures:
        message = f'the Clifford relation fails at (μ, ν) = {failures[0]}'
        raise CertificateError(message)
    binders = '(G : Fin 4 → Matrix (Fin 4) (Fin 4) GaussianInt) (η : Fin 4 → Fin 4 → GaussianInt)'
    args = [_family(gammas), _metric(eta)]
    lowered = '∑ μ, η μ μ • (G μ * G μ)'
    facts = [
        _fact('hclifford', binders, '∀ μ ν : Fin 4, G μ * G ν + G ν * G μ = (2 * η μ ν) • 1', args),
        _fact(
            'htrace4',
            binders,
            '∀ μ ν ρ σ : Fin 4, Matrix.trace (G μ * G ν * G ρ * G σ) = '
            '4 * (η μ ν * η ρ σ - η μ ρ * η ν σ + η μ σ * η ν ρ)',
            args,
        ),
        _fact('hcontract', binders, f'{lowered} = (4 : GaussianInt) • 1', args),
        _fact('hcontract3', binders, '∀ ν : Fin 4, ∑ μ, η μ μ • (G μ * G ν * G μ) = (-2 : GaussianInt) • G ν', args),
    ]
    return tactic('\n'.join(facts))

clifford_violations(gammas, eta)

Return the index pairs where {γ^μ, γ^ν} = 2 η^{μν} fails.

Parameters:

Name Type Description Default
gammas Sequence[Matrix]

The gamma matrices.

required
eta Matrix

The metric.

required

Returns:

Type Description
list[tuple[int, int]]

The failing pairs.

Source code in src/veriphy/toolkit/_clifford.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def clifford_violations(gammas: Sequence[Matrix], eta: Matrix) -> list[tuple[int, int]]:
    """Return the index pairs where `{γ^μ, γ^ν} = 2 η^{μν}` fails.

    Args:
        gammas: The gamma matrices.
        eta: The metric.

    Returns:
        The failing pairs.
    """
    one = identity_matrix(FIELD, gammas[0].nrows())
    return [
        (m, n)
        for m, n in itertools.product(range(len(gammas)), repeat=2)
        if gammas[m] * gammas[n] + gammas[n] * gammas[m] != 2 * eta[m, n] * one
    ]

coefficients(x, which)

Return a value's rational coefficients on the power basis 1, ζ, … of its field, trailing zeros dropped.

Parameters:

Name Type Description Default
x FieldElement | int

An element of ℚ, ℚ(√5) (ζ = √5) or ℚ(ζ₁₆).

required
which str

'rational', 'sqrt5' or 'zeta16'.

required

Returns:

Type Description
list[FieldElement]

The coefficients; empty for zero.

Examples:

>>> coefficients(SQRT5.gen() / 2 - 1, 'sqrt5')
[-1, 1/2]
>>> coefficients(ZETA16.gen() ** 3, 'zeta16')
[0, 0, 0, 1]
Source code in src/veriphy/toolkit/_fusion.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
def coefficients(x: FieldElement | int, which: str) -> list[FieldElement]:
    """Return a value's rational coefficients on the power basis `1, ζ, …` of its field, trailing zeros dropped.

    Args:
        x: An element of `ℚ`, `ℚ(√5)` (`ζ = √5`) or `ℚ(ζ₁₆)`.
        which: `'rational'`, `'sqrt5'` or `'zeta16'`.

    Returns:
        The coefficients; empty for zero.

    Examples:
        >>> coefficients(SQRT5.gen() / 2 - 1, 'sqrt5')
        [-1, 1/2]
        >>> coefficients(ZETA16.gen() ** 3, 'zeta16')
        [0, 0, 0, 1]
    """
    if which == 'rational':
        values = [QQ(x)]
    elif which == 'sqrt5':
        values = list(SQRT5(x))
    else:
        values = list(ZETA16(x).polynomial().padded_list(8)) if ZETA16(x) != 0 else []
    while values and values[-1] == 0:
        values.pop()
    return values

combination(weighted, problem)

Build the weighted sum w₁ * p₁ + … + wₖ * pₖ of proof terms, dropping zero weights.

Parameters:

Name Type Description Default
weighted Sequence[tuple[object, Lean]]

Pairs of a weight (a rational or a polynomial of the problem) and a proof term.

required
problem Problem

The problem whose atoms the weights are written over.

required

Returns:

Type Description
Lean

The sum, a term.

Raises:

Type Description
CertificateError

If every weight is zero.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'goal': {'kind': 'false'}})
>>> str(combination([(1, term('h1')), (0, term('h2')), (QQ(-1) / 2, term('h3')), ('x0', term('h4'))], prob))
'h1 + (-1 / 2) * h3 + (a) * h4'
Source code in src/veriphy/toolkit/_emit.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def combination(weighted: Sequence[tuple[object, Lean]], problem: Problem) -> Lean:
    """Build the weighted sum `w₁ * p₁ + … + wₖ * pₖ` of proof terms, dropping zero weights.

    Args:
        weighted: Pairs of a weight (a rational or a polynomial of the problem) and a proof term.
        problem: The problem whose atoms the weights are written over.

    Returns:
        The sum, a term.

    Raises:
        CertificateError: If every weight is zero.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'goal': {'kind': 'false'}})
        >>> str(combination([(1, term('h1')), (0, term('h2')), (QQ(-1) / 2, term('h3')), ('x0', term('h4'))], prob))
        'h1 + (-1 / 2) * h3 + (a) * h4'
    """
    pieces = []
    for weight, proof in weighted:
        _expect(proof, 'term', 'a combined proof')
        coefficient = polynomial(weight, problem)
        if coefficient.text == '0':
            continue
        pieces.append(proof.text if coefficient.text == '1' else f'({coefficient}) * {proof}')
    if not pieces:
        message = 'empty combination (every weight is zero)'
        raise CertificateError(message)
    return term(' + '.join(pieces))

commutation_facts(element, names, *, size, carrier='ℚ', definitions=True, entrywise_limit=0)

Return have facts C * eᵢ = eᵢ * C: the element (a Casimir) commutes with each basis matrix.

Parameters:

Name Type Description Default
element str

The Lean term of the commuting matrix (a definition, or a literal).

required
names Sequence[str]

The Lean terms of the basis matrices.

required
size int

The matrices' dimension.

required
carrier str

The Lean type of the entries.

'ℚ'
definitions bool

Whether the terms are definitions an entrywise proof must unfold.

True
entrywise_limit int

Prove entrywise up to this dimension even over ℤ or ℚ.

0

Returns:

Type Description
Lean

The facts, named hc_i.

Examples:

>>> print(commutation_facts('C', ['e'], size=2))
have hc_0 : C * e = e * C := VeriphyCheck.commute_of_check (by decide +kernel)
Source code in src/veriphy/toolkit/_lie.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
def commutation_facts(
    element: str,
    names: Sequence[str],
    *,
    size: int,
    carrier: str = 'ℚ',
    definitions: bool = True,
    entrywise_limit: int = 0,
) -> Lean:
    """Return `have` facts `C * eᵢ = eᵢ * C`: the element (a Casimir) commutes with each basis matrix.

    Args:
        element: The Lean term of the commuting matrix (a definition, or a literal).
        names: The Lean terms of the basis matrices.
        size: The matrices' dimension.
        carrier: The Lean type of the entries.
        definitions: Whether the terms are definitions an entrywise proof must unfold.
        entrywise_limit: Prove entrywise up to this dimension even over ℤ or ℚ.

    Returns:
        The facts, named `hc_i`.

    Examples:
        >>> print(commutation_facts('C', ['e'], size=2))
        have hc_0 : C * e = e * C := VeriphyCheck.commute_of_check (by decide +kernel)
    """
    lines = []
    for i, name in enumerate(names):
        unfold = list(dict.fromkeys([element, name])) if definitions else []
        proof = _identity_proof(
            'VeriphyCheck.commute_of_check',
            bracket=False,
            carrier=carrier,
            unfold=unfold,
            size=size,
            limit=entrywise_limit,
        )
        lines.append(f'have hc_{i} : {element} * {name} = {name} * {element} := {proof}')
    return tactic('\n'.join(lines))

commutator(a, b)

Return [a, b] = a b - b a.

Parameters:

Name Type Description Default
a Operator

An operator.

required
b Operator

Another.

required

Returns:

Type Description
Operator

The commutator.

Source code in src/veriphy/toolkit/_operators.py
141
142
143
144
145
146
147
148
149
150
151
def commutator(a: Operator, b: Operator) -> Operator:
    """Return `[a, b] = a b - b a`.

    Args:
        a: An operator.
        b: Another.

    Returns:
        The commutator.
    """
    return a * b - b * a

complex_matrix_term(m)

Return a matrix over ℚ(i) as a Lean !![…] literal over ℂ.

Parameters:

Name Type Description Default
m Matrix

The matrix, with entries in QuadraticField(-1) (or rationals).

required

Returns:

Type Description
str

The literal.

Examples:

>>> i = QuadraticField(-1, 'i').gen()
>>> complex_matrix_term(matrix([[0, -i], [i, 0]]))
'(!![0, (-1 * Complex.I); (Complex.I), 0] : Matrix (Fin 2) (Fin 2) ℂ)'
Source code in src/veriphy/toolkit/_operators.py
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def complex_matrix_term(m: Matrix) -> str:
    """Return a matrix over ℚ(i) as a Lean `!![…]` literal over ℂ.

    Args:
        m: The matrix, with entries in `QuadraticField(-1)` (or rationals).

    Returns:
        The literal.

    Examples:
        >>> i = QuadraticField(-1, 'i').gen()
        >>> complex_matrix_term(matrix([[0, -i], [i, 0]]))
        '(!![0, (-1 * Complex.I); (Complex.I), 0] : Matrix (Fin 2) (Fin 2) ℂ)'
    """
    rows = '; '.join(', '.join(_complex(x) for x in row) for row in m.rows())
    return f'(!![{rows}] : Matrix (Fin {m.nrows()}) (Fin {m.ncols()}) ℂ)'

contraction_proof(names)

Return the proof of a contraction of explicit component arrays: expand the sums and close by ring.

Parameters:

Name Type Description Default
names Sequence[str]

The let-bound arrays of the goal to introduce and unfold.

required

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_geometry.py
232
233
234
235
236
237
238
239
240
241
def contraction_proof(names: Sequence[str]) -> Lean:
    """Return the proof of a contraction of explicit component arrays: expand the sums and close by `ring`.

    Args:
        names: The `let`-bound arrays of the goal to introduce and unfold.

    Returns:
        The tactic.
    """
    return tactic(f'intro {" ".join(names)}\nsimp [{", ".join([*names, "Fin.sum_univ_succ"])}]\nring')

contragredient_superalgebra(even_ops, factors, omega, coefficients=None)

Return the structure constants and parities of g₀ ⊕ V with the moment-map odd bracket.

Parameters:

Name Type Description Default
even_ops Sequence[Matrix]

A basis of g₀, as matrices acting faithfully on V.

required
factors Sequence[int]

The simple factor of each basis element.

required
omega Matrix

An invariant antisymmetric form on V.

required
coefficients Sequence[RingElement] | None

The moment-map coefficients kₛ; Sage's unique solution when None.

None

Returns:

Type Description
tuple[Table, list[int]]

The table (even basis first, then the basis of V) and the parities.

Raises:

Type Description
CertificateError

If the coefficients are not unique (a family; pass them), or no bracket exists.

Source code in src/veriphy/toolkit/_superalgebra.py
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
def contragredient_superalgebra(
    even_ops: Sequence[Matrix],
    factors: Sequence[int],
    omega: Matrix,
    coefficients: Sequence[RingElement] | None = None,
) -> tuple[Table, list[int]]:
    """Return the structure constants and parities of `g₀ ⊕ V` with the moment-map odd bracket.

    Args:
        even_ops: A basis of `g₀`, as matrices acting faithfully on `V`.
        factors: The simple factor of each basis element.
        omega: An invariant antisymmetric form on `V`.
        coefficients: The moment-map coefficients `kₛ`; Sage's unique solution when `None`.

    Returns:
        The table (even basis first, then the basis of `V`) and the parities.

    Raises:
        CertificateError: If the coefficients are not unique (a family; pass them), or no bracket exists.
    """
    if coefficients is None:
        solutions = moment_coefficients(even_ops, factors, omega)
        if len(solutions) != 1:
            message = f'{len(solutions)} independent moment-map coefficients satisfy super-Jacobi; pass them'
            raise CertificateError(message)
        coefficients = solutions[0]
    table = _assemble(even_ops, _moment_maps(even_ops, factors, omega), coefficients)
    return table, [0] * len(even_ops) + [1] * omega.nrows()

coordinates(n)

Return the Sage coordinates x0, … of an n-dimensional chart.

Parameters:

Name Type Description Default
n int

The dimension.

required

Returns:

Type Description
list[object]

The symbols.

Source code in src/veriphy/toolkit/_geometry.py
25
26
27
28
29
30
31
32
33
34
def coordinates(n: int) -> list[object]:
    """Return the Sage coordinates `x0, …` of an `n`-dimensional chart.

    Args:
        n: The dimension.

    Returns:
        The symbols.
    """
    return [var(f'x{i}') for i in range(n)]

create_ring(names)

Create the multivariate polynomial ring QQ[names], multivariate even for one name.

Parameters:

Name Type Description Default
names list[str]

The variable names, valid identifiers.

required

Returns:

Type Description
MPolynomialRing_base

The polynomial ring.

Examples:

>>> create_ring(['x0', 'x1']).gens()
(x0, x1)
Source code in src/veriphy/toolkit/_problem.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def create_ring(names: list[str]) -> MPolynomialRing_base:
    """Create the multivariate polynomial ring QQ[names], multivariate even for one name.

    Args:
        names: The variable names, valid identifiers.

    Returns:
        The polynomial ring.

    Examples:
        >>> create_ring(['x0', 'x1']).gens()
        (x0, x1)
    """
    return PolynomialRing(QQ, len(names), names)

cyclic_submodule(rho, v)

Return a basis (as columns) of the submodule the representation generates from v.

From a highest weight vector this is the irreducible module of that weight, inside the representation.

Parameters:

Name Type Description Default
rho Rep

The representation's matrices.

required
v Sequence[object]

The generating vector.

required

Returns:

Type Description
Matrix

The basis, as the columns of a matrix.

Source code in src/veriphy/toolkit/_representation.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
def cyclic_submodule(rho: Rep, v: Sequence[object]) -> Matrix:
    """Return a basis (as columns) of the submodule the representation generates from `v`.

    From a highest weight vector this is the irreducible module of that weight, inside the representation.

    Args:
        rho: The representation's matrices.
        v: The generating vector.

    Returns:
        The basis, as the columns of a matrix.
    """
    space = matrix(QQ, [vector(QQ, v)]).row_space()
    frontier = [vector(QQ, v)]
    while frontier:
        w = frontier.pop()
        for x in rho:
            image = x * w
            if image not in space:
                space = space + matrix(QQ, [image]).row_space()
                frontier.append(image)
    return space.basis_matrix().transpose()

cyclotomic_context(n, zeta='ζ', primitive='hζ', name='hΦ')

Return ℚ(ζₙ) for a primitive root ζ with primitive : IsPrimitiveRoot ζ n among the hypotheses.

The fact Φₙ(ζ) = 0 is derived from mathlib's IsPrimitiveRoot.isRoot_cyclotomic and, for a prime power n, the explicit Polynomial.cyclotomic_prime_pow_eq_geom_sum. For other n, state Φₙ(ζ) = 0 as a hypothesis named name and pass primitive=None.

Parameters:

Name Type Description Default
n int

The order of the root.

required
zeta str

The root's Lean text.

'ζ'
primitive str | None

The name of the hypothesis IsPrimitiveRoot ζ n, or None when the goal states Φₙ(ζ) = 0.

'hζ'
name str

The name of the fact Φₙ(ζ) = 0.

'hΦ'

Returns:

Type Description
FieldContext

The context.

Raises:

Type Description
CertificateError

If n is not a prime power and no hypothesis states the relation.

Examples:

>>> print(cyclotomic_context(16).modulus)
x^8 + 1
Source code in src/veriphy/toolkit/_number_field.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def cyclotomic_context(n: int, zeta: str = 'ζ', primitive: str | None = 'hζ', name: str = 'hΦ') -> FieldContext:
    """Return ℚ(ζₙ) for a primitive root `ζ` with `primitive : IsPrimitiveRoot ζ n` among the hypotheses.

    The fact `Φₙ(ζ) = 0` is derived from mathlib's `IsPrimitiveRoot.isRoot_cyclotomic` and, for a prime power `n`,
    the explicit `Polynomial.cyclotomic_prime_pow_eq_geom_sum`. For other `n`, state `Φₙ(ζ) = 0` as a hypothesis
    named `name` and pass `primitive=None`.

    Args:
        n: The order of the root.
        zeta: The root's Lean text.
        primitive: The name of the hypothesis `IsPrimitiveRoot ζ n`, or `None` when the goal states `Φₙ(ζ) = 0`.
        name: The name of the fact `Φₙ(ζ) = 0`.

    Returns:
        The context.

    Raises:
        CertificateError: If `n` is not a prime power and no hypothesis states the relation.

    Examples:
        >>> print(cyclotomic_context(16).modulus)
        x^8 + 1
    """
    modulus = PolynomialRing(QQ, 'x').cyclotomic_polynomial(n)
    if primitive is None:
        return FieldContext(zeta, modulus, name, None)
    factored = factor(n)
    if len(factored) != 1:
        message = f'Φ_{n}(ζ) = 0 follows from IsPrimitiveRoot only for prime powers; state it as a hypothesis {name}'
        raise CertificateError(message)
    p, k = factored[0]
    setup = '\n'.join(
        [
            f'have {name} : {render_polynomial(modulus, zeta)} = 0 := by',
            f'  have h := {primitive}.isRoot_cyclotomic (by norm_num)',
            f'  rw [show ({n} : ℕ) = {p} ^ ({k - 1} + 1) by norm_num,',
            '    Polynomial.cyclotomic_prime_pow_eq_geom_sum (by decide)] at h',
            '  simp only [Polynomial.IsRoot, Polynomial.eval_finsetSum, Polynomial.eval_pow, Polynomial.eval_X,',
            '    Polynomial.eval_add, Polynomial.eval_one, Polynomial.eval_zero, Finset.sum_range_succ,',
            '    Finset.sum_range_zero, pow_zero, pow_one] at h',
            '  linear_combination h',
        ],
    )
    return FieldContext(zeta, modulus, name, tactic(setup))

d21_alpha(alpha)

Return D(2,1;α): sl2³ on 2 ⊗ 2 ⊗ 2, with moment-map coefficients (1, α, -1 - α).

Parameters:

Name Type Description Default
alpha RingElement

The parameter (rational, or an element of a field of rational functions).

required

Returns:

Type Description
tuple[Table, list[int]]

The structure constants (17-dimensional, 9 even and 8 odd) and the parities.

Examples:

>>> table, parities = d21_alpha(QQ(2))
>>> len(table), sum(parities), super_jacobi_violations(table, parities)
(17, 8, [])
Source code in src/veriphy/toolkit/_superalgebra.py
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
def d21_alpha(alpha: RingElement) -> tuple[Table, list[int]]:
    """Return `D(2,1;α)`: `sl2³` on `2 ⊗ 2 ⊗ 2`, with moment-map coefficients `(1, α, -1 - α)`.

    Args:
        alpha: The parameter (rational, or an element of a field of rational functions).

    Returns:
        The structure constants (17-dimensional, 9 even and 8 odd) and the parities.

    Examples:
        >>> table, parities = d21_alpha(QQ(2))
        >>> len(table), sum(parities), super_jacobi_violations(table, parities)
        (17, 8, [])
    """
    one = identity_matrix(QQ, 2)
    even = [_kron(s, one, one) for s in SL2] + [_kron(one, s, one) for s in SL2] + [_kron(one, one, s) for s in SL2]
    factors = [0] * 3 + [1] * 3 + [2] * 3
    omega = _kron(EPSILON, EPSILON, EPSILON)
    moments = _moment_maps(even, factors, omega)
    table = _assemble(even, moments, [1, alpha, -1 - alpha])
    return table, [0] * 9 + [1] * 8

decide()

Call decide, for decidable goals over finite domains.

Returns:

Type Description
Lean

The tactic.

Examples:

>>> str(decide())
'decide'
Source code in src/veriphy/toolkit/_emit.py
259
260
261
262
263
264
265
266
267
268
269
def decide() -> Lean:
    """Call `decide`, for decidable goals over finite domains.

    Returns:
        The tactic.

    Examples:
        >>> str(decide())
        'decide'
    """
    return tactic('decide')

decomposition(rho, summands)

Return the change of basis to a direct sum of invariant subspaces, and the block-diagonal matrices.

Parameters:

Name Type Description Default
rho Rep

The representation.

required
summands Sequence[Matrix]

Bases (as columns) of invariant subspaces whose sum is the whole space.

required

Returns:

Type Description
tuple[Matrix, Rep]

P (the summands' bases side by side) and the matrices P⁻¹ ρ(x) P, block diagonal.

Raises:

Type Description
CertificateError

If the subspaces are not invariant or do not form a direct sum of the whole space.

Source code in src/veriphy/toolkit/_representation.py
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
def decomposition(rho: Rep, summands: Sequence[Matrix]) -> tuple[Matrix, Rep]:
    """Return the change of basis to a direct sum of invariant subspaces, and the block-diagonal matrices.

    Args:
        rho: The representation.
        summands: Bases (as columns) of invariant subspaces whose sum is the whole space.

    Returns:
        `P` (the summands' bases side by side) and the matrices `P⁻¹ ρ(x) P`, block diagonal.

    Raises:
        CertificateError: If the subspaces are not invariant or do not form a direct sum of the whole space.
    """
    p = _augment(summands)
    if not p.is_square() or p.det() == 0:
        message = 'the summands do not form a direct sum of the whole space'
        raise CertificateError(message)
    blocks = [block_diagonal_matrix([restrict([x], s)[0] for s in summands]) for x in rho]
    for x, b in zip(rho, blocks, strict=True):
        if x * p != p * b:
            message = 'the change of basis does not block-diagonalize the representation'
            raise CertificateError(message)
    return p, blocks

defining_representation(cartan_type)

Return Sage's defining representation of a simple Lie algebra: its Chevalley generators eᵢ, fᵢ, hᵢ.

Parameters:

Name Type Description Default
cartan_type str

The type, as 'A2', 'G2' or 'F4'.

required

Returns:

Type Description
tuple[Rep, list[str]]

The generator matrices and their labels.

Examples:

>>> rho, labels = defining_representation('A2')
>>> labels, rho[0].nrows()
(['e1', 'e2', 'f1', 'f2', 'h1', 'h2'], 3)
Source code in src/veriphy/toolkit/_representation.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def defining_representation(cartan_type: str) -> tuple[Rep, list[str]]:
    """Return Sage's defining representation of a simple Lie algebra: its Chevalley generators `eᵢ`, `fᵢ`, `hᵢ`.

    Args:
        cartan_type: The type, as `'A2'`, `'G2'` or `'F4'`.

    Returns:
        The generator matrices and their labels.

    Examples:
        >>> rho, labels = defining_representation('A2')
        >>> labels, rho[0].nrows()
        (['e1', 'e2', 'f1', 'f2', 'h1', 'h2'], 3)
    """
    letter, rank = cartan_type[0].upper(), int(cartan_type[1:])
    algebra = LieAlgebra(QQ, cartan_type=[letter, rank], representation='matrix')
    labels, matrices = [], []
    for name, getter in (('e', algebra.e), ('f', algebra.f), ('h', algebra.h)):
        for i in range(1, rank + 1):
            labels.append(f'{name}{i}')
            matrices.append(matrix(QQ, getter(i).value))
    return matrices, labels

derivative_chain(body, var, order, problem, *, function_text=None, proofs=None)

Return facts dₖ : deriv (fun s => Bₖ₋₁) = fun s => Bₖ for k = 1 … order, and their names.

Each fact is proved at an arbitrary point by a composed HasDerivAt term. function_text states the first function as the goal writes it, so that rewriting with d1 matches the goal.

Parameters:

Name Type Description Default
body Expression

The function's body B₀, in var.

required
var Expression

The variable, a Sage symbol ending in _ (rendered without it).

required
order int

How many derivatives.

required
problem Problem

The problem whose atoms name the parameters.

required
function_text str | None

The Lean text of the first function, rendered from body when None.

None
proofs Mapping[str, str] | None

Tactics proving nonzero conditions no hypothesis states, by their Lean text.

None

Returns:

Type Description
tuple[list[Lean], list[str]]

The facts and their names.

Source code in src/veriphy/toolkit/_ode.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def derivative_chain(
    body: Expression,
    var: Expression,
    order: int,
    problem: Problem,
    *,
    function_text: str | None = None,
    proofs: Mapping[str, str] | None = None,
) -> tuple[list[Lean], list[str]]:
    """Return facts `dₖ : deriv (fun s => Bₖ₋₁) = fun s => Bₖ` for `k = 1 … order`, and their names.

    Each fact is proved at an arbitrary point by a composed `HasDerivAt` term. `function_text` states the first
    function as the goal writes it, so that rewriting with `d1` matches the goal.

    Args:
        body: The function's body `B₀`, in `var`.
        var: The variable, a Sage symbol ending in `_` (rendered without it).
        order: How many derivatives.
        problem: The problem whose atoms name the parameters.
        function_text: The Lean text of the first function, rendered from `body` when `None`.
        proofs: Tactics proving nonzero conditions no hypothesis states, by their Lean text.

    Returns:
        The facts and their names.
    """
    facts: list[Lean] = []
    names: list[str] = []
    point = str(var).rstrip('_')
    current, current_text = SR(body), function_text or _lambda(body, var, problem)
    for k in range(1, order + 1):
        nxt = diff(current, var)
        nxt_text = _lambda(nxt, var, problem)
        step = has_deriv_at_proof(current, var, var, nxt, problem, proofs=proofs, function=current_text, at=point)
        name = f'd{k}'
        facts.append(
            tactic(
                f'have {name} : deriv ({current_text}) = {nxt_text} := by\n'
                f'  funext {point}\n' + textwrap.indent(str(step), '  ') + '\n  exact hd.deriv',
            ),
        )
        names.append(name)
        current, current_text = nxt, nxt_text
    return facts, names

derivative_order(expr, function)

Return the highest order to which expr differentiates the unknown function named function.

Parameters:

Name Type Description Default
expr Expression

A symbolic expression.

required
function str

The Sage name of the unknown function.

required

Returns:

Type Description
int

The order, 0 when the function is not differentiated.

Examples:

>>> from sage.all import function
>>> t = SR.var('t'); f = function('F_x')
>>> derivative_order(diff(f(t), t, 2) + f(t), 'F_x')
2
Source code in src/veriphy/toolkit/_ode.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def derivative_order(expr: Expression, function: str) -> int:
    """Return the highest order to which `expr` differentiates the unknown function named `function`.

    Args:
        expr: A symbolic expression.
        function: The Sage name of the unknown function.

    Returns:
        The order, 0 when the function is not differentiated.

    Examples:
        >>> from sage.all import function
        >>> t = SR.var('t'); f = function('F_x')
        >>> derivative_order(diff(f(t), t, 2) + f(t), 'F_x')
        2
    """
    best = 0
    stack = [SR(expr)]
    while stack:
        e = stack.pop()
        op = e.operator()
        if isinstance(op, FDerivativeOperator) and op.function().name() == function:
            best = max(best, len(op.parameter_set()))
        stack.extend(e.operands())
    return best

det_certificate(m)

Return a proof term of A.det = d for an integer matrix, by an LU factorization over ℚ the kernel checks.

The factorization is Sage's, without pivoting; Cartan matrices (and any matrix whose leading minors are nonzero) have one. The goal's matrix may be a definition (CartanMatrix.E 8) or a literal: the kernel unfolds it.

Parameters:

Name Type Description Default
m Matrix

The integer matrix, as the goal states it.

required

Returns:

Type Description
Lean

The term, for exact.

Raises:

Type Description
CertificateError

If the matrix is not square and integral, or needs pivoting.

Examples:

>>> proof = str(det_certificate(matrix(ZZ, [[2, -1], [-1, 2]])))
>>> proof.startswith('VeriphyCheck.det_eq_of_lu_int (d := 3) (L := (!![1, 0; -1 / 2, 1] : ')
True
>>> '(U := (!![2, -1; 0, 3 / 2] : Matrix (Fin 2) (Fin 2) ℚ))' in proof
True
Source code in src/veriphy/toolkit/_lie.py
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
def det_certificate(m: Matrix) -> Lean:
    """Return a proof term of `A.det = d` for an integer matrix, by an LU factorization over ℚ the kernel checks.

    The factorization is Sage's, without pivoting; Cartan matrices (and any matrix whose leading minors are
    nonzero) have one. The goal's matrix may be a definition (`CartanMatrix.E 8`) or a literal: the kernel unfolds it.

    Args:
        m: The integer matrix, as the goal states it.

    Returns:
        The term, for `exact`.

    Raises:
        CertificateError: If the matrix is not square and integral, or needs pivoting.

    Examples:
        >>> proof = str(det_certificate(matrix(ZZ, [[2, -1], [-1, 2]])))
        >>> proof.startswith('VeriphyCheck.det_eq_of_lu_int (d := 3) (L := (!![1, 0; -1 / 2, 1] : ')
        True
        >>> '(U := (!![2, -1; 0, 3 / 2] : Matrix (Fin 2) (Fin 2) ℚ))' in proof
        True
    """
    if not m.is_square() or not all(x in ZZ for x in m.list()):
        message = 'the determinant certificate needs a square integer matrix'
        raise CertificateError(message)
    permutation, lower, upper = matrix(QQ, m).LU(pivot='nonzero')
    if permutation != 1:
        message = 'the matrix needs pivoting (a leading minor vanishes); reorder its rows and columns first'
        raise CertificateError(message)
    checks = ' '.join(['(by decide +kernel)'] * 4)
    return Lean(
        f'VeriphyCheck.det_eq_of_lu_int (d := {m.det()}) (L := {rational_matrix_term(lower)}) '
        f'(U := {rational_matrix_term(upper)}) {checks}',
        'term',
    )

diagonalizable_exponential(a, parameter='t')

Return exp (t • A) for A diagonalizable over ℚ, and a proof of the goal exp (t • A) = E.

The goal's right side may be written with Real.exp, Real.cosh or Real.sinh: the entries are compared after unfolding cosh and sinh into exponentials.

Parameters:

Name Type Description Default
a Matrix

A rational matrix with rational eigenvalues and a basis of eigenvectors.

required
parameter str

The Lean name of the real parameter t.

't'

Returns:

Type Description
tuple[Matrix, Lean]

The closed form (over Sage's symbolic ring, in a variable named as the parameter) and the tactic.

Raises:

Type Description
CertificateError

If A is not diagonalizable over ℚ.

Examples:

>>> e, proof = diagonalizable_exponential(matrix(QQ, [[0, 1], [1, 0]]), 'φ')
>>> e[0, 0]
1/2*e^(-φ) + 1/2*e^φ
Source code in src/veriphy/toolkit/_exponential.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def diagonalizable_exponential(a: Matrix, parameter: str = 't') -> tuple[Matrix, Lean]:
    """Return `exp (t • A)` for `A` diagonalizable over ℚ, and a proof of the goal `exp (t • A) = E`.

    The goal's right side may be written with `Real.exp`, `Real.cosh` or `Real.sinh`: the entries are compared after
    unfolding `cosh` and `sinh` into exponentials.

    Args:
        a: A rational matrix with rational eigenvalues and a basis of eigenvectors.
        parameter: The Lean name of the real parameter `t`.

    Returns:
        The closed form (over Sage's symbolic ring, in a variable named as the parameter) and the tactic.

    Raises:
        CertificateError: If `A` is not diagonalizable over ℚ.

    Examples:
        >>> e, proof = diagonalizable_exponential(matrix(QQ, [[0, 1], [1, 0]]), 'φ')
        >>> e[0, 0]
        1/2*e^(-φ) + 1/2*e^φ
    """
    a = matrix(QQ, a)
    try:
        d, p = a.diagonalization()
    except ValueError:
        message = 'the matrix is not diagonalizable over ℚ; use a Jordan decomposition'
        raise CertificateError(message) from None
    t = var(parameter)
    exp_d = diagonal_matrix(SR, [SR(lam * t).exp() for lam in d.diagonal()])
    e = matrix(SR, p) * exp_d * matrix(SR, p.inverse())
    eigen = ', '.join(f'{_rational(lam)} * {parameter}' for lam in d.diagonal())
    p_text, q_text = _literal(p), _literal(p.inverse())
    lines = [
        f'have hPQ : {p_text} * {q_text} = 1 := by',
        '  ext i j\n  fin_cases i <;> fin_cases j <;> norm_num [Matrix.mul_apply, Fin.sum_univ_succ, Matrix.one_apply]',
        f'have hinv : {p_text}⁻¹ = {q_text} := Matrix.inv_eq_right_inv hPQ',
        f'have hunit : IsUnit {p_text} := ⟨⟨_, _, hPQ, mul_eq_one_comm.mp hPQ⟩, rfl⟩',
        f'have hconj : {parameter} • {_literal(a)} = {p_text} * Matrix.diagonal ![{eigen}] * {p_text}⁻¹ := by',
        '  rw [hinv]',
        f'  ext i j\n  fin_cases i <;> fin_cases j <;> {ENTRY_SIMP} <;> ring',
        'rw [hconj, Matrix.exp_conj _ _ hunit, Matrix.exp_diagonal, hinv, Pi.exp_def]',
        'ext i j\nfin_cases i <;> fin_cases j <;>',
        f'  {ENTRY_SIMP.replace("]", ", ← Real.exp_eq_exp_ℝ, Real.cosh_eq, Real.sinh_eq]")} <;> ring',
    ]
    return e, tactic('\n'.join(lines))

eigen_projectors(c)

Return the projectors onto the eigenspaces of a diagonalizable matrix with rational eigenvalues (a Casimir).

Each is Pₖ = Πₗ≠ₖ (C - λₗ) / (λₖ - λₗ).

Parameters:

Name Type Description Default
c Matrix

The matrix.

required

Returns:

Type Description
list[tuple[object, Matrix]]

(eigenvalue, projector) pairs.

Raises:

Type Description
CertificateError

If an eigenvalue is irrational or the matrix is not diagonalizable.

Source code in src/veriphy/toolkit/_representation.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
def eigen_projectors(c: Matrix) -> list[tuple[object, Matrix]]:
    """Return the projectors onto the eigenspaces of a diagonalizable matrix with rational eigenvalues (a Casimir).

    Each is `Pₖ = Πₗ≠ₖ (C - λₗ) / (λₖ - λₗ)`.

    Args:
        c: The matrix.

    Returns:
        `(eigenvalue, projector)` pairs.

    Raises:
        CertificateError: If an eigenvalue is irrational or the matrix is not diagonalizable.
    """
    minimal = c.minimal_polynomial()
    roots = minimal.roots(QQ)
    if sum(m for _, m in roots) != minimal.degree() or any(m > 1 for _, m in roots):
        message = f'the matrix is not diagonalizable over ℚ (minimal polynomial {minimal.factor()})'
        raise CertificateError(message)
    one = identity_matrix(QQ, c.nrows())
    projectors = []
    for value, _ in roots:
        p = one
        for other, _ in roots:
            if other != value:
                p = p * (c - other * one) / (value - other)
        projectors.append((value, p))
    return projectors

eigenvalue_proof(m, matrix_lean, a, b)

Return a proof of ∃ x : ℝ, (a < x ∧ x < b) ∧ (x • 1 - M).det = 0: an eigenvalue of a 3 by 3 matrix there.

The determinant is expanded to the characteristic polynomial, whose root on the interval root_proof certifies.

Parameters:

Name Type Description Default
m Matrix

The rational 3 by 3 matrix.

required
matrix_lean str

The matrix as the goal writes it (a literal over ℝ).

required
a object

The interval's left end (from isolate_roots on characteristic_polynomial(m)).

required
b object

The interval's right end.

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the matrix is not 3 by 3, or the interval does not isolate a simple root.

Source code in src/veriphy/toolkit/_numerics.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
def eigenvalue_proof(m: Matrix, matrix_lean: str, a: object, b: object) -> Lean:
    """Return a proof of `∃ x : ℝ, (a < x ∧ x < b) ∧ (x • 1 - M).det = 0`: an eigenvalue of a 3 by 3 matrix there.

    The determinant is expanded to the characteristic polynomial, whose root on the interval `root_proof` certifies.

    Args:
        m: The rational 3 by 3 matrix.
        matrix_lean: The matrix as the goal writes it (a literal over ℝ).
        a: The interval's left end (from `isolate_roots` on `characteristic_polynomial(m)`).
        b: The interval's right end.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the matrix is not 3 by 3, or the interval does not isolate a simple root.
    """
    if (m.nrows(), m.ncols()) != (3, 3):  # the determinant expands by `Matrix.det_fin_three`
        message = 'eigenvalue certificates expand the determinant of 3 by 3 matrices'
        raise CertificateError(message)
    p = characteristic_polynomial(m)
    p_lean = str(p).replace('^', ' ^ ').replace('*', ' * ')
    root = '\n'.join(f'  {line}' for line in str(root_proof(p_lean, a, b, p)).splitlines())
    lines = [
        f'have hdet : ∀ x : ℝ, (x • (1 : Matrix (Fin 3) (Fin 3) ℝ) - {matrix_lean}).det = {p_lean} := by',
        '  intro x',
        '  simp [Matrix.det_fin_three, Matrix.one_apply]',
        '  ring',
        f'have hroot : ∃! x : ℝ, ({_decimal(QQ(a))} < x ∧ x < {_decimal(QQ(b))}) ∧ {p_lean} = 0 := by',
        root,
        'obtain ⟨x, hx, -⟩ := hroot',
        'exact ⟨x, hx.1, by rw [hdet]; exact hx.2⟩',
    ]
    return tactic('\n'.join(lines))

exact_form_proof(f, n)

Return a proof of ∀ i x, extDeriv F ![i] x = ω i x for the 0-form F of a primitive f.

The goal's F is fun _ x => f x and its ω the component functions; each component's partial derivative is expanded by simp and closed by ring.

Parameters:

Name Type Description Default
f object

The primitive (only its existence matters to the proof; Sage checks it).

required
n int

The dimension.

required

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_geometry.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def exact_form_proof(f: object, n: int) -> Lean:
    """Return a proof of `∀ i x, extDeriv F ![i] x = ω i x` for the 0-form `F` of a primitive `f`.

    The goal's `F` is `fun _ x => f x` and its `ω` the component functions; each component's partial derivative is
    expanded by `simp` and closed by `ring`.

    Args:
        f: The primitive (only its existence matters to the proof; Sage checks it).
        n: The dimension.

    Returns:
        The tactic.
    """
    del f
    names = ', '.join(['CoordinateGeometry.extDeriv', 'CoordinateGeometry.pd', DERIVATIVE_SIMP])
    return tactic(
        'intro i x\n'
        f'fin_cases i <;> simp (disch := fun_prop) [{names}]\n'
        'all_goals first | ring1 | (field_simp; ring1)',
    )

exterior_square(rho)

Return the representation on Λ²V, in the basis eᵢ ∧ eⱼ (i < j).

Parameters:

Name Type Description Default
rho Rep

The representation on V.

required

Returns:

Type Description
tuple[Rep, list[tuple[int, int]]]

The matrices and the index pairs of the basis.

Examples:

>>> wedge, pairs = exterior_square(defining_representation('A4')[0])
>>> wedge[0].nrows(), pairs[:2]
(10, [(0, 1), (0, 2)])
Source code in src/veriphy/toolkit/_representation.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def exterior_square(rho: Rep) -> tuple[Rep, list[tuple[int, int]]]:
    """Return the representation on `Λ²V`, in the basis `eᵢ ∧ eⱼ` (`i < j`).

    Args:
        rho: The representation on `V`.

    Returns:
        The matrices and the index pairs of the basis.

    Examples:
        >>> wedge, pairs = exterior_square(defining_representation('A4')[0])
        >>> wedge[0].nrows(), pairs[:2]
        (10, [(0, 1), (0, 2)])
    """
    n = rho[0].nrows()
    pairs = list(itertools.combinations(range(n), 2))
    index = {p: k for k, p in enumerate(pairs)}
    result = []
    for x in rho:
        m = zero_matrix(QQ, len(pairs))
        for col, (i, j) in enumerate(pairs):
            # x(eᵢ ∧ eⱼ) = x eᵢ ∧ eⱼ + eᵢ ∧ x eⱼ
            for k in range(n):
                for a, b, c in ((k, j, x[k, i]), (i, k, x[k, j])):
                    if c == 0 or a == b:
                        continue
                    sign = 1 if a < b else -1
                    m[index[(min(a, b), max(a, b))], col] += sign * c
        result.append(m)
    return result, pairs

f4()

Return F(4): so(7) ⊕ sl2 on 8 ⊗ 2, the spinor tensor the doublet (40-dimensional, 24 even, 16 odd).

Returns:

Type Description
tuple[Table, list[int]]

The structure constants and parities.

Source code in src/veriphy/toolkit/_superalgebra.py
417
418
419
420
421
422
423
424
425
426
def f4() -> tuple[Table, list[int]]:
    """Return `F(4)`: `so(7) ⊕ sl2` on `8 ⊗ 2`, the spinor tensor the doublet (40-dimensional, 24 even, 16 odd).

    Returns:
        The structure constants and parities.
    """
    gammas = so7_spinor_gammas()
    so7 = [gammas[i] * gammas[j] / 2 for i, j in itertools.combinations(range(7), 2)]
    even = [_kron(t, identity_matrix(QQ, 2)) for t in so7] + [_kron(identity_matrix(QQ, 8), s) for s in SL2]
    return contragredient_superalgebra(even, [0] * len(so7) + [1] * 3, _kron(identity_matrix(QQ, 8), EPSILON))

fibonacci()

Return the Fibonacci category's fusion data, with F-symbols in a gauge over ℚ(√5).

The only nontrivial F-matrix is F^{τττ}_τ; its entries are solved from the pentagon equations with the gauge fixed by (F^{τττ}_τ)_{1τ} = 1.

Returns:

Type Description
Fusion

The data.

Raises:

Type Description
CertificateError

If the pentagon equations have no solution in ℚ(√5) in this gauge.

Source code in src/veriphy/toolkit/_fusion.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def fibonacci() -> Fusion:
    """Return the Fibonacci category's fusion data, with F-symbols in a gauge over `ℚ(√5)`.

    The only nontrivial F-matrix is `F^{τττ}_τ`; its entries are solved from the pentagon equations with the gauge
    fixed by `(F^{τττ}_τ)_{1τ} = 1`.

    Returns:
        The data.

    Raises:
        CertificateError: If the pentagon equations have no solution in `ℚ(√5)` in this gauge.
    """
    data = Fusion(['1', 'τ'], [0, 1], {(0, 0): [0], (0, 1): [1], (1, 0): [1], (1, 1): [0, 1]}, field='sqrt5')
    ring = PolynomialRing(SQRT5, 'p, q, r')
    p, q, r = ring.gens()
    trial = Fusion(data.labels, data.dual, data.rules)
    trial.f_values = {(1, 1, 1, 1, 0, 0): p, (1, 1, 1, 1, 0, 1): 1, (1, 1, 1, 1, 1, 0): q, (1, 1, 1, 1, 1, 1): r}
    equations = [lhs - rhs for lhs, rhs in _pentagon_sides(trial)]
    solutions = ring.ideal(equations).variety()
    if not solutions:
        message = 'the Fibonacci pentagon equations have no solution in ℚ(√5) in this gauge'
        raise CertificateError(message)
    s = next(x for x in solutions if x[p] != 0)
    data.f_values = {
        (1, 1, 1, 1, 0, 0): s[p],
        (1, 1, 1, 1, 1, 0): s[q],
        (1, 1, 1, 1, 1, 1): s[r],
    }
    return data

field_identity(context, max_power=None)

Return a proof of a scalar identity over the field: the generator's fact, then field_reduce.

Parameters:

Name Type Description Default
context FieldContext

The field's context.

required
max_power int | None

The largest power of the generator after normalizing the goal.

None

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_number_field.py
279
280
281
282
283
284
285
286
287
288
289
290
291
def field_identity(context: FieldContext, max_power: int | None = None) -> Lean:
    """Return a proof of a scalar identity over the field: the generator's fact, then `field_reduce`.

    Args:
        context: The field's context.
        max_power: The largest power of the generator after normalizing the goal.

    Returns:
        The tactic.
    """
    steps = [str(context.setup)] if context.setup is not None else []
    steps.append(str(field_reduce(context, max_power)))
    return tactic('\n'.join(steps))

field_invariant(f, eta)

Return F_{μν} F^{μν} for a field strength with lower indices and a constant metric.

Parameters:

Name Type Description Default
f Matrix

The matrix F_{μν}.

required
eta Matrix

The metric (its own inverse up to the sign conventions of a diagonal ±1 metric is not assumed).

required

Returns:

Type Description
object

The contraction, expanded.

Examples:

>>> from sage.all import diagonal_matrix
>>> e1, b3 = var('E1 B3')
>>> f = matrix(SR, [[0, -e1, 0, 0], [e1, 0, -b3, 0], [0, b3, 0, 0], [0, 0, 0, 0]])
>>> field_invariant(f, diagonal_matrix(SR, [1, -1, -1, -1]))
2*B3^2 - 2*E1^2
Source code in src/veriphy/toolkit/_geometry.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def field_invariant(f: Matrix, eta: Matrix) -> object:
    """Return `F_{μν} F^{μν}` for a field strength with lower indices and a constant metric.

    Args:
        f: The matrix `F_{μν}`.
        eta: The metric (its own inverse up to the sign conventions of a diagonal `±1` metric is not assumed).

    Returns:
        The contraction, expanded.

    Examples:
        >>> from sage.all import diagonal_matrix
        >>> e1, b3 = var('E1 B3')
        >>> f = matrix(SR, [[0, -e1, 0, 0], [e1, 0, -b3, 0], [0, b3, 0, 0], [0, 0, 0, 0]])
        >>> field_invariant(f, diagonal_matrix(SR, [1, -1, -1, -1]))
        2*B3^2 - 2*E1^2
    """
    inverse = eta.inverse()
    upper = inverse * f * inverse.transpose()
    return sum(f[m, n] * upper[m, n] for m in range(f.nrows()) for n in range(f.ncols())).expand()

field_matrix_identity(context, max_power=None)

Return an entrywise proof of an identity between explicit matrices over the field (A * B = C, A = B, …).

Parameters:

Name Type Description Default
context FieldContext

The field's context.

required
max_power int | None

The largest power of the generator in a normalized entry (by default that of a product of two reduced elements, enough for A * B = C).

None

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_number_field.py
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def field_matrix_identity(context: FieldContext, max_power: int | None = None) -> Lean:
    """Return an entrywise proof of an identity between explicit matrices over the field (`A * B = C`, `A = B`, …).

    Args:
        context: The field's context.
        max_power: The largest power of the generator in a normalized entry (by default that of a product of two
            reduced elements, enough for `A * B = C`).

    Returns:
        The tactic.
    """
    steps = [str(context.setup)] if context.setup is not None else []
    reduce = str(field_reduce(context, max_power))
    steps += ['ext i j', 'fin_cases i <;> fin_cases j', f'all_goals ({ENTRY_SIMP})', f'all_goals ({reduce})']
    return tactic('\n'.join(steps))

field_matrix_term(m, context)

Render a matrix over the field as a Lean !![…] literal.

Parameters:

Name Type Description Default
m Matrix

The matrix.

required
context FieldContext

The field's context.

required

Returns:

Type Description
Lean

The term.

Source code in src/veriphy/toolkit/_number_field.py
220
221
222
223
224
225
226
227
228
229
230
231
def field_matrix_term(m: Matrix, context: FieldContext) -> Lean:
    """Render a matrix over the field as a Lean `!![…]` literal.

    Args:
        m: The matrix.
        context: The field's context.

    Returns:
        The term.
    """
    rows = '; '.join(', '.join(render_element(x, context) for x in row) for row in m.rows())
    return term(f'!![{rows}]')

field_reduce(context, max_power=None)

Return a tactic closing a true identity over the field: normalize, reduce the generator's powers, normalize.

Parameters:

Name Type Description Default
context FieldContext

The field's context, whose fact is in scope.

required
max_power int | None

The largest power of the generator the normalized goal can contain (twice the degree minus two for a product of two reduced elements, the default).

None

Returns:

Type Description
Lean

The tactic.

Examples:

>>> reduce = str(field_reduce(quadratic_context(2)))
>>> reduce.startswith('(try ring_nf) <;> (set_option linter.unusedSimpArgs false in try simp only [show (')
True
>>> reduce.endswith('^ 2 = 2 by linear_combination (1) * hs]) <;> ring_nf')
True
Source code in src/veriphy/toolkit/_number_field.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def field_reduce(context: FieldContext, max_power: int | None = None) -> Lean:
    """Return a tactic closing a true identity over the field: normalize, reduce the generator's powers, normalize.

    Args:
        context: The field's context, whose fact is in scope.
        max_power: The largest power of the generator the normalized goal can contain (twice the degree minus two
            for a product of two reduced elements, the default).

    Returns:
        The tactic.

    Examples:
        >>> reduce = str(field_reduce(quadratic_context(2)))
        >>> reduce.startswith('(try ring_nf) <;> (set_option linter.unusedSimpArgs false in try simp only [show (')
        True
        >>> reduce.endswith('^ 2 = 2 by linear_combination (1) * hs]) <;> ring_nf')
        True
    """
    if context.modulus is None:
        return tactic('(try field_simp) <;> ring')
    bound = max_power if max_power is not None else 2 * context.modulus.degree() - 2
    reductions = _reductions(context, bound)
    if not reductions:
        return tactic('ring_nf')
    return tactic(
        f'(try ring_nf) <;> (set_option linter.unusedSimpArgs false in try simp only [{reductions}]) <;> ring_nf',
    )

field_simp(lemmas=())

Call field_simp, optionally with extra simp lemmas (such as nonzero-denominator facts).

Parameters:

Name Type Description Default
lemmas Sequence[str]

Lemma or hypothesis names.

()

Returns:

Type Description
Lean

The tactic.

Examples:

>>> str(field_simp(['hx']))
'field_simp [hx]'
Source code in src/veriphy/toolkit/_emit.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def field_simp(lemmas: Sequence[str] = ()) -> Lean:
    """Call `field_simp`, optionally with extra simp lemmas (such as nonzero-denominator facts).

    Args:
        lemmas: Lemma or hypothesis names.

    Returns:
        The tactic.

    Examples:
        >>> str(field_simp(['hx']))
        'field_simp [hx]'
    """
    return _with_list('field_simp', lemmas)

field_simp_clearing(problem, proofs=None)

Clear the problem's denominators with field_simp, passing a fact for every nonzero-denominator condition.

A denominator condition neither discharged nor supplied raises CertificateError, as in require_domain.

Parameters:

Name Type Description Default
problem Problem

The problem.

required
proofs Mapping[str, Lean | str] | None

A tactic proving a denominator condition no hypothesis discharges, by the condition's text.

None

Returns:

Type Description
Lean

The tactic, preceded by a have per supplied proof.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['y', 'x'], 'goal': {'kind': 'false'},
...     'domain': [{'expr': 'x0', 'kind': 'ne', 'text': 'y ≠ 0', 'discharged_by': 'hy'},
...                {'expr': 'x1^2 + 1', 'kind': 'ne', 'text': 'x ^ 2 + 1 ≠ 0'}]})
>>> print(field_simp_clearing(prob, proofs={'x ^ 2 + 1 ≠ 0': 'positivity'}))
have hd : x ^ 2 + 1 ≠ 0 := by positivity
field_simp [hy, hd]
Source code in src/veriphy/toolkit/_symbolic_emit.py
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
def field_simp_clearing(problem: Problem, proofs: Mapping[str, Lean | str] | None = None) -> Lean:
    """Clear the problem's denominators with `field_simp`, passing a fact for every nonzero-denominator condition.

    A denominator condition neither discharged nor supplied raises `CertificateError`, as in `require_domain`.

    Args:
        problem: The problem.
        proofs: A tactic proving a denominator condition no hypothesis discharges, by the condition's text.

    Returns:
        The tactic, preceded by a `have` per supplied proof.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['y', 'x'], 'goal': {'kind': 'false'},
        ...     'domain': [{'expr': 'x0', 'kind': 'ne', 'text': 'y ≠ 0', 'discharged_by': 'hy'},
        ...                {'expr': 'x1^2 + 1', 'kind': 'ne', 'text': 'x ^ 2 + 1 ≠ 0'}]})
        >>> print(field_simp_clearing(prob, proofs={'x ^ 2 + 1 ≠ 0': 'positivity'}))
        have hd : x ^ 2 + 1 ≠ 0 := by positivity
        field_simp [hy, hd]
    """
    names, haves = require_domain(problem, ('ne',), proofs)
    return steps(*haves, field_simp(names)) if haves else field_simp(names)

fierz_basis(gammas, eta)

Return the 16 matrices 1, γ^μ, γ^μ γ^ν (μ < ν), γ^μ γ⁵, γ⁵ and their classes S, V, T, A, P (0 … 4).

σ^{μν} = (i/2)[γ^μ, γ^ν] = i γ^μ γ^ν for μ ≠ ν; the factor i is dropped, which rescales the tensor class.

Parameters:

Name Type Description Default
gammas Sequence[Matrix]

The gamma matrices.

required
eta Matrix

The metric.

required

Returns:

Type Description
tuple[list[Matrix], list[int]]

The basis and the class of each element.

Source code in src/veriphy/toolkit/_clifford.py
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def fierz_basis(gammas: Sequence[Matrix], eta: Matrix) -> tuple[list[Matrix], list[int]]:
    """Return the 16 matrices `1, γ^μ, γ^μ γ^ν (μ < ν), γ^μ γ⁵, γ⁵` and their classes `S, V, T, A, P` (0 … 4).

    `σ^{μν} = (i/2)[γ^μ, γ^ν] = i γ^μ γ^ν` for `μ ≠ ν`; the factor `i` is dropped, which rescales the tensor class.

    Args:
        gammas: The gamma matrices.
        eta: The metric.

    Returns:
        The basis and the class of each element.
    """
    g5 = gamma5(gammas, eta)
    basis = [identity_matrix(FIELD, 4), *gammas]
    basis += [gammas[m] * gammas[n] for m, n in itertools.combinations(range(SPACETIME), 2)]
    basis += [g * g5 for g in gammas] + [g5]
    classes = [0] + [1] * 4 + [2] * 6 + [3] * 4 + [4]
    return basis, classes

fierz_coefficients(gammas, eta)

Return the 5×5 Fierz matrix C with Σ_{A∈X} (Γ_A)ᵢⱼ (Γ^A)ₖₗ = Σ_Y C_XY Σ_{B∈Y} (Γ_B)ᵢₗ (Γ^B)ₖⱼ.

Γ^A is the inverse of Γ_A (each squares to ±1).

Parameters:

Name Type Description Default
gammas Sequence[Matrix]

The gamma matrices.

required
eta Matrix

The metric.

required

Returns:

Type Description
Matrix

The coefficients.

Raises:

Type Description
CertificateError

If the rearrangement has no solution (the basis is not complete).

Examples:

>>> c = fierz_coefficients(*gamma_matrices())
>>> 4 * c[0]
(1, 1, 1, 1, 1)
Source code in src/veriphy/toolkit/_clifford.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
def fierz_coefficients(gammas: Sequence[Matrix], eta: Matrix) -> Matrix:
    """Return the 5×5 Fierz matrix `C` with `Σ_{A∈X} (Γ_A)ᵢⱼ (Γ^A)ₖₗ = Σ_Y C_XY Σ_{B∈Y} (Γ_B)ᵢₗ (Γ^B)ₖⱼ`.

    `Γ^A` is the inverse of `Γ_A` (each squares to `±1`).

    Args:
        gammas: The gamma matrices.
        eta: The metric.

    Returns:
        The coefficients.

    Raises:
        CertificateError: If the rearrangement has no solution (the basis is not complete).

    Examples:
        >>> c = fierz_coefficients(*gamma_matrices())
        >>> 4 * c[0]
        (1, 1, 1, 1, 1)
    """
    basis, classes = fierz_basis(gammas, eta)
    inverses = [b.inverse() for b in basis]

    def bilinear(x: int, swap: bool) -> list[object]:
        values = []
        for i, j, k, ell in itertools.product(range(4), repeat=4):
            a, b = (i, ell) if swap else (i, j)
            c, d = (k, j) if swap else (k, ell)
            values.append(sum(basis[n][a, b] * inverses[n][c, d] for n in range(16) if classes[n] == x))
        return values

    targets = [bilinear(y, swap=True) for y in range(5)]
    system = matrix(FIELD, targets).transpose()
    rows = []
    for x in range(5):
        try:
            rows.append(list(system.solve_right(vector(FIELD, bilinear(x, swap=False)))))
        except ValueError:
            message = 'the Fierz rearrangement has no solution in this basis'
            raise CertificateError(message) from None
    return matrix(FIELD, rows)

fierz_fact(gammas, eta)

Return the Fierz coefficients and a fact proving the rearrangement for every index and class.

The fact is stated with 4 C (integers) and checked by the kernel over ℤ[i].

Parameters:

Name Type Description Default
gammas Sequence[Matrix]

The gamma matrices.

required
eta Matrix

The metric.

required

Returns:

Type Description
tuple[Matrix, Lean]

C and the fact hfierz.

Source code in src/veriphy/toolkit/_clifford.py
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
def fierz_fact(gammas: Sequence[Matrix], eta: Matrix) -> tuple[Matrix, Lean]:
    """Return the Fierz coefficients and a fact proving the rearrangement for every index and class.

    The fact is stated with `4 C` (integers) and checked by the kernel over ℤ[i].

    Args:
        gammas: The gamma matrices.
        eta: The metric.

    Returns:
        `C` and the fact `hfierz`.
    """
    c = fierz_coefficients(gammas, eta)
    basis, classes = fierz_basis(gammas, eta)
    inverses = [b.inverse() for b in basis]
    coefficient = (
        '(!['
        + ', '.join('![' + ', '.join(_gaussian(4 * x) for x in row) + ']' for row in c.rows())
        + '] : Fin 5 → Fin 5 → GaussianInt)'
    )
    class_map = '(![' + ', '.join(str(k) for k in classes) + '] : Fin 16 → Fin 5)'
    binders = (
        '(Γ Γi : Fin 16 → Matrix (Fin 4) (Fin 4) GaussianInt) (cls : Fin 16 → Fin 5) (C : Fin 5 → Fin 5 → GaussianInt)'
    )
    body = (
        '∀ X : Fin 5, ∀ i j k l : Fin 4, '
        '4 * ∑ A ∈ Finset.univ.filter (cls · = X), Γ A i j * Γi A k l = '
        '∑ Y, C X Y * ∑ B ∈ Finset.univ.filter (cls · = Y), Γ B i l * Γi B k j'
    )
    return c, tactic(_fact('hfierz', binders, body, [_family(basis), _family(inverses), class_map, coefficient]))

find_gauge(reference, target)

Return a gauge taking the reference data to the target's, or None when they are not gauge equivalent.

The unknowns u^{ab}_c (one per admissible triple, nonzero) satisfy polynomial equations; gauge freedom left over (gauges fixing the data) is spent by setting unknowns to 1 while the system stays consistent, and the remaining finite system is solved in the data's field.

Parameters:

Name Type Description Default
reference Fusion

The data in Sage's gauge.

required
target Fusion

The data in the goal's gauge, same labels and rules.

required

Returns:

Type Description
Gauge | None

The gauge, on the admissible triples where it is not 1.

Raises:

Type Description
CertificateError

If the fusion rules differ.

Examples:

>>> fib = fibonacci()
>>> u = find_gauge(fib, gauge_transform(fib, {(1, 1, 1): 2}))
>>> gauge_transform(fib, u).f_values == gauge_transform(fib, {(1, 1, 1): 2}).f_values
True
Source code in src/veriphy/toolkit/_fusion.py
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
def find_gauge(reference: Fusion, target: Fusion) -> Gauge | None:
    """Return a gauge taking the reference data to the target's, or `None` when they are not gauge equivalent.

    The unknowns `u^{ab}_c` (one per admissible triple, nonzero) satisfy polynomial equations; gauge freedom left over
    (gauges fixing the data) is spent by setting unknowns to 1 while the system stays consistent, and the remaining
    finite system is solved in the data's field.

    Args:
        reference: The data in Sage's gauge.
        target: The data in the goal's gauge, same labels and rules.

    Returns:
        The gauge, on the admissible triples where it is not 1.

    Raises:
        CertificateError: If the fusion rules differ.

    Examples:
        >>> fib = fibonacci()
        >>> u = find_gauge(fib, gauge_transform(fib, {(1, 1, 1): 2}))
        >>> gauge_transform(fib, u).f_values == gauge_transform(fib, {(1, 1, 1): 2}).f_values
        True
    """
    if reference.rules != target.rules:
        message = 'the fusion rules differ: no gauge relates the data'
        raise CertificateError(message)
    triples = _admissible_triples(reference)
    ring = PolynomialRing(_field(reference), [f'u{i}' for i in range(len(triples))] + ['t'])
    *us, t = ring.gens()
    var = dict(zip(triples, us, strict=True))
    equations = _gauge_equations(reference, target, var)
    if equations is None:
        return None
    ideal = ring.ideal([t * prod(us) - 1, *equations])
    if ideal.dimension() < 0:
        return None
    for u in us:
        if ideal.dimension() == 0:
            break
        # spend gauge freedom that fixes the data
        fixed = ideal + ring.ideal([u - 1])
        if fixed.dimension() >= 0:
            ideal = fixed
    solutions = ideal.variety()
    if not solutions:
        return None
    solution = solutions[0]
    return {key: solution[v] for key, v in var.items() if solution[v] != 1}

fresh_name(problem, base, taken=())

Choose a hypothesis name unused in the problem.

Parameters:

Name Type Description Default
problem Problem

The problem whose hypothesis names are taken.

required
base str

The preferred name.

required
taken Sequence[str]

Further names to avoid.

()

Returns:

Type Description
str

base, or base_1, base_2, ... when base is taken.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'hyps': [{'name': 'h', 'text': 'True'}], 'goal': {'kind': 'false'}})
>>> fresh_name(prob, 'h'), fresh_name(prob, 'k')
('h_1', 'k')
Source code in src/veriphy/toolkit/_emit.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def fresh_name(problem: Problem, base: str, taken: Sequence[str] = ()) -> str:
    """Choose a hypothesis name unused in the problem.

    Args:
        problem: The problem whose hypothesis names are taken.
        base: The preferred name.
        taken: Further names to avoid.

    Returns:
        `base`, or `base_1`, `base_2`, ... when `base` is taken.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'hyps': [{'name': 'h', 'text': 'True'}], 'goal': {'kind': 'false'}})
        >>> fresh_name(prob, 'h'), fresh_name(prob, 'k')
        ('h_1', 'k')
    """
    used = {h.name for h in problem.hyps} | set(taken)
    if base not in used:
        return base
    index = 1
    while f'{base}_{index}' in used:
        index += 1
    return f'{base}_{index}'

fusion_data_term(data)

Return the data as a Lean FusionData.RepData n term, values as coefficient lists.

Parameters:

Name Type Description Default
data Fusion

The fusion data.

required

Returns:

Type Description
str

The term.

Source code in src/veriphy/toolkit/_fusion.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
def fusion_data_term(data: Fusion) -> str:
    """Return the data as a Lean `FusionData.RepData n` term, values as coefficient lists.

    Args:
        data: The fusion data.

    Returns:
        The term.
    """
    n = data.size
    dual = '![' + ', '.join(str(d) for d in data.dual) + ']'
    rules = (
        '!['
        + ', '.join(
            '![' + ', '.join('![' + ', '.join(str(data.n(a, b, c)) for c in range(n)) + ']' for b in range(n)) + ']'
            for a in range(n)
        )
        + ']'
    )
    admissible_f = 'N a b e ≠ 0 ∧ N e c d ≠ 0 ∧ N b c f ≠ 0 ∧ N a f d ≠ 0'
    labels6 = ' × '.join([f'Fin {n}'] * 6)
    f_term = (
        f'fun a b c d e f => (({_lookup(data.f_values, data.field, n)} : List (({labels6}) × List ℚ)).lookup '
        f'(a, b, c, d, e, f)).getD (if {admissible_f} then [1] else [])'
    )

    def braiding(values: Gauge) -> str:
        listed = _lookup(values, data.field, n)
        return (
            f'fun a b c => (({listed} : List ((Fin {n} × Fin {n} × Fin {n}) × List ℚ)).lookup (a, b, c)).getD '
            '(if N a b c ≠ 0 then [1] else [])'
        )

    r_term = braiding(data.r_values)
    rinv_term = braiding({key: 1 / value for key, value in data.r_values.items()})
    return (
        f'(let N : Fin {n} → Fin {n} → Fin {n} → ℕ := {rules}; '
        f'(⟨0, {dual}, N, {f_term}, {r_term}, {rinv_term}⟩ : FusionData.RepData {n}))'
    )

fusion_facts(data, *, braided, symbols=True)

Return the fact that the data form a fusion category (and a braided one), checked in Sage, then by the kernel.

The kernel checks the equations once on coefficient lists, on admissible labels; the asset's theorems give them in every ℚ-algebra at every ζ with ζ ^ m = r (ζ² = 5 for ℚ(√5), ζ⁸ = -1 for ℚ(ζ₁₆)), so the fact applies over ℝ or ℂ and at every Galois conjugate.

Parameters:

Name Type Description Default
data Fusion

The fusion data.

required
braided bool

Whether to state the hexagons too.

required
symbols bool

Whether to state the pentagon (False for fusion rules alone, with no F-symbols).

True

Returns:

Type Description
Lean

The fact hfusion: ∀ K ζ, ζ ^ m = r → FusionData.IsFusion (E.toData ζ) (or IsBraidedFusion), or the fusion

Lean

ring axioms of the rules alone.

Raises:

Type Description
CertificateError

If associativity, the pentagon or (when braided) the hexagons fail in Sage.

Source code in src/veriphy/toolkit/_fusion.py
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
def fusion_facts(data: Fusion, *, braided: bool, symbols: bool = True) -> Lean:
    """Return the fact that the data form a fusion category (and a braided one), checked in Sage, then by the kernel.

    The kernel checks the equations once on coefficient lists, on admissible labels; the asset's theorems give them
    in every `ℚ`-algebra at every `ζ` with `ζ ^ m = r` (`ζ² = 5` for `ℚ(√5)`, `ζ⁸ = -1` for `ℚ(ζ₁₆)`), so the fact
    applies over `ℝ` or `ℂ` and at every Galois conjugate.

    Args:
        data: The fusion data.
        braided: Whether to state the hexagons too.
        symbols: Whether to state the pentagon (`False` for fusion rules alone, with no F-symbols).

    Returns:
        The fact `hfusion`: `∀ K ζ, ζ ^ m = r → FusionData.IsFusion (E.toData ζ)` (or `IsBraidedFusion`), or the fusion
        ring axioms of the rules alone.

    Raises:
        CertificateError: If associativity, the pentagon or (when braided) the hexagons fail in Sage.
    """
    failure = _failure(data, braided=braided) if symbols else None
    if failure is not None:
        raise CertificateError(failure)
    if associativity_violations(data):
        message = f'the fusion rules are not associative at {associativity_violations(data)[0]}'
        raise CertificateError(message)
    term = fusion_data_term(data)
    if not symbols:
        return tactic(f'have hfusion : FusionData.RepData.IsFusionRing {term} := by\n  decide +kernel')
    m, r, _ = FIELDS[data.field]
    bundle = 'IsBraidedFusion' if braided else 'IsFusion'
    check = 'isBraidedFusion' if braided else 'isFusion'
    # the kernel decides the pruned equations on coefficients; the asset's theorems give the full ones at every ζ
    return tactic(
        f'have hfusion : ∀ {{K : Type}} [CommRing K] [Algebra ℚ K] (ζ : K), ζ ^ {m} = algebraMap ℚ K ({r}) →\n'
        f'    FusionData.{bundle} (FusionData.RepData.toData {term} ζ) :=\n'
        f'  fun _ hζ => FusionData.RepData.{check} (m := {m}) (r := {r}) hζ\n'
        '    (by set_option maxRecDepth 100000 in decide +kernel)',
    )

fusion_proof(data, *, braided, root=True)

Return a proof of a goal FusionData.IsFusion (RepData.toData E ζ) (or IsBraidedFusion) for explicit E.

Sage checks the data first, naming what fails; the kernel then checks the goal's own E (a definition or the literal of fusion_data_term), so the data must be Sage's in the goal's gauge (see gauge_mismatch).

Parameters:

Name Type Description Default
data Fusion

The fusion data the goal states.

required
braided bool

Whether the goal is IsBraidedFusion.

required
root bool

Whether ζ is the field's standard root (root_fact); otherwise a hypothesis hζ : ζ ^ m = … must be in the context.

True

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If associativity, the pentagon or (when braided) the hexagons fail.

Source code in src/veriphy/toolkit/_fusion.py
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
def fusion_proof(data: Fusion, *, braided: bool, root: bool = True) -> Lean:
    """Return a proof of a goal `FusionData.IsFusion (RepData.toData E ζ)` (or `IsBraidedFusion`) for explicit `E`.

    Sage checks the data first, naming what fails; the kernel then checks the goal's own `E` (a definition or the
    literal of `fusion_data_term`), so the data must be Sage's in the goal's gauge (see `gauge_mismatch`).

    Args:
        data: The fusion data the goal states.
        braided: Whether the goal is `IsBraidedFusion`.
        root: Whether `ζ` is the field's standard root (`root_fact`); otherwise a hypothesis `hζ : ζ ^ m = …` must be
            in the context.

    Returns:
        The tactic.

    Raises:
        CertificateError: If associativity, the pentagon or (when braided) the hexagons fail.
    """
    failure = _failure(data, braided=braided)
    if failure is not None:
        raise CertificateError(failure)
    m, r, _ = FIELDS[data.field]
    check = 'isBraidedFusion' if braided else 'isFusion'
    lines = [str(root_fact(data.field))] if root else []
    lines.append(
        f'exact FusionData.RepData.{check} (m := {m}) (r := {r}) hζ '
        '(by set_option maxRecDepth 100000 in decide +kernel)',
    )
    return tactic('\n'.join(lines))

fusion_rules(cartan_type, level)

Return the fusion rules of a quantum group at a root of unity, from Sage's FusionRing.

Parameters:

Name Type Description Default
cartan_type str

The type, as 'A1' or 'G2'.

required
level int

The level.

required

Returns:

Type Description
Fusion

The fusion data with rules and duals (F- and R-symbols to be supplied).

Raises:

Type Description
CertificateError

If a fusion multiplicity exceeds 1: the asset covers multiplicity-free categories only.

Examples:

>>> ising = fusion_rules('A1', 2)
>>> ising.labels, ising.rules[(1, 1)]
(['A12(0)', 'A12(1)', 'A12(2)'], [0, 2])
Source code in src/veriphy/toolkit/_fusion.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def fusion_rules(cartan_type: str, level: int) -> Fusion:
    """Return the fusion rules of a quantum group at a root of unity, from Sage's `FusionRing`.

    Args:
        cartan_type: The type, as `'A1'` or `'G2'`.
        level: The level.

    Returns:
        The fusion data with rules and duals (F- and R-symbols to be supplied).

    Raises:
        CertificateError: If a fusion multiplicity exceeds 1: the asset covers multiplicity-free categories only.

    Examples:
        >>> ising = fusion_rules('A1', 2)
        >>> ising.labels, ising.rules[(1, 1)]
        (['A12(0)', 'A12(1)', 'A12(2)'], [0, 2])
    """
    ring = FusionRing(cartan_type, level)
    basis = list(ring.basis())
    index = {b: i for i, b in enumerate(basis)}
    rules = {}
    for (i, a), (j, b) in itertools.product(enumerate(basis), repeat=2):
        product = list(a * b)
        if any(coefficient > 1 for _, coefficient in product):
            message = (
                f'{a} ⊗ {b} has a fusion multiplicity above 1: {cartan_type} at level {level} is not multiplicity '
                'free, outside the FusionData asset (F-symbols would be matrices on multiplicity spaces)'
            )
            raise CertificateError(message)
        rules[(i, j)] = sorted(index[ring.monomial(key)] for key, _ in product)
    dual = [index[b.dual()] for b in basis]
    return Fusion([str(b) for b in basis], dual, rules)

g3()

Return G(3): G₂ ⊕ sl2 on 7 ⊗ 2 (31-dimensional, 17 even and 14 odd).

Returns:

Type Description
tuple[Table, list[int]]

The structure constants and parities.

Source code in src/veriphy/toolkit/_superalgebra.py
370
371
372
373
374
375
376
377
378
379
def g3() -> tuple[Table, list[int]]:
    """Return `G(3)`: `G₂ ⊕ sl2` on `7 ⊗ 2` (31-dimensional, 17 even and 14 odd).

    Returns:
        The structure constants and parities.
    """
    g2 = [matrix(QQ, b.value) for b in LieAlgebra(QQ, cartan_type=['G', 2], representation='matrix').basis()]
    (form,) = invariant_forms(g2, symmetric=True)
    even = [_kron(t, identity_matrix(QQ, 2)) for t in g2] + [_kron(identity_matrix(QQ, 7), s) for s in SL2]
    return contragredient_superalgebra(even, [0] * len(g2) + [1] * 3, _kron(form, EPSILON))

gamma5(gammas, eta)

Return γ⁵, the product γ⁰ γ¹ γ² γ³ times i or 1, whichever squares to the identity.

Parameters:

Name Type Description Default
gammas Sequence[Matrix]

The gamma matrices.

required
eta Matrix

The metric (unused: both signatures give (γ⁰ γ¹ γ² γ³)² = -1).

required

Returns:

Type Description
Matrix

γ⁵.

Examples:

>>> gammas, eta = gamma_matrices()
>>> g5 = gamma5(gammas, eta)
>>> g5 * g5 == 1, all(g5 * g + g * g5 == 0 for g in gammas)
(True, True)
Source code in src/veriphy/toolkit/_clifford.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def gamma5(gammas: Sequence[Matrix], eta: Matrix) -> Matrix:
    """Return `γ⁵`, the product `γ⁰ γ¹ γ² γ³` times `i` or `1`, whichever squares to the identity.

    Args:
        gammas: The gamma matrices.
        eta: The metric (unused: both signatures give `(γ⁰ γ¹ γ² γ³)² = -1`).

    Returns:
        `γ⁵`.

    Examples:
        >>> gammas, eta = gamma_matrices()
        >>> g5 = gamma5(gammas, eta)
        >>> g5 * g5 == 1, all(g5 * g + g * g5 == 0 for g in gammas)
        (True, True)
    """
    del eta
    product = gammas[0] * gammas[1] * gammas[2] * gammas[3]
    return I * product if (I * product) ** 2 == 1 else product

gamma_matrices(representation='dirac', signature='mostly_minus')

Return γ⁰ … γ³ and the metric η, over ℚ(i).

Parameters:

Name Type Description Default
representation str

'dirac' (γ⁰ diagonal) or 'weyl' (chiral, γ⁵ diagonal).

'dirac'
signature str

'mostly_minus' (η = diag(1, -1, -1, -1)) or 'mostly_plus' (η = diag(-1, 1, 1, 1), the matrices multiplied by i).

'mostly_minus'

Returns:

Type Description
tuple[list[Matrix], Matrix]

The four matrices and η.

Raises:

Type Description
CertificateError

If the representation or signature is unknown.

Examples:

>>> gammas, eta = gamma_matrices()
>>> clifford_violations(gammas, eta)
[]
Source code in src/veriphy/toolkit/_clifford.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def gamma_matrices(representation: str = 'dirac', signature: str = 'mostly_minus') -> tuple[list[Matrix], Matrix]:
    """Return `γ⁰ … γ³` and the metric `η`, over ℚ(i).

    Args:
        representation: `'dirac'` (`γ⁰` diagonal) or `'weyl'` (chiral, `γ⁵` diagonal).
        signature: `'mostly_minus'` (`η = diag(1, -1, -1, -1)`) or `'mostly_plus'` (`η = diag(-1, 1, 1, 1)`,
            the matrices multiplied by `i`).

    Returns:
        The four matrices and `η`.

    Raises:
        CertificateError: If the representation or signature is unknown.

    Examples:
        >>> gammas, eta = gamma_matrices()
        >>> clifford_violations(gammas, eta)
        []
    """
    one, zero = identity_matrix(FIELD, 2), matrix(FIELD, 2, 2)
    if representation == 'dirac':
        gammas = [_block(one, zero, zero, -one)] + [_block(zero, s, -s, zero) for s in PAULI]
    elif representation == 'weyl':
        gammas = [_block(zero, one, one, zero)] + [_block(zero, s, -s, zero) for s in PAULI]
    else:
        message = f"unknown representation {representation!r}; use 'dirac' or 'weyl'"
        raise CertificateError(message)
    eta = matrix(FIELD, 4, 4, lambda i, j: (1 if i == 0 else -1) if i == j else 0)
    if signature == 'mostly_plus':
        gammas, eta = [I * g for g in gammas], -eta
    elif signature != 'mostly_minus':
        message = f"unknown signature {signature!r}; use 'mostly_minus' or 'mostly_plus'"
        raise CertificateError(message)
    return gammas, eta

gauge_facts(reference, target, u)

Return the fact that the target is the reference in the gauge u, checked in Sage, then by the kernel.

Parameters:

Name Type Description Default
reference Fusion

The data in Sage's gauge.

required
target Fusion

The data in the goal's gauge.

required
u Gauge

The gauge (from find_gauge).

required

Returns:

Type Description
Lean

The fact hgauge : ∀ K ζ, ζ ^ m = r → FusionData.GaugeRelated (E.toData ζ) (E'.toData ζ) u uinv.

Raises:

Type Description
CertificateError

If u does not relate the data.

Source code in src/veriphy/toolkit/_fusion.py
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
def gauge_facts(reference: Fusion, target: Fusion, u: Gauge) -> Lean:
    """Return the fact that the target is the reference in the gauge `u`, checked in Sage, then by the kernel.

    Args:
        reference: The data in Sage's gauge.
        target: The data in the goal's gauge.
        u: The gauge (from `find_gauge`).

    Returns:
        The fact `hgauge : ∀ K ζ, ζ ^ m = r → FusionData.GaugeRelated (E.toData ζ) (E'.toData ζ) u uinv`.

    Raises:
        CertificateError: If `u` does not relate the data.
    """
    failure = _gauge_failure(reference, target, u)
    if failure is not None:
        raise CertificateError(failure)
    m, r, _ = FIELDS[reference.field]
    gauge = gauge_term(u, reference)
    inverse = gauge_term(u, reference, inverse=True)
    return tactic(
        f'have hgauge : ∀ {{K : Type}} [CommRing K] [Algebra ℚ K] (ζ : K), ζ ^ {m} = algebraMap ℚ K ({r}) →\n'
        f'    FusionData.GaugeRelated (FusionData.RepData.toData {fusion_data_term(reference)} ζ)\n'
        f'      (FusionData.RepData.toData {fusion_data_term(target)} ζ)\n'
        f'      (fun a b c => FusionData.Coeffs.eval ζ (({gauge}) a b c))\n'
        f'      (fun a b c => FusionData.Coeffs.eval ζ (({inverse}) a b c)) :=\n'
        f'  fun _ hζ => FusionData.RepData.gaugeRelated (m := {m}) (r := {r}) hζ\n'
        '    (by set_option maxRecDepth 100000 in decide +kernel)',
    )

gauge_mismatch(reference, target)

Return a report on how the target's F- and R-symbols differ from the reference's, and the gauge relating them.

Parameters:

Name Type Description Default
reference Fusion

The data in Sage's gauge.

required
target Fusion

The data in the goal's gauge.

required

Returns:

Type Description
str

The report: the differing symbols, then the gauge (or that none exists in the field: then the target is a

str

different category, or needs a field extension).

Examples:

>>> fib = fibonacci()
>>> print(gauge_mismatch(fib, gauge_transform(fib, {(1, 1, 1): 2})).splitlines()[-1])
gauge: u[(1, 1, 1)] = 2
Source code in src/veriphy/toolkit/_fusion.py
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
def gauge_mismatch(reference: Fusion, target: Fusion) -> str:
    """Return a report on how the target's F- and R-symbols differ from the reference's, and the gauge relating them.

    Args:
        reference: The data in Sage's gauge.
        target: The data in the goal's gauge.

    Returns:
        The report: the differing symbols, then the gauge (or that none exists in the field: then the target is a
        different category, or needs a field extension).

    Examples:
        >>> fib = fibonacci()
        >>> print(gauge_mismatch(fib, gauge_transform(fib, {(1, 1, 1): 2})).splitlines()[-1])
        gauge: u[(1, 1, 1)] = 2
    """
    lines = []
    for key in itertools.product(range(reference.size), repeat=6):
        if reference.f(*key) != target.f(*key):
            lines.append(f'F{key}: reference {reference.f(*key)}, target {target.f(*key)}')
    for key in _admissible_triples(reference):
        if reference.r(*key) != target.r(*key):
            lines.append(f'R{key}: reference {reference.r(*key)}, target {target.r(*key)}')
    if not lines:
        return 'the data agree: no gauge transformation needed'
    u = find_gauge(reference, target)
    if u is None:
        lines.append('no gauge in the field relates them: a different category, or a gauge over a field extension')
    else:
        lines.append('gauge: ' + ', '.join(f'u[{key}] = {value}' for key, value in sorted(u.items())))
    return '\n'.join(lines)

gauge_proof(reference, target, u, *, root=True)

Return a proof of a goal FusionData.GaugeRelated (E.toData ζ) (E'.toData ζ) (eval U) (eval Uinv).

The goal's gauge functions are fun a b c => FusionData.Coeffs.eval ζ (U a b c) with U the lookup that gauge_term(u, data) prints; Sage checks that u relates the data, the kernel checks the goal's.

Parameters:

Name Type Description Default
reference Fusion

The data in Sage's gauge.

required
target Fusion

The data in the goal's gauge.

required
u Gauge

The gauge (from find_gauge).

required
root bool

Whether ζ is the field's standard root; otherwise a hypothesis hζ must be in the context.

True

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If u does not relate the data.

Source code in src/veriphy/toolkit/_fusion.py
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
def gauge_proof(reference: Fusion, target: Fusion, u: Gauge, *, root: bool = True) -> Lean:
    """Return a proof of a goal `FusionData.GaugeRelated (E.toData ζ) (E'.toData ζ) (eval U) (eval Uinv)`.

    The goal's gauge functions are `fun a b c => FusionData.Coeffs.eval ζ (U a b c)` with `U` the lookup that
    `gauge_term(u, data)` prints; Sage checks that `u` relates the data, the kernel checks the goal's.

    Args:
        reference: The data in Sage's gauge.
        target: The data in the goal's gauge.
        u: The gauge (from `find_gauge`).
        root: Whether `ζ` is the field's standard root; otherwise a hypothesis `hζ` must be in the context.

    Returns:
        The tactic.

    Raises:
        CertificateError: If `u` does not relate the data.
    """
    failure = _gauge_failure(reference, target, u)
    if failure is not None:
        raise CertificateError(failure)
    m, r, _ = FIELDS[reference.field]
    lines = [str(root_fact(reference.field))] if root else []
    lines.append(
        f'exact FusionData.RepData.gaugeRelated (m := {m}) (r := {r}) hζ '
        '(by set_option maxRecDepth 100000 in decide +kernel)',
    )
    return tactic('\n'.join(lines))

gauge_term(u, data, *, inverse=False)

Return a gauge (or its inverse) as a Lean Fin n → Fin n → Fin n → List ℚ, 1 where unlisted.

Parameters:

Name Type Description Default
u Gauge

The gauge.

required
data Fusion

The data it acts on (for the labels and the field).

required
inverse bool

Whether to return the inverse gauge.

False

Returns:

Type Description
str

The term.

Examples:

>>> term = gauge_term({(1, 1, 1): 2}, fibonacci(), inverse=True)
>>> '(((1 : Fin 2), (1 : Fin 2), (1 : Fin 2)), [(1 / 2)])' in term, term.endswith('.getD [1]')
(True, True)
Source code in src/veriphy/toolkit/_fusion.py
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
def gauge_term(u: Gauge, data: Fusion, *, inverse: bool = False) -> str:
    """Return a gauge (or its inverse) as a Lean `Fin n → Fin n → Fin n → List ℚ`, 1 where unlisted.

    Args:
        u: The gauge.
        data: The data it acts on (for the labels and the field).
        inverse: Whether to return the inverse gauge.

    Returns:
        The term.

    Examples:
        >>> term = gauge_term({(1, 1, 1): 2}, fibonacci(), inverse=True)
        >>> '(((1 : Fin 2), (1 : Fin 2), (1 : Fin 2)), [(1 / 2)])' in term, term.endswith('.getD [1]')
        (True, True)
    """
    n = data.size
    values = {key: (1 / _field(data)(v) if inverse else v) for key, v in u.items()}
    listed = _lookup(values, data.field, n)
    return f'fun a b c => (({listed} : List ((Fin {n} × Fin {n} × Fin {n}) × List ℚ)).lookup (a, b, c)).getD [1]'

gauge_transform(data, u)

Return the data in another gauge.

(F'^{abc}_d)_{ef} = u^{ab}_e u^{ec}_d / (u^{bc}_f u^{af}_d) (F^{abc}_d)_{ef} and R'^{ab}_c = u^{ba}_c / u^{ab}_c R^{ab}_c; unlisted u are 1.

Parameters:

Name Type Description Default
data Fusion

The fusion data.

required
u Gauge

The gauge, on admissible triples.

required

Returns:

Type Description
Fusion

The transformed data, which satisfies the same pentagon and hexagon equations.

Examples:

>>> fib = fibonacci()
>>> other = gauge_transform(fib, {(1, 1, 1): 2})
>>> other.f(1, 1, 1, 1, 1, 0) == 4 * fib.f(1, 1, 1, 1, 1, 0), pentagon_violations(other)
(True, 0)
Source code in src/veriphy/toolkit/_fusion.py
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
def gauge_transform(data: Fusion, u: Gauge) -> Fusion:
    """Return the data in another gauge.

    `(F'^{abc}_d)_{ef} = u^{ab}_e u^{ec}_d / (u^{bc}_f u^{af}_d) (F^{abc}_d)_{ef}` and
    `R'^{ab}_c = u^{ba}_c / u^{ab}_c R^{ab}_c`; unlisted `u` are 1.

    Args:
        data: The fusion data.
        u: The gauge, on admissible triples.

    Returns:
        The transformed data, which satisfies the same pentagon and hexagon equations.

    Examples:
        >>> fib = fibonacci()
        >>> other = gauge_transform(fib, {(1, 1, 1): 2})
        >>> other.f(1, 1, 1, 1, 1, 0) == 4 * fib.f(1, 1, 1, 1, 1, 0), pentagon_violations(other)
        (True, 0)
    """
    field = _field(data)

    def g(a: int, b: int, c: int) -> FieldElement:
        return field(u.get((a, b, c), 1))

    f_values = {}
    for key in itertools.product(range(data.size), repeat=6):
        a, b, c, d, e, f = key
        value = data.f(*key)
        if value == 0:
            continue
        new = g(a, b, e) * g(e, c, d) / (g(b, c, f) * g(a, f, d)) * value
        if new != 1:
            f_values[key] = new
    r_values = {}
    for a, b, c in _admissible_triples(data):
        new = g(b, a, c) / g(a, b, c) * data.r(a, b, c)
        if new != 1:
            r_values[(a, b, c)] = new
    return Fusion(data.labels, data.dual, data.rules, f_values, r_values, data.field)

gaussian_matrix_term(m)

Return a matrix over ℤ[i] as a Lean !![…] literal over GaussianInt.

Parameters:

Name Type Description Default
m Matrix

The matrix.

required

Returns:

Type Description
str

The literal.

Examples:

>>> gaussian_matrix_term(PAULI[1])
'(!![0, ⟨0, -1⟩; ⟨0, 1⟩, 0] : Matrix (Fin 2) (Fin 2) GaussianInt)'
Source code in src/veriphy/toolkit/_clifford.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def gaussian_matrix_term(m: Matrix) -> str:
    """Return a matrix over ℤ[i] as a Lean `!![…]` literal over `GaussianInt`.

    Args:
        m: The matrix.

    Returns:
        The literal.

    Examples:
        >>> gaussian_matrix_term(PAULI[1])
        '(!![0, ⟨0, -1⟩; ⟨0, 1⟩, 0] : Matrix (Fin 2) (Fin 2) GaussianInt)'
    """
    rows = '; '.join(', '.join(_gaussian(x) for x in row) for row in m.rows())
    return f'(!![{rows}] : Matrix (Fin {m.nrows()}) (Fin {m.ncols()}) GaussianInt)'

gosper_closed_form(f)

Return Σ_{k<n} f(k) in closed form by Gosper's algorithm.

Parameters:

Name Type Description Default
f Expr

The summand in k (and parameters).

required

Returns:

Type Description
Expr

The closed form in n.

Raises:

Type Description
CertificateError

If the sum is not Gosper-summable.

Examples:

>>> sp.combsimp(gosper_closed_form(K * sp.factorial(K)))
factorial(n) - 1
Source code in src/veriphy/toolkit/_summation.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def gosper_closed_form(f: sp.Expr) -> sp.Expr:
    """Return `Σ_{k<n} f(k)` in closed form by Gosper's algorithm.

    Args:
        f: The summand in `k` (and parameters).

    Returns:
        The closed form in `n`.

    Raises:
        CertificateError: If the sum is not Gosper-summable.

    Examples:
        >>> sp.combsimp(gosper_closed_form(K * sp.factorial(K)))
        factorial(n) - 1
    """
    closed = gosper_sum(_sympy(f), (K, 0, N - 1))
    if closed is None:
        message = f'{f} is not Gosper-summable: no hypergeometric closed form'
        raise CertificateError(message)
    return closed

gosper_proof()

Return the induction proving ∑ k ∈ Finset.range n, f k = S n once Sage has checked S is the antidifference.

Returns:

Type Description
Lean

The tactic: induction on n, Finset.sum_range_succ, the factorial and power recurrences, ring.

Source code in src/veriphy/toolkit/_summation.py
188
189
190
191
192
193
194
195
196
197
198
def gosper_proof() -> Lean:
    """Return the induction proving `∑ k ∈ Finset.range n, f k = S n` once Sage has checked `S` is the antidifference.

    Returns:
        The tactic: induction on `n`, `Finset.sum_range_succ`, the factorial and power recurrences, `ring`.
    """
    return tactic(
        'induction n with\n'
        '| zero => simp\n'
        '| succ n ih => rw [Finset.sum_range_succ, ih]; push_cast [Nat.factorial_succ, pow_succ]; ring',
    )

graded_commutator(x, y, px, py)

Return x y - (-1)^(|x||y|) y x.

Parameters:

Name Type Description Default
x Matrix

A homogeneous supermatrix.

required
y Matrix

Another.

required
px int

The parity of x (0 even, 1 odd).

required
py int

The parity of y.

required

Returns:

Type Description
Matrix

The graded commutator.

Source code in src/veriphy/toolkit/_superalgebra.py
32
33
34
35
36
37
38
39
40
41
42
43
44
def graded_commutator(x: Matrix, y: Matrix, px: int, py: int) -> Matrix:
    """Return `x y - (-1)^(|x||y|) y x`.

    Args:
        x: A homogeneous supermatrix.
        y: Another.
        px: The parity of `x` (0 even, 1 odd).
        py: The parity of `y`.

    Returns:
        The graded commutator.
    """
    return x * y - (-1) ** (px * py) * y * x

guess_recurrence(terms, order=2)

Guess a holonomic recurrence for a sequence from its first terms, with ore_algebra.

Parameters:

Name Type Description Default
terms Sequence[object]

The first terms of the sequence.

required
order int

The order of the recurrence sought.

2

Returns:

Type Description
object

The guessed operator (an ore_algebra object).

Raises:

Type Description
CertificateError

If ore_algebra is not installed (naming the command) or nothing is found.

Source code in src/veriphy/toolkit/_summation.py
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
def guess_recurrence(terms: Sequence[object], order: int = 2) -> object:
    """Guess a holonomic recurrence for a sequence from its first terms, with `ore_algebra`.

    Args:
        terms: The first terms of the sequence.
        order: The order of the recurrence sought.

    Returns:
        The guessed operator (an `ore_algebra` object).

    Raises:
        CertificateError: If `ore_algebra` is not installed (naming the command) or nothing is found.
    """
    try:
        guess = importlib.import_module('ore_algebra').guess
    except ImportError:
        message = f'guessing a holonomic recurrence needs the ore_algebra package: {ORE_ALGEBRA}'
        raise CertificateError(message) from None
    ring = PolynomialRing(QQ, 'n')
    try:
        return guess(list(terms), ring, order=order)
    except ValueError:
        message = 'no recurrence of this order fits the terms; give more terms or raise the order'
        raise CertificateError(message) from None

has_deriv_at(expr, var, point, problem, proofs=None)

Build a proof term of HasDerivAt (fun var => expr) _ point from mathlib's derivative lemmas.

The term composes hasDerivAt_const, hasDerivAt_id', .add, .mul, .div, .const_mul, .pow, .sqrt, .rpow_const, .exp, .log, .sin and .cos. The derivative it proves is mathlib's unsimplified form, which a proof states as Sage's derivative by HasDerivAt.congr_deriv and ring_nf or field_simp. A quotient, log, sqrt or real power needs its argument nonzero at the point: a hypothesis stating it, or a tactic in proofs. Any other variable of the expression is a constant, so the same term proves partial derivatives.

Parameters:

Name Type Description Default
expr object

The function's body, an expression in var and the problem's atoms.

required
var Expression

The variable of differentiation, a Sage symbol.

required
point object

The point, an expression over the problem's atoms.

required
problem Problem

The problem whose atoms name the variables.

required
proofs Mapping[str, str] | None

Tactics proving side conditions no hypothesis states, by their Lean text.

None

A side condition neither stated nor supplied, or a function with no derivative lemma, raises CertificateError naming it.

Returns:

Type Description
Lean

The proof term.

Examples:

>>> from sage.all import exp, log
>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'goal': {'kind': 'false'}})
>>> t = SR.var('t')
>>> str(has_deriv_at(t**2 * exp(t), t, 1, prob))
"(((hasDerivAt_id' (1 : ℝ)).pow 2).mul ((hasDerivAt_id' (1 : ℝ)).exp))"
>>> has_deriv_at(log(t), t, prob.symbols['x0'], prob)
Traceback (most recent call last):
...
veriphy.core._errors.CertificateError: the derivative needs 'a ≠ 0', which no hypothesis states; pass its proof
Source code in src/veriphy/toolkit/_derivative.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def has_deriv_at(
    expr: object,
    var: Expression,
    point: object,
    problem: Problem,
    proofs: Mapping[str, str] | None = None,
) -> Lean:
    """Build a proof term of `HasDerivAt (fun var => expr) _ point` from mathlib's derivative lemmas.

    The term composes `hasDerivAt_const`, `hasDerivAt_id'`, `.add`, `.mul`, `.div`, `.const_mul`, `.pow`, `.sqrt`,
    `.rpow_const`, `.exp`, `.log`, `.sin` and `.cos`. The derivative it proves is mathlib's unsimplified form, which
    a proof states as Sage's derivative by `HasDerivAt.congr_deriv` and `ring_nf` or `field_simp`. A quotient, `log`,
    `sqrt` or real power needs its argument nonzero at the point: a hypothesis stating it, or a tactic in `proofs`.
    Any other variable of the expression is a constant, so the same term proves partial derivatives.

    Args:
        expr: The function's body, an expression in `var` and the problem's atoms.
        var: The variable of differentiation, a Sage symbol.
        point: The point, an expression over the problem's atoms.
        problem: The problem whose atoms name the variables.
        proofs: Tactics proving side conditions no hypothesis states, by their Lean text.

    A side condition neither stated nor supplied, or a function with no derivative lemma, raises `CertificateError`
    naming it.

    Returns:
        The proof term.

    Examples:
        >>> from sage.all import exp, log
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'goal': {'kind': 'false'}})
        >>> t = SR.var('t')
        >>> str(has_deriv_at(t**2 * exp(t), t, 1, prob))
        "(((hasDerivAt_id' (1 : ℝ)).pow 2).mul ((hasDerivAt_id' (1 : ℝ)).exp))"
        >>> has_deriv_at(log(t), t, prob.symbols['x0'], prob)
        Traceback (most recent call last):
        ...
        veriphy.core._errors.CertificateError: the derivative needs 'a ≠ 0', which no hypothesis states; pass its proof
    """
    builder = _Builder(var, SR(point), problem, proofs or {})
    built = builder.build(SR(expr))
    return term(built)

has_deriv_at_proof(expr, var, point, value, problem, *, proofs=None, name='hd', function=None, at=None)

Build have name : HasDerivAt (fun var => expr) value point, proved from the composed term.

The step converts the term of has_deriv_at to the stated derivative (Sage's), unfolds the pointwise operations the term introduced, and closes the algebra by ring or field_simp. name.deriv then rewrites deriv.

Parameters:

Name Type Description Default
expr object

The function's body, an expression in var and the problem's atoms.

required
var Expression

The variable of differentiation; a symbol ending in _ renders without it (t_ as t).

required
point object

The point, an expression over the problem's atoms.

required
value object

The derivative at the point, as Sage computed it.

required
problem Problem

The problem whose atoms name the variables.

required
proofs Mapping[str, str] | None

Tactics proving side conditions no hypothesis states, by their Lean text.

None
name str

The name of the fact.

'hd'
function str | None

The function's Lean text, stated instead of the rendered body (so it matches a goal's deriv).

None
at str | None

The point's Lean text, stated instead of the rendered point.

None

Returns:

Type Description
Lean

The tactic block.

Examples:

>>> from sage.all import exp
>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'carrier': 'ℝ', 'goal': {'kind': 'false'}})
>>> t = SR.var('t_')
>>> first, second, closing = str(has_deriv_at_proof(t**2 * exp(t), t, 1, 3 * exp(1), prob)).splitlines()
>>> print(first); print(second)
have hd : HasDerivAt (fun t => ((t ^ 2) * (Real.exp t) : ℝ)) ((3 * (Real.exp 1) : ℝ)) (1 : ℝ) := by
  convert (((hasDerivAt_id' (1 : ℝ)).pow 2).mul ((hasDerivAt_id' (1 : ℝ)).exp)) using 1
>>> closing.split(' | (simp only')[0]
'  all_goals first | rfl | (funext y; simp only [Pi.mul_apply, Pi.pow_apply]; ring_nf; done)'
Source code in src/veriphy/toolkit/_derivative.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
def has_deriv_at_proof(
    expr: object,
    var: Expression,
    point: object,
    value: object,
    problem: Problem,
    *,
    proofs: Mapping[str, str] | None = None,
    name: str = 'hd',
    function: str | None = None,
    at: str | None = None,
) -> Lean:
    """Build `have name : HasDerivAt (fun var => expr) value point`, proved from the composed term.

    The step converts the term of `has_deriv_at` to the stated derivative (Sage's), unfolds the pointwise operations
    the term introduced, and closes the algebra by `ring` or `field_simp`. `name.deriv` then rewrites `deriv`.

    Args:
        expr: The function's body, an expression in `var` and the problem's atoms.
        var: The variable of differentiation; a symbol ending in `_` renders without it (`t_` as `t`).
        point: The point, an expression over the problem's atoms.
        value: The derivative at the point, as Sage computed it.
        problem: The problem whose atoms name the variables.
        proofs: Tactics proving side conditions no hypothesis states, by their Lean text.
        name: The name of the fact.
        function: The function's Lean text, stated instead of the rendered body (so it matches a goal's `deriv`).
        at: The point's Lean text, stated instead of the rendered point.

    Returns:
        The tactic block.

    Examples:
        >>> from sage.all import exp
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'carrier': 'ℝ', 'goal': {'kind': 'false'}})
        >>> t = SR.var('t_')
        >>> first, second, closing = str(has_deriv_at_proof(t**2 * exp(t), t, 1, 3 * exp(1), prob)).splitlines()
        >>> print(first); print(second)
        have hd : HasDerivAt (fun t => ((t ^ 2) * (Real.exp t) : ℝ)) ((3 * (Real.exp 1) : ℝ)) (1 : ℝ) := by
          convert (((hasDerivAt_id' (1 : ℝ)).pow 2).mul ((hasDerivAt_id' (1 : ℝ)).exp)) using 1
        >>> closing.split(' | (simp only')[0]
        '  all_goals first | rfl | (funext y; simp only [Pi.mul_apply, Pi.pow_apply]; ring_nf; done)'
    """
    builder = _Builder(var, SR(point), problem, proofs or {})
    built = builder.build(SR(expr))
    stated_function = function or f'fun {str(var).rstrip("_")} => {symbolic(SR(expr), problem, lean_type="ℝ")}'
    stated_point = f'({at})' if at else builder.point_text
    stated_value = symbolic(SR(value), problem, lean_type='ℝ')
    lines = [
        f'have {name} : HasDerivAt ({stated_function}) ({stated_value}) {stated_point} := by',
        f'  convert {built} using 1',
    ]
    applied = ', '.join(sorted(builder.pi_lemmas))
    value_close = f'(simp only [{applied}]; {CLOSE})' if applied else f'({CLOSE})'
    # `convert` may leave the function too, when the stated text orders arguments as Sage does (`s * ω`)
    unfold = ', '.join(sorted(_pointwise_lemmas(SR(expr), var)))
    function_close = f'(funext y; simp only [{unfold}]; ring_nf; done)' if unfold else '(funext y; ring_nf; done)'
    lines.append(f'  all_goals first | rfl | {function_close} | {value_close}')
    return Lean('\n'.join(lines), 'tactic')

hexagon_violations(data)

Return the number of hexagon equations (both kinds) the R- and F-symbols fail.

Parameters:

Name Type Description Default
data Fusion

The fusion data.

required

Returns:

Type Description
int

The count.

Source code in src/veriphy/toolkit/_fusion.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
def hexagon_violations(data: Fusion) -> int:
    """Return the number of hexagon equations (both kinds) the R- and F-symbols fail.

    Args:
        data: The fusion data.

    Returns:
        The count.
    """
    labels = range(data.size)

    def inv(x: FieldElement) -> FieldElement:
        return QQ(0) if x == 0 else QQ(1) / x

    failures = 0
    for a, b, c, d, e, g in itertools.product(labels, repeat=6):
        one = data.r(c, a, e) * data.f(a, c, b, d, e, g) * data.r(c, b, g)
        one_rhs = sum(data.f(c, a, b, d, e, f) * data.r(c, f, d) * data.f(a, b, c, d, f, g) for f in labels)
        two = inv(data.r(a, c, e)) * data.f(a, c, b, d, e, g) * inv(data.r(b, c, g))
        two_rhs = sum(data.f(c, a, b, d, e, f) * inv(data.r(f, c, d)) * data.f(a, b, c, d, f, g) for f in labels)
        failures += (one != one_rhs) + (two != two_rhs)
    return failures

hyp(problem, name)

Reference a hypothesis of the problem by name.

Parameters:

Name Type Description Default
problem Problem

The problem.

required
name str

The hypothesis name.

required

Returns:

Type Description
Lean

The reference, a term.

Raises:

Type Description
CertificateError

If the problem has no hypothesis of that name.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'hyps': [{'name': 'h1', 'text': 'True'}], 'goal': {'kind': 'false'}})
>>> str(hyp(prob, 'h1'))
'h1'
Source code in src/veriphy/toolkit/_emit.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def hyp(problem: Problem, name: str) -> Lean:
    """Reference a hypothesis of the problem by name.

    Args:
        problem: The problem.
        name: The hypothesis name.

    Returns:
        The reference, a term.

    Raises:
        CertificateError: If the problem has no hypothesis of that name.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'hyps': [{'name': 'h1', 'text': 'True'}], 'goal': {'kind': 'false'}})
        >>> str(hyp(prob, 'h1'))
        'h1'
    """
    names = [h.name for h in problem.hyps]
    if name not in names:
        message = f"unknown hypothesis '{name}' (known: {sorted(map(str, names))})"
        raise CertificateError(message)
    return term(name)

integral_rescaling(table, parities)

Rescale the basis (even elements by μ, odd by λ) so that every structure constant is an integer.

With eᵢ' = sᵢ eᵢ the constants become sᵢ sⱼ / sₖ cᵢⱼₖ: even-even and even-odd ones scale by μ, odd-odd ones by λ² / μ. Integer constants make the kernel check faster.

Parameters:

Name Type Description Default
table Table

Rational structure constants.

required
parities Sequence[int]

The basis parities.

required

Returns:

Type Description
tuple[Table, tuple[int, int]]

The rescaled table and (μ, λ).

Source code in src/veriphy/toolkit/_superalgebra.py
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
def integral_rescaling(table: Table, parities: Sequence[int]) -> tuple[Table, tuple[int, int]]:
    """Rescale the basis (even elements by `μ`, odd by `λ`) so that every structure constant is an integer.

    With `eᵢ' = sᵢ eᵢ` the constants become `sᵢ sⱼ / sₖ cᵢⱼₖ`: even-even and even-odd ones scale by `μ`, odd-odd ones
    by `λ² / μ`. Integer constants make the kernel check faster.

    Args:
        table: Rational structure constants.
        parities: The basis parities.

    Returns:
        The rescaled table and `(μ, λ)`.
    """
    n = len(table)
    mixed, odd = ZZ(1), ZZ(1)
    for i, j, k in itertools.product(range(n), repeat=3):
        d = QQ(table[i][j][k]).denominator()
        if parities[i] and parities[j]:
            odd = odd.lcm(d)
        else:
            mixed = mixed.lcm(d)
    mu = mixed
    # the smallest λ with λ² divisible by μ times the odd-odd denominators
    lam = ZZ(1)
    for prime, exponent in factor(mu * odd):
        lam *= prime ** ((exponent + 1) // 2)
    scale = [lam if p else mu for p in parities]
    rescaled = [
        [[table[i][j][k] * scale[i] * scale[j] / scale[k] for k in range(n)] for j in range(n)] for i in range(n)
    ]
    return rescaled, (int(mu), int(lam))

intertwiners(rho, sigma)

Return a basis of the matrices T with T ρ(x) = σ(x) T for every basis element x.

Parameters:

Name Type Description Default
rho Rep

The source representation.

required
sigma Rep

The target representation.

required

Returns:

Type Description
list[Matrix]

The intertwiners (empty when the representations share no irreducible summand).

Source code in src/veriphy/toolkit/_representation.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def intertwiners(rho: Rep, sigma: Rep) -> list[Matrix]:
    """Return a basis of the matrices `T` with `T ρ(x) = σ(x) T` for every basis element `x`.

    Args:
        rho: The source representation.
        sigma: The target representation.

    Returns:
        The intertwiners (empty when the representations share no irreducible summand).
    """
    m, n = sigma[0].nrows(), rho[0].nrows()
    equations = []
    for a, b in zip(rho, sigma, strict=True):
        # T a - b T, as a linear map on the entries of T (row-major)
        equations.append(
            a.transpose().tensor_product(identity_matrix(QQ, m)) - identity_matrix(QQ, n).tensor_product(b),
        )
    system = block_matrix_rows(equations)
    return [matrix(QQ, n, m, list(k)).transpose() for k in system.right_kernel().basis()]

intertwining_facts(t, rho, sigma, name=None)

Return facts T * ρ(x) = σ(x) * T for each basis element, and T * T⁻¹ = 1 for an invertible T.

Parameters:

Name Type Description Default
t Matrix

The intertwiner (square).

required
rho Rep

The source representation.

required
sigma Rep

The target representation.

required
name str | None

A Lean name for T; its literal when None.

None

Returns:

Type Description
Lean

The facts hi_k, and hinv when T is invertible.

Raises:

Type Description
CertificateError

If T does not intertwine.

Source code in src/veriphy/toolkit/_representation.py
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
def intertwining_facts(t: Matrix, rho: Rep, sigma: Rep, name: str | None = None) -> Lean:
    """Return facts `T * ρ(x) = σ(x) * T` for each basis element, and `T * T⁻¹ = 1` for an invertible `T`.

    Args:
        t: The intertwiner (square).
        rho: The source representation.
        sigma: The target representation.
        name: A Lean name for `T`; its literal when `None`.

    Returns:
        The facts `hi_k`, and `hinv` when `T` is invertible.

    Raises:
        CertificateError: If `T` does not intertwine.
    """
    term = _term(t, name)
    lines = []
    for k, (a, b) in enumerate(zip(rho, sigma, strict=True)):
        if t * a != b * t:
            message = f'T does not intertwine on matrix {k}'
            raise CertificateError(message)
        lines.append(
            f'have hi_{k} : {term} * {rational_matrix_term(a)} = {rational_matrix_term(b)} * {term} := {PRODUCTS}',
        )
    if t.is_square() and t.det() != 0:
        lines.append(
            f'have hinv : {term} * {rational_matrix_term(t.inverse())} = 1 := {PRODUCT}',
        )
    return tactic('\n'.join(lines))

invariant_forms(ops, *, symmetric)

Return a basis of the bilinear forms B with Tᵀ B + B T = 0 for every T, symmetric or antisymmetric.

Parameters:

Name Type Description Default
ops Sequence[Matrix]

Matrices acting on one space.

required
symmetric bool

Whether to look for symmetric (otherwise antisymmetric) forms.

required

Returns:

Type Description
list[Matrix]

The forms.

Source code in src/veriphy/toolkit/_superalgebra.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def invariant_forms(ops: Sequence[Matrix], *, symmetric: bool) -> list[Matrix]:
    """Return a basis of the bilinear forms `B` with `Tᵀ B + B T = 0` for every `T`, symmetric or antisymmetric.

    Args:
        ops: Matrices acting on one space.
        symmetric: Whether to look for symmetric (otherwise antisymmetric) forms.

    Returns:
        The forms.
    """
    d = ops[0].nrows()
    equations = []
    for t in ops:
        # the entries of Tᵀ B + B T, as linear functions of the entries of B (row-major)
        equations.append(
            t.transpose().tensor_product(identity_matrix(QQ, d)) + identity_matrix(QQ, d).tensor_product(t.transpose()),
        )
    sign = -1 if symmetric else 1
    swap = matrix(QQ, d * d, d * d, lambda r, c: 1 if c == (r % d) * d + r // d else 0)
    equations.append(identity_matrix(QQ, d * d) + sign * swap)
    system = matrix(QQ, [row for block in equations for row in block.rows()])
    return [matrix(QQ, d, d, list(b)) for b in system.right_kernel().basis()]

inverse_certificate()

Return a proof term of A⁻¹ = B for explicit matrices: the kernel checks A * B = 1.

Returns:

Type Description
Lean

The term, for exact.

Examples:

>>> print(inverse_certificate())
VeriphyCheck.inv_eq_of_check (by decide +kernel)
Source code in src/veriphy/toolkit/_lie.py
453
454
455
456
457
458
459
460
461
462
463
def inverse_certificate() -> Lean:
    """Return a proof term of `A⁻¹ = B` for explicit matrices: the kernel checks `A * B = 1`.

    Returns:
        The term, for `exact`.

    Examples:
        >>> print(inverse_certificate())
        VeriphyCheck.inv_eq_of_check (by decide +kernel)
    """
    return Lean('VeriphyCheck.inv_eq_of_check (by decide +kernel)', 'term')

ising()

Return the Ising category's fusion data, with F- and R-symbols over ℚ(ζ₁₆).

The labels and rules are SU(2) at level 2 (1, σ, ψ); F^{σσσ}_σ = (1/√2) [[1, 1], [1, -1]] and F^{σψσ}_ψ = F^{ψσψ}_σ = -1; R^{σσ}_1 = ζ₁₆⁻¹, R^{σσ}_ψ = ζ₁₆³, R^{σψ}_σ = R^{ψσ}_σ = -i, R^{ψψ}_1 = -1. (SU(2) at level 2 itself has the opposite sign of F^{σσσ}_σ and other R-symbols: a different braided category with the same fusion rules.)

Returns:

Type Description
Fusion

The data.

Examples:

>>> data = ising()
>>> pentagon_violations(data), hexagon_violations(data)
(0, 0)
Source code in src/veriphy/toolkit/_fusion.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def ising() -> Fusion:
    """Return the Ising category's fusion data, with F- and R-symbols over `ℚ(ζ₁₆)`.

    The labels and rules are SU(2) at level 2 (`1`, `σ`, `ψ`); `F^{σσσ}_σ = (1/√2) [[1, 1], [1, -1]]` and
    `F^{σψσ}_ψ = F^{ψσψ}_σ = -1`; `R^{σσ}_1 = ζ₁₆⁻¹`, `R^{σσ}_ψ = ζ₁₆³`, `R^{σψ}_σ = R^{ψσ}_σ = -i`, `R^{ψψ}_1 = -1`.
    (SU(2) at level 2 itself has the opposite sign of `F^{σσσ}_σ` and other R-symbols: a different braided category
    with the same fusion rules.)

    Returns:
        The data.

    Examples:
        >>> data = ising()
        >>> pentagon_violations(data), hexagon_violations(data)
        (0, 0)
    """
    rules = fusion_rules('A1', 2)
    z = ZETA16.gen()
    root2 = z**2 + z**-2  # √2 = ζ₈ + ζ₈⁻¹
    sigma, psi = 1, 2
    f_values: dict[Key, FieldElement] = {
        (sigma, sigma, sigma, sigma, 0, 0): 1 / root2,
        (sigma, sigma, sigma, sigma, 0, psi): 1 / root2,
        (sigma, sigma, sigma, sigma, psi, 0): 1 / root2,
        (sigma, sigma, sigma, sigma, psi, psi): -1 / root2,
        (sigma, psi, sigma, psi, sigma, sigma): ZETA16(-1),
        (psi, sigma, psi, sigma, sigma, sigma): ZETA16(-1),
    }
    r_values = {
        (sigma, sigma, 0): z**-1,
        (sigma, sigma, psi): z**3,
        (sigma, psi, sigma): z**-4,
        (psi, sigma, sigma): z**-4,
        (psi, psi, 0): ZETA16(-1),
    }
    return Fusion(['1', 'σ', 'ψ'], rules.dual, rules.rules, f_values, r_values, field='zeta16')

isolate_roots(p)

Return disjoint rational intervals (a, b), each holding exactly one simple real root of p.

Parameters:

Name Type Description Default
p Polynomial

A polynomial over ℚ.

required

Returns:

Type Description
list[tuple[QQ, QQ]]

The intervals, with p(a) and p(b) nonzero and of opposite signs, and p' without a root in [a, b].

Examples:

>>> x = PolynomialRing(QQ, 'x').gen()
>>> isolate_roots(x**3 - 2*x - 1)[-1]
(3/2, 3)
Source code in src/veriphy/toolkit/_numerics.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def isolate_roots(p: Polynomial) -> list[tuple[QQ, QQ]]:
    """Return disjoint rational intervals `(a, b)`, each holding exactly one simple real root of `p`.

    Args:
        p: A polynomial over ℚ.

    Returns:
        The intervals, with `p(a)` and `p(b)` nonzero and of opposite signs, and `p'` without a root in `[a, b]`.

    Examples:
        >>> x = PolynomialRing(QQ, 'x').gen()
        >>> isolate_roots(x**3 - 2*x - 1)[-1]
        (3/2, 3)
    """
    p = PolynomialRing(QQ, 'x')(p)
    intervals = []
    for root in p.roots(AA, multiplicities=False):
        a, b = QQ(root.interval_fast(RIF).lower().floor()) - 1, QQ(root.interval_fast(RIF).upper().ceil()) + 1
        while p(a) == 0 or p(b) == 0 or p(a) * p(b) > 0 or _critical_in(p, a, b):
            middle = (a + b) / 2
            if middle == root:
                middle = (a + middle) / 2
            a, b = (a, middle) if middle > root else (middle, b)
        intervals.append((a, b))
    return intervals

jacobi_certificate(table, name='hjacobi')

Return a fact that the bracket the structure constants define satisfies the Jacobi identity.

VeriphyCheck.jacobi_of_check reduces it, by trilinearity, to the Jacobi identity on basis triples, which the kernel checks. With antisymmetry (VeriphyCheck.bracket_self_of_check) this certifies a Lie algebra.

Parameters:

Name Type Description Default
table Table

Rational structure constants.

required
name str

The fact's name.

'hjacobi'

Returns:

Type Description
Lean

The fact ∀ x y z, ⁅x, ⁅y, z⁆⁆ + ⁅y, ⁅z, x⁆⁆ + ⁅z, ⁅x, y⁆⁆ = 0 for VeriphyCheck.scBracket.

Raises:

Type Description
CertificateError

If a structure constant is not rational.

Examples:

>>> fact = str(jacobi_certificate([[[0, 0], [0, 1]], [[0, -1], [0, 0]]]))
>>> fact.startswith('have hjacobi := VeriphyCheck.jacobi_of_check (c := (![![![0, 0], ![0, 1]], ')
True
Source code in src/veriphy/toolkit/_lie.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
def jacobi_certificate(table: Table, name: str = 'hjacobi') -> Lean:
    """Return a fact that the bracket the structure constants define satisfies the Jacobi identity.

    `VeriphyCheck.jacobi_of_check` reduces it, by trilinearity, to the Jacobi identity on basis triples, which the
    kernel checks. With antisymmetry (`VeriphyCheck.bracket_self_of_check`) this certifies a Lie algebra.

    Args:
        table: Rational structure constants.
        name: The fact's name.

    Returns:
        The fact `∀ x y z, ⁅x, ⁅y, z⁆⁆ + ⁅y, ⁅z, x⁆⁆ + ⁅z, ⁅x, y⁆⁆ = 0` for `VeriphyCheck.scBracket`.

    Raises:
        CertificateError: If a structure constant is not rational.

    Examples:
        >>> fact = str(jacobi_certificate([[[0, 0], [0, 1]], [[0, -1], [0, 0]]]))
        >>> fact.startswith('have hjacobi := VeriphyCheck.jacobi_of_check (c := (![![![0, 0], ![0, 1]], ')
        True
    """
    if not all(c in QQ for plane in table for row in plane for c in row):
        message = 'the structure constants are not rational; choose a basis with rational constants'
        raise CertificateError(message)
    table_term = structure_constants_term(table)
    return tactic(f'have {name} := VeriphyCheck.jacobi_of_check (c := {table_term}) (by decide +kernel)')

jordan_certificate(a)

Return the Jordan form J = P⁻¹ A P of a rational matrix with rational eigenvalues, with facts proving it.

A diagonalizable matrix gets a diagonal J: this is its diagonalization.

Parameters:

Name Type Description Default
a Matrix

A square rational matrix whose eigenvalues are rational.

required

Returns:

Type Description
tuple[Matrix, Matrix, Lean]

J, P and the facts hjordan : A * P = P * J and hP : P * P⁻¹ = 1.

Raises:

Type Description
CertificateError

If an eigenvalue is not rational (work over a number field instead).

Examples:

>>> j, p, facts = jordan_certificate(matrix(QQ, [[2, 1], [0, 2]]))
>>> j
[2 1]
[0 2]
Source code in src/veriphy/toolkit/_normal_form.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def jordan_certificate(a: Matrix) -> tuple[Matrix, Matrix, Lean]:
    """Return the Jordan form `J = P⁻¹ A P` of a rational matrix with rational eigenvalues, with facts proving it.

    A diagonalizable matrix gets a diagonal `J`: this is its diagonalization.

    Args:
        a: A square rational matrix whose eigenvalues are rational.

    Returns:
        `J`, `P` and the facts `hjordan : A * P = P * J` and `hP : P * P⁻¹ = 1`.

    Raises:
        CertificateError: If an eigenvalue is not rational (work over a number field instead).

    Examples:
        >>> j, p, facts = jordan_certificate(matrix(QQ, [[2, 1], [0, 2]]))
        >>> j
        [2 1]
        [0 2]
    """
    a = matrix(QQ, a)
    if not a.charpoly().is_constant() and any(f.degree() > 1 for f, _ in a.charpoly().factor()):
        message = f'the eigenvalues are not all rational (characteristic polynomial {a.charpoly().factor()})'
        raise CertificateError(message)
    j, p = a.jordan_form(transformation=True, subdivide=False)
    term_a, term_j, term_p = (rational_matrix_term(m) for m in (a, j, p))
    facts = [
        f'have hjordan : {term_a} * {term_p} = {term_p} * {term_j} := {PRODUCTS}',
        f'have hP : {term_p} * {rational_matrix_term(p.inverse())} = 1 := {PRODUCT}',
    ]
    return j, p, tactic('\n'.join(facts))

killing_form(table)

Return the Killing form's matrix, K[i][j] = tr(ad eᵢ ad eⱼ).

Parameters:

Name Type Description Default
table Table

Structure constants.

required

Returns:

Type Description
Matrix

The symmetric matrix of the form.

Examples:

>>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
>>> killing_form(structure_constants([e, f, h]))
[0 4 0]
[4 0 0]
[0 0 8]
Source code in src/veriphy/toolkit/_lie.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def killing_form(table: Table) -> Matrix:
    """Return the Killing form's matrix, `K[i][j] = tr(ad eᵢ ad eⱼ)`.

    Args:
        table: Structure constants.

    Returns:
        The symmetric matrix of the form.

    Examples:
        >>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
        >>> killing_form(structure_constants([e, f, h]))
        [0 4 0]
        [4 0 0]
        [0 0 8]
    """
    ads = adjoint_matrices(table)
    return matrix(ads[0].base_ring(), len(ads), len(ads), lambda i, j: (ads[i] * ads[j]).trace())

killing_proof(*, unfold=())

Return a proof of CoordinateGeometry.IsKilling G X for an explicit metric and vector field.

Every component of the Lie derivative is expanded (simp with derivative lemmas) and closed by ring.

Parameters:

Name Type Description Default
unfold Sequence[str]

Definitions to unfold (the metric's and the field's names).

()

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_geometry.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def killing_proof(*, unfold: Sequence[str] = ()) -> Lean:
    """Return a proof of `CoordinateGeometry.IsKilling G X` for an explicit metric and vector field.

    Every component of the Lie derivative is expanded (`simp` with derivative lemmas) and closed by `ring`.

    Args:
        unfold: Definitions to unfold (the metric's and the field's names).

    Returns:
        The tactic.
    """
    names = ', '.join(['CoordinateGeometry.lieMetric', 'CoordinateGeometry.pd', *unfold, DERIVATIVE_SIMP])
    return tactic(
        'intro a b x\n'
        'fin_cases a <;> fin_cases b <;>\n'
        f'  simp (disch := fun_prop) [{names}]\n'
        'all_goals first | ring1 | (field_simp; ring1)',
    )

ladder_combination(coefficients, xi=None, hbar=None)

Return a combination of a, a†, x and p (one coordinate) as coefficients of x and p.

Parameters:

Name Type Description Default
coefficients dict[str, object]

The coefficients of 'a' (lowering), 'a†' (raising), 'x' and 'p'.

required
xi object

The length scale ξ; the symbol xi when None.

None
hbar object

The reduced Planck constant; the symbol hbar when None.

None

Returns:

Type Description
dict[str, object]

The coefficients of 'x' and 'p'.

Raises:

Type Description
CertificateError

If a name is not one of a, a†, x, p.

Examples:

>>> ladder_combination({'a': 1, 'a†': 1})
{'x': sqrt(2)/xi, 'p': 0}
Source code in src/veriphy/toolkit/_operators.py
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
def ladder_combination(coefficients: dict[str, object], xi: object = None, hbar: object = None) -> dict[str, object]:
    """Return a combination of `a`, `a†`, `x` and `p` (one coordinate) as coefficients of `x` and `p`.

    Args:
        coefficients: The coefficients of `'a'` (lowering), `'a†'` (raising), `'x'` and `'p'`.
        xi: The length scale `ξ`; the symbol `xi` when `None`.
        hbar: The reduced Planck constant; the symbol `hbar` when `None`.

    Returns:
        The coefficients of `'x'` and `'p'`.

    Raises:
        CertificateError: If a name is not one of `a`, `a†`, `x`, `p`.

    Examples:
        >>> ladder_combination({'a': 1, 'a†': 1})
        {'x': sqrt(2)/xi, 'p': 0}
    """
    unknown = set(coefficients) - {'a', 'a†', 'x', 'p'}
    if unknown:
        message = f'unknown operators {sorted(unknown)}: combine a, a†, x and p'
        raise CertificateError(message)
    lowering, raising = ladder_operators(xi, hbar)
    parts: dict[str, dict[str, object]] = {'a': lowering, 'a†': raising, 'x': {'x': 1}, 'p': {'p': 1}}
    return {
        axis: sum((SR(c) * SR(parts[name].get(axis, 0)) for name, c in coefficients.items()), SR(0)).simplify_full()
        for axis in ('x', 'p')
    }

ladder_commutator_proof(index='i', oscillator='Q')

Return a proof of ⁅A, B⁆ = c • ContinuousLinearMap.id ℂ 𝓢(Space d, ℂ) for Physlib's ladder operators.

A and B are combinations of Q.loweringCLM i, Q.raisingCLM i, 𝐱 i and 𝐩 i. The proof unfolds the ladder operators, expands the bracket by bilinearity with Physlib's commutation lemmas, and closes the scalar identity by field_simp and ring, with i² = -1 and (√2)² = 2. The goal's file must make the bracket a Lie ring, as Physlib does: attribute [local instance 100] LieRing.ofAssociativeRing.

Parameters:

Name Type Description Default
index str

The coordinate index of the goal.

'i'
oscillator str

The HarmonicOscillator of the goal.

'Q'

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_operators.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
def ladder_commutator_proof(index: str = 'i', oscillator: str = 'Q') -> Lean:
    """Return a proof of `⁅A, B⁆ = c • ContinuousLinearMap.id ℂ 𝓢(Space d, ℂ)` for Physlib's ladder operators.

    `A` and `B` are combinations of `Q.loweringCLM i`, `Q.raisingCLM i`, `𝐱 i` and `𝐩 i`. The proof unfolds the
    ladder operators, expands the bracket by bilinearity with Physlib's commutation lemmas, and closes the scalar
    identity by `field_simp` and `ring`, with `i² = -1` and `(√2)² = 2`. The goal's file must make the bracket a
    Lie ring, as Physlib does: `attribute [local instance 100] LieRing.ofAssociativeRing`.

    Args:
        index: The coordinate index of the goal.
        oscillator: The `HarmonicOscillator` of the goal.

    Returns:
        The tactic.
    """
    h = 'QuantumMechanics.HarmonicOscillator'
    unfold = (
        f'{h}.loweringCLM, {h}.raisingCLM, lie_smul, smul_lie, add_lie, lie_add, sub_lie, lie_sub, '
        'QuantumMechanics.position_commutation_position, QuantumMechanics.momentum_commutation_momentum, '
        f'QuantumMechanics.position_commutation_momentum, ← lie_skew (QuantumMechanics.momentumCLM {index}) '
        f'(QuantumMechanics.positionCLM {index}), smul_zero, zero_add, add_zero, sub_zero, zero_sub, smul_neg, '
        'neg_neg, smul_smul, KroneckerDelta.eq_one_of_same, one_smul'
    )
    apply = 'smul_apply, neg_apply, add_apply, sub_apply, ContinuousLinearMap.id_apply, zero_apply, smul_eq_mul'
    close = f'Complex.I_sq, inv_pow, {h}.sqrt_two_sq_ofReal'
    return tactic(
        f'simp only [{unfold}]\n'
        'ext ψ x\n'
        f'simp only [{apply}]\n'
        f'have hξ := {oscillator}.ξ_ofReal_ne_zero {index}\n'
        'have hℏ := Constants.ℏ_ofReal_ne_zero\n'
        'have h2 : ((Real.sqrt 2 : ℝ) : ℂ) ≠ 0 := by norm_num\n'
        'field_simp\n'
        f'all_goals first | ring1 | (ring_nf; simp only [{close}]; (try ring_nf); done) | '
        '(ring_nf; simp only [Complex.I_sq]; ring1)',
    )

ladder_matrices(levels)

Return the annihilation and creation operators truncated to levels states, over Sage's symbolic ring.

a |n⟩ = √n |n - 1⟩; on the truncated space [a, a†] = 1 - levels · |levels - 1⟩⟨levels - 1|.

Parameters:

Name Type Description Default
levels int

The number of states.

required

Returns:

Type Description
tuple[Matrix, Matrix]

a and a†.

Examples:

>>> a, ad = ladder_matrices(3)
>>> (a * ad - ad * a).diagonal()
[1, 1, -2]
Source code in src/veriphy/toolkit/_operators.py
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
def ladder_matrices(levels: int) -> tuple[Matrix, Matrix]:
    """Return the annihilation and creation operators truncated to `levels` states, over Sage's symbolic ring.

    `a |n⟩ = √n |n - 1⟩`; on the truncated space `[a, a†] = 1 - levels · |levels - 1⟩⟨levels - 1|`.

    Args:
        levels: The number of states.

    Returns:
        `a` and `a†`.

    Examples:
        >>> a, ad = ladder_matrices(3)
        >>> (a * ad - ad * a).diagonal()
        [1, 1, -2]
    """
    a = matrix(SR, levels, levels)
    for n in range(1, levels):
        a[n - 1, n] = SR(n).sqrt()
    return a, a.transpose()

ladder_operators(xi=None, hbar=None)

Return the lowering and raising operators as combinations of one coordinate's position and momentum.

a = (x/ξ + i ξ p/ℏ)/√2 and a† = (x/ξ - i ξ p/ℏ)/√2, Physlib's convention for the harmonic oscillator.

Parameters:

Name Type Description Default
xi object

The length scale ξ; the symbol xi when None.

None
hbar object

The reduced Planck constant; the symbol hbar when None.

None

Returns:

Type Description
tuple[dict[str, object], dict[str, object]]

The coefficients of 'x' and 'p' in a and in a†.

Examples:

>>> lowering, raising = ladder_operators()
>>> lowering['p']
1/2*I*sqrt(2)*xi/hbar
Source code in src/veriphy/toolkit/_operators.py
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
def ladder_operators(xi: object = None, hbar: object = None) -> tuple[dict[str, object], dict[str, object]]:
    """Return the lowering and raising operators as combinations of one coordinate's position and momentum.

    `a = (x/ξ + i ξ p/ℏ)/√2` and `a† = (x/ξ - i ξ p/ℏ)/√2`, Physlib's convention for the harmonic oscillator.

    Args:
        xi: The length scale `ξ`; the symbol `xi` when `None`.
        hbar: The reduced Planck constant; the symbol `hbar` when `None`.

    Returns:
        The coefficients of `'x'` and `'p'` in `a` and in `a†`.

    Examples:
        >>> lowering, raising = ladder_operators()
        >>> lowering['p']
        1/2*I*sqrt(2)*xi/hbar
    """
    xi = var('xi') if xi is None else xi
    hbar = var('hbar') if hbar is None else hbar
    scale = 1 / SR(2).sqrt()
    lowering = {'x': (scale / xi).simplify_full(), 'p': (scale * SR.I() * xi / hbar).simplify_full()}
    raising = {'x': lowering['x'], 'p': -lowering['p']}
    return lowering, raising

lemma_rewrite(lemma, problem, *, reverse=False, side=(), proofs=None, at=None)

Rewrite with a named mathlib lemma, passing its side conditions as arguments.

Each side condition is the Lean text of a proposition. It is passed as the hypothesis or domain fact that states it, or as (by <proof>) from proofs.

Parameters:

Name Type Description Default
lemma str

The lemma name, such as Real.exp_add.

required
problem Problem

The problem.

required
reverse bool

Rewrite right to left (← lemma).

False
side Sequence[str]

The lemma's side conditions, in argument order.

()
proofs Mapping[str, Lean | str] | None

A tactic proving a side condition no hypothesis states, by its text.

None
at str | None

A hypothesis to rewrite instead of the goal.

None

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If a side condition is neither stated nor supplied, naming it.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['u'], 'goal': {'kind': 'false'},
...     'hyps': [{'name': 'hu', 'poly': '-x0', 'rel': 'le', 'text': '0 ≤ u'}]})
>>> str(lemma_rewrite('Real.exp_add', prob, reverse=True))
'rw [← Real.exp_add]'
>>> str(lemma_rewrite('Real.sqrt_mul_self', prob, side=['0 ≤ u'], at='h'))
'rw [Real.sqrt_mul_self hu] at h'
>>> lemma_rewrite('Real.log_exp', prob, side=['0 < u'])
Traceback (most recent call last):
...
veriphy.core._errors.CertificateError: side condition '0 < u' of Real.log_exp is not stated; pass its proof
Source code in src/veriphy/toolkit/_symbolic_emit.py
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
def lemma_rewrite(
    lemma: str,
    problem: Problem,
    *,
    reverse: bool = False,
    side: Sequence[str] = (),
    proofs: Mapping[str, Lean | str] | None = None,
    at: str | None = None,
) -> Lean:
    """Rewrite with a named mathlib lemma, passing its side conditions as arguments.

    Each side condition is the Lean text of a proposition. It is passed as the hypothesis or domain fact that states
    it, or as `(by <proof>)` from `proofs`.

    Args:
        lemma: The lemma name, such as `Real.exp_add`.
        problem: The problem.
        reverse: Rewrite right to left (`← lemma`).
        side: The lemma's side conditions, in argument order.
        proofs: A tactic proving a side condition no hypothesis states, by its text.
        at: A hypothesis to rewrite instead of the goal.

    Returns:
        The tactic.

    Raises:
        CertificateError: If a side condition is neither stated nor supplied, naming it.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['u'], 'goal': {'kind': 'false'},
        ...     'hyps': [{'name': 'hu', 'poly': '-x0', 'rel': 'le', 'text': '0 ≤ u'}]})
        >>> str(lemma_rewrite('Real.exp_add', prob, reverse=True))
        'rw [← Real.exp_add]'
        >>> str(lemma_rewrite('Real.sqrt_mul_self', prob, side=['0 ≤ u'], at='h'))
        'rw [Real.sqrt_mul_self hu] at h'
        >>> lemma_rewrite('Real.log_exp', prob, side=['0 < u'])
        Traceback (most recent call last):
        ...
        veriphy.core._errors.CertificateError: side condition '0 < u' of Real.log_exp is not stated; pass its proof
    """
    proofs = proofs or {}
    stated = {h.text: h.name for h in problem.hyps if h.name}
    stated.update({c.text: c.discharged_by for c in problem.domain if c.discharged_by})
    args = []
    for condition in side:
        if condition in stated:
            args.append(str(stated[condition]))
        elif condition in proofs:
            args.append(f'(by {proofs[condition]})')
        else:
            message = f"side condition '{condition}' of {lemma} is not stated; pass its proof"
            raise CertificateError(message)
    rule = ' '.join([f'{"← " if reverse else ""}{lemma}', *args])
    return tactic(f'rw [{rule}]' + (f' at {at}' if at else ''))

lift(x)

Return the representative of a number-field element as a polynomial over ℚ in x.

Parameters:

Name Type Description Default
x NumberFieldElement | int

An element of a number field with one generator, or a rational.

required

Returns:

Type Description
Polynomial

The polynomial of degree below the field's degree.

Examples:

>>> from sage.all import QuadraticField
>>> lift(QuadraticField(2, 's').gen() / 2 + 1)
1/2*x + 1
Source code in src/veriphy/toolkit/_number_field.py
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def lift(x: NumberFieldElement | int) -> Polynomial:
    """Return the representative of a number-field element as a polynomial over ℚ in `x`.

    Args:
        x: An element of a number field with one generator, or a rational.

    Returns:
        The polynomial of degree below the field's degree.

    Examples:
        >>> from sage.all import QuadraticField
        >>> lift(QuadraticField(2, 's').gen() / 2 + 1)
        1/2*x + 1
    """
    if isinstance(x, NumberFieldElement):
        return X.parent()(list(x.polynomial()))
    return X.parent()(QQ(x))

linear_combination(weighted, problem)

Close an equality or inequality goal by linear_combination of weighted proofs.

Parameters:

Name Type Description Default
weighted Sequence[tuple[object, Lean]]

Pairs of a weight and a proof term, as for combination.

required
problem Problem

The problem.

required

Returns:

Type Description
Lean

The tactic.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'goal': {'kind': 'false'}})
>>> str(linear_combination([('x0 + 3', term('h')), (-2, term("h'"))], prob))
"linear_combination (a + 3) * h + (-2) * h'"
Source code in src/veriphy/toolkit/_emit.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def linear_combination(weighted: Sequence[tuple[object, Lean]], problem: Problem) -> Lean:
    """Close an equality or inequality goal by `linear_combination` of weighted proofs.

    Args:
        weighted: Pairs of a weight and a proof term, as for `combination`.
        problem: The problem.

    Returns:
        The tactic.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['a'], 'goal': {'kind': 'false'}})
        >>> str(linear_combination([('x0 + 3', term('h')), (-2, term("h'"))], prob))
        "linear_combination (a + 3) * h + (-2) * h'"
    """
    return tactic(f'linear_combination {combination(weighted, problem)}')

load_core_table() cached

Return the core (mathlib) table shipped with the toolkit.

Returns:

Type Description
Table

The core table.

Examples:

>>> core = load_core_table()
>>> core.namespace, [f.lean for f in core.functions if f.sage == 'exp']
('mathlib', ['Real.exp', 'Complex.exp'])
Source code in src/veriphy/toolkit/_table.py
284
285
286
287
288
289
290
291
292
293
294
295
296
297
@cache
def load_core_table() -> Table:
    """Return the core (`mathlib`) table shipped with the toolkit.

    Returns:
        The core table.

    Examples:
        >>> core = load_core_table()
        >>> core.namespace, [f.lean for f in core.functions if f.sage == 'exp']
        ('mathlib', ['Real.exp', 'Complex.exp'])
    """
    text = files('veriphy.toolkit').joinpath(CORE_TABLE_FILE).read_text(encoding='utf-8')
    return parse_table(json.loads(text), CORE_TABLE_FILE)

load_pack(name) cached

Return a library pack shipped with the toolkit, by its namespace.

Parameters:

Name Type Description Default
name str

The pack's namespace, such as physlib.

required

Returns:

Type Description
Table

The pack's table.

Raises:

Type Description
TableError

If no pack has that name.

Examples:

>>> physlib = load_pack('physlib')
>>> [f.lean for f in physlib.functions], dict(physlib.conventions)['metric_signature']
(['Time.deriv', 'Space.deriv'], '+---')
Source code in src/veriphy/toolkit/_table.py
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
@cache
def load_pack(name: str) -> Table:
    """Return a library pack shipped with the toolkit, by its namespace.

    Args:
        name: The pack's namespace, such as `physlib`.

    Returns:
        The pack's table.

    Raises:
        TableError: If no pack has that name.

    Examples:
        >>> physlib = load_pack('physlib')
        >>> [f.lean for f in physlib.functions], dict(physlib.conventions)['metric_signature']
        (['Time.deriv', 'Space.deriv'], '+---')
    """
    source = f'{PACKS_DIR}/{name}.json'
    resource = files('veriphy.toolkit').joinpath(PACKS_DIR, f'{name}.json')
    if not resource.is_file():
        raise TableError(source, 'namespace', 'no such pack')
    return parse_table(json.loads(resource.read_text(encoding='utf-8')), source)

matrix_goal_proof(kind, size, carrier, entrywise_limit=ENTRYWISE_LIMIT)

Return a proof of a matrix goal stated with explicit matrices: entrywise when small, by a checker when large.

The checker reads the goal's own matrices, so it needs no data from the script. Kernel evaluation needs decidable equality of entries, so goals over ℝ or ℂ always get the entrywise proof.

Parameters:

Name Type Description Default
kind str

The goal's shape: mul (A * B = C), bracket (⁅A, B⁆ = C), commute (A * B = B * A), trace (A.trace = c) or eq (A = B).

required
size int

The matrices' dimension.

required
carrier str

The type of the entries.

required
entrywise_limit int

The largest dimension proved entry by entry.

ENTRYWISE_LIMIT

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the kind is unknown.

Examples:

>>> print(matrix_goal_proof('mul', 2, 'ℚ'))
ext i j
fin_cases i <;> fin_cases j <;> norm_num
>>> print(matrix_goal_proof('commute', 12, 'ℤ'))
exact VeriphyCheck.commute_of_check (by decide +kernel)
>>> print(matrix_goal_proof('commute', 12, 'ℝ'))
ext i j
fin_cases i <;> fin_cases j <;> norm_num
Source code in src/veriphy/toolkit/_reflect.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def matrix_goal_proof(kind: str, size: int, carrier: str, entrywise_limit: int = ENTRYWISE_LIMIT) -> Lean:
    """Return a proof of a matrix goal stated with explicit matrices: entrywise when small, by a checker when large.

    The checker reads the goal's own matrices, so it needs no data from the script. Kernel evaluation needs decidable
    equality of entries, so goals over ℝ or ℂ always get the entrywise proof.

    Args:
        kind: The goal's shape: `mul` (`A * B = C`), `bracket` (`⁅A, B⁆ = C`), `commute` (`A * B = B * A`), `trace`
            (`A.trace = c`) or `eq` (`A = B`).
        size: The matrices' dimension.
        carrier: The type of the entries.
        entrywise_limit: The largest dimension proved entry by entry.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the kind is unknown.

    Examples:
        >>> print(matrix_goal_proof('mul', 2, 'ℚ'))
        ext i j
        fin_cases i <;> fin_cases j <;> norm_num
        >>> print(matrix_goal_proof('commute', 12, 'ℤ'))
        exact VeriphyCheck.commute_of_check (by decide +kernel)
        >>> print(matrix_goal_proof('commute', 12, 'ℝ'))
        ext i j
        fin_cases i <;> fin_cases j <;> norm_num
    """
    if kind not in KINDS:
        message = f'unknown matrix goal kind {kind!r}; use one of {list(KINDS)}'
        raise CertificateError(message)
    if size <= entrywise_limit or carrier not in DECIDABLE_CARRIERS:
        return tactic(ENTRYWISE)
    return tactic(f'exact {CHECK_LEMMAS[kind]} (by decide +kernel)')

matrix_identity_proof()

Return the entrywise proof of an identity between explicit matrices over ℝ or ℂ (√k, Complex.I entries).

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_operators.py
371
372
373
374
375
376
377
def matrix_identity_proof() -> Lean:
    """Return the entrywise proof of an identity between explicit matrices over ℝ or ℂ (`√k`, `Complex.I` entries).

    Returns:
        The tactic.
    """
    return tactic(ENTRYWISE)

matrix_term(m, problem, index=None)

Render a Sage matrix as a Lean !![…] literal over the problem's atoms, or by blocks for a sum index.

Parameters:

Name Type Description Default
m Matrix

A Sage matrix with entries Sage's symbolic ring converts.

required
problem Problem

The problem whose atoms name the variables.

required
index MatrixIndex | None

The index type of a matrix indexed by Fin a ⊕ Fin b (from problem.matrix_indices): the matrix is rendered as Matrix.fromBlocks of its blocks, a term of that type.

None

Returns:

Type Description
Lean

The term.

Raises:

Type Description
CertificateError

If the matrix's size does not match the index type.

Examples:

>>> from veriphy.toolkit import read_problem
>>> from sage.all import matrix
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['c'], 'goal': {'kind': 'false'}})
>>> str(matrix_term(matrix(SR, [[1, prob.symbols['x0']], [0, 1/2]]), prob))
'!![1, c; 0, 1 / 2]'
>>> sum_index = MatrixIndex((1, 2), (1, 2), 'Matrix (Fin 1 ⊕ Fin 2) (Fin 1 ⊕ Fin 2) ℚ')
>>> str(matrix_term(matrix(SR, [[1, 0, 0], [0, -1, 0], [0, 0, -1]]), prob, sum_index))
'(Matrix.fromBlocks !![1] !![0, 0] !![0; 0] !![-1, 0; 0, -1] : Matrix (Fin 1 ⊕ Fin 2) (Fin 1 ⊕ Fin 2) ℚ)'
Source code in src/veriphy/toolkit/_symbolic_emit.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
def matrix_term(m: Matrix, problem: Problem, index: MatrixIndex | None = None) -> Lean:
    """Render a Sage matrix as a Lean `!![…]` literal over the problem's atoms, or by blocks for a sum index.

    Args:
        m: A Sage matrix with entries Sage's symbolic ring converts.
        problem: The problem whose atoms name the variables.
        index: The index type of a matrix indexed by `Fin a ⊕ Fin b` (from `problem.matrix_indices`): the matrix is
            rendered as `Matrix.fromBlocks` of its blocks, a term of that type.

    Returns:
        The term.

    Raises:
        CertificateError: If the matrix's size does not match the index type.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> from sage.all import matrix
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['c'], 'goal': {'kind': 'false'}})
        >>> str(matrix_term(matrix(SR, [[1, prob.symbols['x0']], [0, 1/2]]), prob))
        '!![1, c; 0, 1 / 2]'
        >>> sum_index = MatrixIndex((1, 2), (1, 2), 'Matrix (Fin 1 ⊕ Fin 2) (Fin 1 ⊕ Fin 2) ℚ')
        >>> str(matrix_term(matrix(SR, [[1, 0, 0], [0, -1, 0], [0, 0, -1]]), prob, sum_index))
        '(Matrix.fromBlocks !![1] !![0, 0] !![0; 0] !![-1, 0; 0, -1] : Matrix (Fin 1 ⊕ Fin 2) (Fin 1 ⊕ Fin 2) ℚ)'
    """
    ctx = _Context(problem, '')

    def literal(block: Matrix) -> str:
        """Return the `!![…]` literal of a block."""
        rows = [', '.join(_render(SR(entry), ctx) for entry in row) for row in block.rows()]
        return '!![' + '; '.join(rows) + ']'

    if index is None:
        return term(literal(m))
    two_blocks = len(index.rows) == len(index.cols) == len(('inl', 'inr'))
    if not two_blocks or (m.nrows(), m.ncols()) != (sum(index.rows), sum(index.cols)):
        message = f'a {m.nrows()} by {m.ncols()} matrix does not split into the blocks of {index.text}'
        raise CertificateError(message)
    (r, _), (c, _) = index.rows, index.cols
    blocks = [m[:r, :c], m[:r, c:], m[r:, :c], m[r:, c:]]
    return term(f'(Matrix.fromBlocks {" ".join(literal(b) for b in blocks)} : {index.text})')

metric_term(g)

Return a metric's components as a Lean Fin n → Fin n → (Fin n → ℝ) → ℝ.

Parameters:

Name Type Description Default
g Matrix

The metric's matrix of expressions in the coordinates.

required

Returns:

Type Description
str

The term.

Examples:

>>> from sage.all import sin
>>> x0, x1 = coordinates(2)
>>> term = metric_term(matrix(SR, [[1, 0], [0, sin(x0)**2]]))
>>> 'fun x => ((Real.sin (x 0)) ^ 2 : ℝ)' in term, term.endswith(': Fin 2 → Fin 2 → (Fin 2 → ℝ) → ℝ)')
(True, True)
Source code in src/veriphy/toolkit/_geometry.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def metric_term(g: Matrix) -> str:
    """Return a metric's components as a Lean `Fin n → Fin n → (Fin n → ℝ) → ℝ`.

    Args:
        g: The metric's matrix of expressions in the coordinates.

    Returns:
        The term.

    Examples:
        >>> from sage.all import sin
        >>> x0, x1 = coordinates(2)
        >>> term = metric_term(matrix(SR, [[1, 0], [0, sin(x0)**2]]))
        >>> 'fun x => ((Real.sin (x 0)) ^ 2 : ℝ)' in term, term.endswith(': Fin 2 → Fin 2 → (Fin 2 → ℝ) → ℝ)')
        (True, True)
    """
    n = g.nrows()
    rows = ', '.join('![' + ', '.join(f'fun x => {_render(e, n)}' for e in row) + ']' for row in g.rows())
    return f'(![{rows}] : Fin {n} → Fin {n} → (Fin {n} → ℝ) → ℝ)'

moment_coefficients(even_ops, factors, omega)

Return a basis of the coefficients kₛ for which the moment-map bracket satisfies super-Jacobi on V.

Parameters:

Name Type Description Default
even_ops Sequence[Matrix]

A basis of g₀, as matrices acting faithfully on V.

required
factors Sequence[int]

The simple factor of each basis element.

required
omega Matrix

An invariant antisymmetric form on V.

required

Returns:

Type Description
list[list[RingElement]]

The solutions (one for G(3) and F(4), two for the family D(2,1;α)).

Source code in src/veriphy/toolkit/_superalgebra.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
def moment_coefficients(even_ops: Sequence[Matrix], factors: Sequence[int], omega: Matrix) -> list[list[RingElement]]:
    """Return a basis of the coefficients `kₛ` for which the moment-map bracket satisfies super-Jacobi on `V`.

    Args:
        even_ops: A basis of `g₀`, as matrices acting faithfully on `V`.
        factors: The simple factor of each basis element.
        omega: An invariant antisymmetric form on `V`.

    Returns:
        The solutions (one for `G(3)` and `F(4)`, two for the family `D(2,1;α)`).
    """
    moments = _moment_maps(even_ops, factors, omega)
    d, n0 = omega.nrows(), len(even_ops)
    rows = []
    for i, j, k in itertools.product(range(d), repeat=3):
        for m in range(d):
            # Σ_cyclic [vᵢ, [vⱼ, vₖ]] at vₘ, for each factor's moment map: [vᵢ, T] = -T vᵢ
            row = []
            for mu in moments:
                total = 0
                for x, y, z in ((i, j, k), (j, k, i), (k, i, j)):
                    total -= sum(mu[y][z][a] * even_ops[a][m, x] for a in range(n0))
                row.append(total)
            rows.append(row)
    return [list(b) for b in matrix(QQ, rows).right_kernel().basis()]

mul_nonneg(left, right)

Build the proof term mul_nonneg left right from two nonnegativity proofs.

Parameters:

Name Type Description Default
left Lean

A proof of 0 ≤ a.

required
right Lean

A proof of 0 ≤ b.

required

Returns:

Type Description
Lean

A proof of 0 ≤ a * b.

Examples:

>>> str(mul_nonneg(term('ha'), term('sq_nonneg b')))
'mul_nonneg ha (sq_nonneg b)'
Source code in src/veriphy/toolkit/_emit.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def mul_nonneg(left: Lean, right: Lean) -> Lean:
    """Build the proof term `mul_nonneg left right` from two nonnegativity proofs.

    Args:
        left: A proof of `0 ≤ a`.
        right: A proof of `0 ≤ b`.

    Returns:
        A proof of `0 ≤ a * b`.

    Examples:
        >>> str(mul_nonneg(term('ha'), term('sq_nonneg b')))
        'mul_nonneg ha (sq_nonneg b)'
    """
    return term(f'mul_nonneg {parenthesize(left.text)} {parenthesize(right.text)}')

mul_pos(left, right)

Build the proof term mul_pos left right from two positivity proofs.

Parameters:

Name Type Description Default
left Lean

A proof of 0 < a.

required
right Lean

A proof of 0 < b.

required

Returns:

Type Description
Lean

A proof of 0 < a * b.

Examples:

>>> str(mul_pos(term('ha'), term('hb')))
'mul_pos ha hb'
Source code in src/veriphy/toolkit/_emit.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def mul_pos(left: Lean, right: Lean) -> Lean:
    """Build the proof term `mul_pos left right` from two positivity proofs.

    Args:
        left: A proof of `0 < a`.
        right: A proof of `0 < b`.

    Returns:
        A proof of `0 < a * b`.

    Examples:
        >>> str(mul_pos(term('ha'), term('hb')))
        'mul_pos ha hb'
    """
    return term(f'mul_pos {parenthesize(left.text)} {parenthesize(right.text)}')

natural_iso_facts(t, name=None)

Return facts T * U = 1 and U * T = 1 for an invertible natural transformation and its inverse U.

With the naturality fact they give VeriphyCheck.repIso, a natural isomorphism.

Parameters:

Name Type Description Default
t Matrix

The transformation (square, invertible, rational).

required
name str | None

A Lean name for T; its literal when None.

None

Returns:

Type Description
Lean

The facts hTU and hUT.

Raises:

Type Description
CertificateError

If T is not invertible.

Source code in src/veriphy/toolkit/_representation.py
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
def natural_iso_facts(t: Matrix, name: str | None = None) -> Lean:
    """Return facts `T * U = 1` and `U * T = 1` for an invertible natural transformation and its inverse `U`.

    With the naturality fact they give `VeriphyCheck.repIso`, a natural isomorphism.

    Args:
        t: The transformation (square, invertible, rational).
        name: A Lean name for `T`; its literal when `None`.

    Returns:
        The facts `hTU` and `hUT`.

    Raises:
        CertificateError: If `T` is not invertible.
    """
    if not (t.is_square() and t.det() != 0):
        message = 'the transformation is not invertible: no natural isomorphism'
        raise CertificateError(message)
    term = _term(t, name)
    inverse = rational_matrix_term(t.inverse())
    return tactic(
        f'have hTU : {term} * {inverse} = 1 := {PRODUCT}\nhave hUT : {inverse} * {term} = 1 := {PRODUCT}',
    )

naturality_proof(t, rho_generators, sigma_generators, *, rho, sigma, closure)

Return a proof of ∀ g, T * ρ g = σ g * T: naturality between two group representations as functors.

A representation of G is a functor from its one-object category, and T is natural when every square T ρ(g) = σ(g) T commutes. Sage checks the squares for the generators; the kernel checks each again (decide +kernel, unfolding ρ and σ at the generator), and VeriphyCheck.intertwines_of_closure extends them to the whole group from the goal's closure fact. VeriphyCheck.repHom (or repIso, given natural_iso_facts) packages the result as a morphism of Rep.

Parameters:

Name Type Description Default
t Matrix

The candidate natural transformation.

required
rho_generators Sequence[Matrix]

ρ at the generators, in the order of the goal's generating set.

required
sigma_generators Sequence[Matrix]

σ at the generators.

required
rho str

The Lean name of ρ : G →* Matrix n n R.

required
sigma str

The Lean name of σ.

required
closure str

The name of the hypothesis Subgroup.closure {g₁, …} = ⊤.

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If a square fails, naming the generator.

Examples:

>>> swap = matrix(QQ, [[0, 1, 0], [1, 0, 0], [0, 0, 1]])
>>> cycle = matrix(QQ, [[0, 0, 1], [1, 0, 0], [0, 1, 0]])
>>> one = identity_matrix(QQ, 1)
>>> print(naturality_proof(matrix(QQ, [[1, 1, 1]]), [cycle, swap], [one, one], rho='perm', sigma='triv',
...     closure='hS'))
refine VeriphyCheck.intertwines_of_closure perm triv _ hS ?_
rintro s (rfl | rfl)
· decide +kernel
· decide +kernel
Source code in src/veriphy/toolkit/_representation.py
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
def naturality_proof(
    t: Matrix,
    rho_generators: Sequence[Matrix],
    sigma_generators: Sequence[Matrix],
    *,
    rho: str,
    sigma: str,
    closure: str,
) -> Lean:
    """Return a proof of `∀ g, T * ρ g = σ g * T`: naturality between two group representations as functors.

    A representation of `G` is a functor from its one-object category, and `T` is natural when every square
    `T ρ(g) = σ(g) T` commutes. Sage checks the squares for the generators; the kernel checks each again
    (`decide +kernel`, unfolding `ρ` and `σ` at the generator), and `VeriphyCheck.intertwines_of_closure` extends them
    to the whole group from the goal's closure fact. `VeriphyCheck.repHom` (or `repIso`, given `natural_iso_facts`)
    packages the result as a morphism of `Rep`.

    Args:
        t: The candidate natural transformation.
        rho_generators: `ρ` at the generators, in the order of the goal's generating set.
        sigma_generators: `σ` at the generators.
        rho: The Lean name of `ρ : G →* Matrix n n R`.
        sigma: The Lean name of `σ`.
        closure: The name of the hypothesis `Subgroup.closure {g₁, …} = ⊤`.

    Returns:
        The tactic.

    Raises:
        CertificateError: If a square fails, naming the generator.

    Examples:
        >>> swap = matrix(QQ, [[0, 1, 0], [1, 0, 0], [0, 0, 1]])
        >>> cycle = matrix(QQ, [[0, 0, 1], [1, 0, 0], [0, 1, 0]])
        >>> one = identity_matrix(QQ, 1)
        >>> print(naturality_proof(matrix(QQ, [[1, 1, 1]]), [cycle, swap], [one, one], rho='perm', sigma='triv',
        ...     closure='hS'))
        refine VeriphyCheck.intertwines_of_closure perm triv _ hS ?_
        rintro s (rfl | rfl)
        · decide +kernel
        · decide +kernel
    """
    for k, (a, b) in enumerate(zip(rho_generators, sigma_generators, strict=True)):
        if t * a != b * t:
            message = f'the naturality square fails at generator {k}: T ρ(g{k}) ≠ σ(g{k}) T'
            raise CertificateError(message)
    count = len(rho_generators)
    pattern = 'rfl' if count == 1 else '(' + ' | '.join(['rfl'] * count) + ')'
    lines = [
        f'refine VeriphyCheck.intertwines_of_closure {rho} {sigma} _ {closure} ?_',
        f'rintro s {pattern}',
        *(['· decide +kernel'] * count),
    ]
    return tactic('\n'.join(lines))

negate_goal(name)

Turn a ≤/< goal into False with its negation as hypothesis name.

Parameters:

Name Type Description Default
name str

The name of the negated goal (use fresh_name).

required

Returns:

Type Description
Lean

The tactic block.

Examples:

>>> print(negate_goal('hneg'))
by_contra hneg
push Not at hneg
Source code in src/veriphy/toolkit/_emit.py
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
def negate_goal(name: str) -> Lean:
    """Turn a `≤`/`<` goal into `False` with its negation as hypothesis `name`.

    Args:
        name: The name of the negated goal (use `fresh_name`).

    Returns:
        The tactic block.

    Examples:
        >>> print(negate_goal('hneg'))
        by_contra hneg
        push Not at hneg
    """
    return steps(tactic(f'by_contra {name}'), tactic(f'push Not at {name}'))

nilpotency_index(n)

Return the least k with Nᵏ = 0, or None when N is not nilpotent.

Parameters:

Name Type Description Default
n Matrix

A square matrix.

required

Returns:

Type Description
int | None

The index.

Examples:

>>> nilpotency_index(matrix(QQ, [[0, 1, 2], [0, 0, 3], [0, 0, 0]]))
3
Source code in src/veriphy/toolkit/_exponential.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def nilpotency_index(n: Matrix) -> int | None:
    """Return the least `k` with `Nᵏ = 0`, or `None` when `N` is not nilpotent.

    Args:
        n: A square matrix.

    Returns:
        The index.

    Examples:
        >>> nilpotency_index(matrix(QQ, [[0, 1, 2], [0, 0, 3], [0, 0, 0]]))
        3
    """
    power = matrix.identity(n.base_ring(), n.nrows())
    for k in range(1, n.nrows() + 1):
        power = power * n
        if power == 0:
            return k
    return None

nilpotent_exponential(n)

Return exp N for a nilpotent rational matrix, and a proof of exp N = E for the goal's N and E.

Parameters:

Name Type Description Default
n Matrix

The nilpotent matrix.

required

Returns:

Type Description
tuple[Matrix, Lean]

E = Σ_{k<index} Nᵏ / k! and the tactic, which first proves N ^ index = 0.

Raises:

Type Description
CertificateError

If N is not nilpotent.

Examples:

>>> e, proof = nilpotent_exponential(matrix(QQ, [[0, 1, 2], [0, 0, 3], [0, 0, 0]]))
>>> e
[  1   1 7/2]
[  0   1   3]
[  0   0   1]
Source code in src/veriphy/toolkit/_exponential.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def nilpotent_exponential(n: Matrix) -> tuple[Matrix, Lean]:
    """Return `exp N` for a nilpotent rational matrix, and a proof of `exp N = E` for the goal's `N` and `E`.

    Args:
        n: The nilpotent matrix.

    Returns:
        `E = Σ_{k<index} Nᵏ / k!` and the tactic, which first proves `N ^ index = 0`.

    Raises:
        CertificateError: If `N` is not nilpotent.

    Examples:
        >>> e, proof = nilpotent_exponential(matrix(QQ, [[0, 1, 2], [0, 0, 3], [0, 0, 0]]))
        >>> e
        [  1   1 7/2]
        [  0   1   3]
        [  0   0   1]
    """
    index = nilpotency_index(n)
    if index is None:
        message = 'the matrix is not nilpotent; use diagonalizable_exponential or a Jordan decomposition'
        raise CertificateError(message)
    e = sum((n**k / factorial(k) for k in range(index)), 0 * n)
    lines = [
        f'have hN : {_literal(n)} ^ {index} = 0 := by',
        f'  ext i j\n  fin_cases i <;> fin_cases j <;> {ENTRY_SIMP.replace("simp [", "simp [pow_succ, ")}',
        'rw [NormedSpace.exp_eq_tsum_rat]',
        'beta_reduce',
        f'rw [tsum_eq_sum (s := Finset.range {index}) (fun n hn => by',
        '  simp only [Finset.mem_range, not_lt] at hn',
        '  rw [pow_eq_zero_of_le hn hN, smul_zero])]',
        'ext i j\nfin_cases i <;> fin_cases j <;>',
        f'  {ENTRY_SIMP.replace("simp [", "simp [Finset.sum_range_succ, pow_succ, Rat.smul_def, ")} <;> norm_num',
    ]
    return e, tactic('\n'.join(lines))

nlinarith(hints=())

Call nlinarith with hint terms (products and squares that Sage found).

Parameters:

Name Type Description Default
hints Sequence[Lean]

Proof terms added to nlinarith's atom set.

()

Returns:

Type Description
Lean

The tactic.

Examples:

>>> str(nlinarith([term('sq_nonneg (a - b)'), term('mul_pos ha hb')]))
'nlinarith [sq_nonneg (a - b), mul_pos ha hb]'
Source code in src/veriphy/toolkit/_emit.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def nlinarith(hints: Sequence[Lean] = ()) -> Lean:
    """Call `nlinarith` with hint terms (products and squares that Sage found).

    Args:
        hints: Proof terms added to `nlinarith`'s atom set.

    Returns:
        The tactic.

    Examples:
        >>> str(nlinarith([term('sq_nonneg (a - b)'), term('mul_pos ha hb')]))
        'nlinarith [sq_nonneg (a - b), mul_pos ha hb]'
    """
    return _with_list('nlinarith', [_expect(h, 'term', 'an nlinarith hint') for h in hints])

norm_num(lemmas=(), at=None)

Call norm_num, optionally with extra simp lemmas and at a hypothesis.

Parameters:

Name Type Description Default
lemmas Sequence[str]

Lemma or hypothesis names.

()
at str | None

A hypothesis to normalize instead of the goal.

None

Returns:

Type Description
Lean

The tactic.

Examples:

>>> str(norm_num()), str(norm_num(at='hk'))
('norm_num', 'norm_num at hk')
Source code in src/veriphy/toolkit/_emit.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
def norm_num(lemmas: Sequence[str] = (), at: str | None = None) -> Lean:
    """Call `norm_num`, optionally with extra simp lemmas and at a hypothesis.

    Args:
        lemmas: Lemma or hypothesis names.
        at: A hypothesis to normalize instead of the goal.

    Returns:
        The tactic.

    Examples:
        >>> str(norm_num()), str(norm_num(at='hk'))
        ('norm_num', 'norm_num at hk')
    """
    return _with_list('norm_num', lemmas, f' at {at}' if at else '')

nullstellensatz_proof(problem)

Refute a system of polynomial equalities with no common zero: 1 = Σ gᵢ fᵢ, from a Gröbner lift.

Parameters:

Name Type Description Default
problem Problem

The problem; its goal is False and its equality hypotheses have no common zero.

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If 1 is not in the ideal of the equalities (they have a common zero over ℂ).

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'y'], 'carrier': 'ℚ', 'goal': {'kind': 'false'},
...     'hyps': [{'name': 'hxy', 'rel': 'eq', 'poly': '(x0 * x1) - (1)', 'text': 'x * y = 1'},
...              {'name': 'hx', 'rel': 'eq', 'poly': '(x0) - (0)', 'text': 'x = 0'}]})
>>> print(nullstellensatz_proof(prob))
have h1 : (1 : ℚ) = 0 := by linear_combination (-1) * hxy + (y) * hx
exact one_ne_zero h1
Source code in src/veriphy/toolkit/_algebraic.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def nullstellensatz_proof(problem: Problem) -> Lean:
    """Refute a system of polynomial equalities with no common zero: `1 = Σ gᵢ fᵢ`, from a Gröbner lift.

    Args:
        problem: The problem; its goal is `False` and its equality hypotheses have no common zero.

    Returns:
        The tactic.

    Raises:
        CertificateError: If 1 is not in the ideal of the equalities (they have a common zero over ℂ).

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'y'], 'carrier': 'ℚ', 'goal': {'kind': 'false'},
        ...     'hyps': [{'name': 'hxy', 'rel': 'eq', 'poly': '(x0 * x1) - (1)', 'text': 'x * y = 1'},
        ...              {'name': 'hx', 'rel': 'eq', 'poly': '(x0) - (0)', 'text': 'x = 0'}]})
        >>> print(nullstellensatz_proof(prob))
        have h1 : (1 : ℚ) = 0 := by linear_combination (-1) * hxy + (y) * hx
        exact one_ne_zero h1
    """
    eqs = _equalities(problem)
    ideal = problem.ring.ideal([h.poly for h in eqs])
    if 1 not in ideal:
        message = 'the equalities have a common zero over ℂ: 1 is not in their ideal'
        raise CertificateError(message)
    cofactors = problem.ring(1).lift(ideal)
    combination = linear_combination([(c, hyp(problem, h.name)) for c, h in zip(cofactors, eqs, strict=True)], problem)
    carrier = problem.carrier or 'ℚ'
    return tactic(f'have h1 : (1 : {carrier}) = 0 := by {combination}\nexact one_ne_zero h1')

operator_identity(lhs, rhs, coordinates, *, psi='ψ', smooth='hψ', point='x', name='hop')

Return a fact (lhs ψ) x = (rhs ψ) x, checked first in Sage on a symbolic ψ, then proved in Lean.

Parameters:

Name Type Description Default
lhs Operator

The left operator.

required
rhs Operator

The right operator.

required
coordinates Coordinates

The coordinates.

required
psi str

The Lean name of the test function.

'ψ'
smooth str

The Lean name of the hypothesis ContDiff ℝ k ψ, with k the identity's order.

'hψ'
point str

The Lean name of the point.

'x'
name str

The fact's name.

'hop'

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the identity fails in Sage (the difference is shown).

Examples:

>>> c = Coordinates(1)
>>> d, x = c.partial(0), c.multiplication(c.symbols[0])
>>> print(operator_identity(commutator(d, x), c.scalar(1), c).text.splitlines()[0])
have hd0 : Differentiable ℝ (ψ) := (hψ.iterate_deriv' 1 0).differentiable (by norm_num)
Source code in src/veriphy/toolkit/_operators.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
def operator_identity(
    lhs: Operator,
    rhs: Operator,
    coordinates: Coordinates,
    *,
    psi: str = 'ψ',
    smooth: str = 'hψ',
    point: str = 'x',
    name: str = 'hop',
) -> Lean:
    """Return a fact `(lhs ψ) x = (rhs ψ) x`, checked first in Sage on a symbolic `ψ`, then proved in Lean.

    Args:
        lhs: The left operator.
        rhs: The right operator.
        coordinates: The coordinates.
        psi: The Lean name of the test function.
        smooth: The Lean name of the hypothesis `ContDiff ℝ k ψ`, with `k` the identity's order.
        point: The Lean name of the point.
        name: The fact's name.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the identity fails in Sage (the difference is shown).

    Examples:
        >>> c = Coordinates(1)
        >>> d, x = c.partial(0), c.multiplication(c.symbols[0])
        >>> print(operator_identity(commutator(d, x), c.scalar(1), c).text.splitlines()[0])
        have hd0 : Differentiable ℝ (ψ) := (hψ.iterate_deriv' 1 0).differentiable (by norm_num)
    """
    xs = coordinates.symbols
    test = function('psi_')(*xs)
    difference = (lhs.act(test) - rhs.act(test)).expand()
    if difference != 0:
        message = f'the identity fails in Sage: the two sides differ by {difference}'
        raise CertificateError(message)
    order = max(lhs.order, rhs.order)
    facts = _differentiability(coordinates, order, psi, smooth, point)
    statement = f'({lhs.lean(psi)}) {point} = ({rhs.lean(psi)}) {point}'
    if coordinates.n == 1:
        proof = ['simp (disch := fun_prop) [deriv_fun_mul, deriv_fun_sub, deriv_fun_add]', 'all_goals ring']
    else:
        proof = [
            (
                f'have hproj : ∀ (i : Fin {coordinates.n}) (v : Fin {coordinates.n} → ℝ), '
                f'fderiv ℝ (fun y : Fin {coordinates.n} → ℝ => y i) {point} v = v i := '
                f'fun i v => by rw [(hasFDerivAt_apply i {point}).fderiv]; rfl'
            ),
            (
                'simp (disch := fun_prop) [fderiv_fun_sub, fderiv_fun_add, fderiv_fun_mul, fderiv_clm_apply, hproj, '
                'Pi.single_apply]'
            ),
        ]
        if order == SECOND_ORDER:
            pairs = [(i, j) for i in range(coordinates.n) for j in range(i + 1, coordinates.n)]
            rewrites = ', '.join(f'hsymm (Pi.single {i} 1) (Pi.single {j} 1)' for i, j in pairs)
            proof.append(f'try simp only [{rewrites}]')
        proof.append('all_goals ring')
    body = '\n'.join(f'  {line}' for line in proof)
    return tactic('\n'.join([*facts, f'have {name} : {statement} := by', body]))

operator_relations(matrices, names, pairs=None)

Return facts Xᵢ * Xⱼ - Xⱼ * Xᵢ = Σ cₖ • Xₖ for matrices over ℚ(i), proved entrywise over ℂ.

Parameters:

Name Type Description Default
matrices Sequence[Matrix]

The matrices (Pauli, spin, gamma matrices), with entries in ℚ(i).

required
names Sequence[str]

Their Lean names (definitions over ℂ, unfolded by the proof).

required
pairs Sequence[tuple[int, int]] | None

The pairs to state; every i < j when None.

None

Returns:

Type Description
Lean

The facts hcomm_i_j.

Raises:

Type Description
CertificateError

If a commutator is not in the span of the matrices.

Source code in src/veriphy/toolkit/_operators.py
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def operator_relations(
    matrices: Sequence[Matrix],
    names: Sequence[str],
    pairs: Sequence[tuple[int, int]] | None = None,
) -> Lean:
    """Return facts `Xᵢ * Xⱼ - Xⱼ * Xᵢ = Σ cₖ • Xₖ` for matrices over ℚ(i), proved entrywise over ℂ.

    Args:
        matrices: The matrices (Pauli, spin, gamma matrices), with entries in ℚ(i).
        names: Their Lean names (definitions over ℂ, unfolded by the proof).
        pairs: The pairs to state; every `i < j` when `None`.

    Returns:
        The facts `hcomm_i_j`.

    Raises:
        CertificateError: If a commutator is not in the span of the matrices.
    """
    field = QuadraticField(-1, 'i')
    rows = matrix(field, [m.list() for m in matrices])
    chosen = pairs if pairs is not None else [(i, j) for i in range(len(matrices)) for j in range(i + 1, len(matrices))]
    lines = []
    for i, j in chosen:
        bracket = matrices[i] * matrices[j] - matrices[j] * matrices[i]
        try:
            coefficients = rows.solve_left(vector(field, bracket.list()))
        except ValueError:
            message = f'[{names[i]}, {names[j]}] is not in the span of the matrices'
            raise CertificateError(message) from None
        terms = [f'{_complex(c)} • {names[k]}' for k, c in enumerate(coefficients) if c != 0]
        rhs = ' + '.join(terms) if terms else '0'
        proof = '\n'.join(f'  {line}' for line in [f'unfold {" ".join(dict.fromkeys(names))}', *ENTRYWISE.splitlines()])
        lines.append(f'have hcomm_{i}_{j} : {names[i]} * {names[j]} - {names[j]} * {names[i]} = {rhs} := by\n{proof}')
    return tactic('\n'.join(lines))

packed_product_certificate(a, b, c)

Return a proof term of A * B = C for large integer matrices, with their rows packed in the certificate.

The goal's matrices should be definitions (or literals) equal by unfolding to the packed ones; the kernel checks the product on lists, at n³ integer operations.

Parameters:

Name Type Description Default
a Matrix

The left factor, an integer matrix.

required
b Matrix

The right factor.

required
c Matrix

The claimed product.

required

Returns:

Type Description
Lean

The proof term.

Raises:

Type Description
CertificateError

If the matrices are not square integer matrices of one size, or a * b != c.

Examples:

>>> from sage.all import matrix
>>> a, b = matrix(ZZ, [[1, 2], [3, 4]]), matrix(ZZ, [[0, 1], [1, 0]])
>>> print(packed_product_certificate(a, b, a * b))
VeriphyCheck.ofLists_mul (n := 2) (rows₁ := VeriphyCheck.unpackRows 4 4 2 [101, 135])
  (rows₂ := VeriphyCheck.unpackRows 4 4 2 [84, 69]) (prod := VeriphyCheck.unpackRows 4 4 2 [86, 120])
  (by decide +kernel) (by decide +kernel)
Source code in src/veriphy/toolkit/_reflect.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def packed_product_certificate(a: Matrix, b: Matrix, c: Matrix) -> Lean:
    """Return a proof term of `A * B = C` for large integer matrices, with their rows packed in the certificate.

    The goal's matrices should be definitions (or literals) equal by unfolding to the packed ones; the kernel checks
    the product on lists, at `n³` integer operations.

    Args:
        a: The left factor, an integer matrix.
        b: The right factor.
        c: The claimed product.

    Returns:
        The proof term.

    Raises:
        CertificateError: If the matrices are not square integer matrices of one size, or `a * b != c`.

    Examples:
        >>> from sage.all import matrix
        >>> a, b = matrix(ZZ, [[1, 2], [3, 4]]), matrix(ZZ, [[0, 1], [1, 0]])
        >>> print(packed_product_certificate(a, b, a * b))
        VeriphyCheck.ofLists_mul (n := 2) (rows₁ := VeriphyCheck.unpackRows 4 4 2 [101, 135])
          (rows₂ := VeriphyCheck.unpackRows 4 4 2 [84, 69]) (prod := VeriphyCheck.unpackRows 4 4 2 [86, 120])
          (by decide +kernel) (by decide +kernel)
    """
    n = a.nrows()
    if any(m.nrows() != n or m.ncols() != n for m in (a, b, c)) or any(m.base_ring() != ZZ for m in (a, b, c)):
        message = 'packed certificates take square integer matrices of one size; scale rational ones to integers'
        raise CertificateError(message)
    if a * b != c:
        message = 'the claimed product is wrong: Sage computes a different A * B'
        raise CertificateError(message)
    width, offset = _packing([a, b, c])
    return term(
        f'VeriphyCheck.ofLists_mul (n := {n}) (rows₁ := {_packed(a, width, offset)})\n'
        f'  (rows₂ := {_packed(b, width, offset)}) (prod := {_packed(c, width, offset)})\n'
        '  (by decide +kernel) (by decide +kernel)',
    )

parameter_context(name='α')

Return ℚ(α) for a transcendental parameter α: identities are identities of rational functions.

Parameters:

Name Type Description Default
name str

The parameter's Lean text.

'α'

Returns:

Type Description
FieldContext

The context; denominators need ≠ 0 hypotheses, which field_simp uses.

Source code in src/veriphy/toolkit/_number_field.py
164
165
166
167
168
169
170
171
172
173
def parameter_context(name: str = 'α') -> FieldContext:
    """Return ℚ(α) for a transcendental parameter `α`: identities are identities of rational functions.

    Args:
        name: The parameter's Lean text.

    Returns:
        The context; denominators need `≠ 0` hypotheses, which `field_simp` uses.
    """
    return FieldContext(name, None, None, None)

parenthesize(text)

Return Lean text wrapped in parentheses unless it is a single identifier or numeral.

Parameters:

Name Type Description Default
text str

The Lean source.

required

Returns:

Type Description
str

The text, safe to use as an operand.

Examples:

>>> parenthesize('f a'), parenthesize('v₁'), parenthesize('↑n')
('(f a)', 'v₁', '(↑n)')
Source code in src/veriphy/toolkit/_lean.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def parenthesize(text: str) -> str:
    """Return Lean text wrapped in parentheses unless it is a single identifier or numeral.

    Args:
        text: The Lean source.

    Returns:
        The text, safe to use as an operand.

    Examples:
        >>> parenthesize('f a'), parenthesize('v₁'), parenthesize('↑n')
        ('(f a)', 'v₁', '(↑n)')
    """
    if ATOMIC.fullmatch(text) or text.isdigit():
        return text
    return f'({text})'

parity_of(m, even_dim)

Return the parity of a homogeneous supermatrix: 0 when block diagonal, 1 when block off-diagonal.

Parameters:

Name Type Description Default
m Matrix

The supermatrix.

required
even_dim int

The dimension of the even part.

required

Returns:

Type Description
int

The parity.

Raises:

Type Description
CertificateError

If the matrix is not homogeneous.

Source code in src/veriphy/toolkit/_superalgebra.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def parity_of(m: Matrix, even_dim: int) -> int:
    """Return the parity of a homogeneous supermatrix: 0 when block diagonal, 1 when block off-diagonal.

    Args:
        m: The supermatrix.
        even_dim: The dimension of the even part.

    Returns:
        The parity.

    Raises:
        CertificateError: If the matrix is not homogeneous.
    """
    n = m.nrows()
    even = all(m[i, j] == 0 for i in range(n) for j in range(n) if (i < even_dim) != (j < even_dim))
    odd = all(m[i, j] == 0 for i in range(n) for j in range(n) if (i < even_dim) == (j < even_dim))
    if even and not odd:
        return 0
    if odd and not even:
        return 1
    message = 'the supermatrix is not homogeneous (both its diagonal and off-diagonal blocks are nonzero)'
    raise CertificateError(message)

parse_polynomial(ring, s, *, param)

Parse a polynomial string in ring.

Parameters:

Name Type Description Default
ring MPolynomialRing_base

The polynomial ring.

required
s object

The polynomial, written in Sage syntax.

required
param str

The request parameter the string came from, named in errors.

required

Returns:

Type Description
MPolynomial

The polynomial.

Raises:

Type Description
ParamError

If s is not a string or not a polynomial in ring.

Examples:

>>> parse_polynomial(create_ring(['x0']), 'x0^2 - 1/2', param='poly')
x0^2 - 1/2
Source code in src/veriphy/toolkit/_problem.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def parse_polynomial(ring: MPolynomialRing_base, s: object, *, param: str) -> MPolynomial:
    """Parse a polynomial string in `ring`.

    Args:
        ring: The polynomial ring.
        s: The polynomial, written in Sage syntax.
        param: The request parameter the string came from, named in errors.

    Returns:
        The polynomial.

    Raises:
        ParamError: If `s` is not a string or not a polynomial in `ring`.

    Examples:
        >>> parse_polynomial(create_ring(['x0']), 'x0^2 - 1/2', param='poly')
        x0^2 - 1/2
    """
    if not isinstance(s, str):
        raise ParamError(param=param, reason='must be a polynomial string')
    try:
        return ring(s)
    except (TypeError, ValueError, SyntaxError, NameError) as exc:
        gens = ', '.join(map(str, ring.gens()))
        raise ParamError(param=param, reason=f'not a polynomial in QQ[{gens}]: {s!r} ({exc})') from exc

parse_symbolic(text, symbols, typed=None)

Parse a symbolic expression serialized by Lean's symbolic translation, or return None when it does not parse.

typed(e, 'ℕ') markers (a sub-expression computed at another Lean type) evaluate to e, and are recorded in typed when a list is given.

Parameters:

Name Type Description Default
text object

The expression in Sage syntax, over the variables in symbols.

required
symbols dict[str, Expression]

The symbolic variables by name.

required
typed list[tuple[Expression, str]] | None

Where to record the marked sub-expressions with their Lean type.

None

Returns:

Type Description
Expression | Matrix | None

The expression in Sage's symbolic ring (a matrix over it for a matrix-valued expression), or None.

Examples:

>>> x0 = SR.var('x0')
>>> parse_symbolic('(sqrt(x0) + log(x0))', {'x0': x0})
sqrt(x0) + log(x0)
>>> parse_symbolic('sum((k_)^(2), k_, 0, x0 - 1)', {'x0': x0})
1/3*x0^3 - 1/2*x0^2 + 1/6*x0
>>> parse_symbolic('mod(x0, 3)', {'x0': x0})
mod(x0, 3)
>>> parse_symbolic('(matrix(SR, [[1, x0]])) - (matrix(SR, [[1, 0]]))', {'x0': x0})
[ 0 x0]
>>> marked = []
>>> parse_symbolic("typed(mod(x0, 7), 'ℕ') + gcd(x0, 6) + gcd(4, 6)", {'x0': x0}, marked)
gcd(x0, 6) + mod(x0, 7) + 2
>>> marked
[(mod(x0, 7), 'ℕ')]
Source code in src/veriphy/toolkit/_problem.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def parse_symbolic(
    text: object,
    symbols: dict[str, Expression],
    typed: list[tuple[Expression, str]] | None = None,
) -> Expression | Matrix | None:
    """Parse a symbolic expression serialized by Lean's symbolic translation, or return `None` when it does not parse.

    `typed(e, 'ℕ')` markers (a sub-expression computed at another Lean type) evaluate to `e`, and are recorded in
    `typed` when a list is given.

    Args:
        text: The expression in Sage syntax, over the variables in `symbols`.
        symbols: The symbolic variables by name.
        typed: Where to record the marked sub-expressions with their Lean type.

    Returns:
        The expression in Sage's symbolic ring (a matrix over it for a matrix-valued expression), or `None`.

    Examples:
        >>> x0 = SR.var('x0')
        >>> parse_symbolic('(sqrt(x0) + log(x0))', {'x0': x0})
        sqrt(x0) + log(x0)
        >>> parse_symbolic('sum((k_)^(2), k_, 0, x0 - 1)', {'x0': x0})
        1/3*x0^3 - 1/2*x0^2 + 1/6*x0
        >>> parse_symbolic('mod(x0, 3)', {'x0': x0})
        mod(x0, 3)
        >>> parse_symbolic('(matrix(SR, [[1, x0]])) - (matrix(SR, [[1, 0]]))', {'x0': x0})
        [ 0 x0]
        >>> marked = []
        >>> parse_symbolic("typed(mod(x0, 7), 'ℕ') + gcd(x0, 6) + gcd(4, 6)", {'x0': x0}, marked)
        gcd(x0, 6) + mod(x0, 7) + 2
        >>> marked
        [(mod(x0, 7), 'ℕ')]
    """
    if not isinstance(text, str):
        return None
    names: dict[str, object] = {
        'SR': SR,
        'pi': pi,
        'I': I,
        'X': SR.var('X'),
        'sqrt': sqrt,
        'exp': exp,
        'log': log,
        'sin': sin,
        'cos': cos,
        'tan': tan,
        'abs': abs_symbolic,
        'mod': mod,
        'sum': symbolic_sum,
        'product': product,
        'typed': lambda e, ty: _record(typed, e, ty),
        'cosh': cosh,
        'sinh': sinh,
        'tanh': tanh,
        'arcsin': arcsin,
        'arccos': arccos,
        'arctan': arctan,
        'logb': lambda b, x: log(x) / log(b),
        'floor': floor,
        'ceil': ceil,
        'factorial': factorial,
        'binomial': binomial,
        'gcd': integer_gcd,
        'matrix': matrix,
        'block_matrix': block_matrix,
        'max': max_symbolic,
        'min': min_symbolic,
    }
    names.update({k: SR.var(k) for k in BOUND.findall(text)})
    names.update(symbols)
    try:
        value = sage_eval(text, locals=names)
    except (TypeError, ValueError, SyntaxError, NameError, ArithmeticError, AttributeError):
        return None
    return value if isinstance(value, Expression | Matrix) else SR(value)

parse_table(data, source, default_namespace=None)

Validate a table read from JSON against the schema and return it.

Parameters:

Name Type Description Default
data object

The parsed JSON.

required
source str

Where it came from (a file name), named in errors.

required
default_namespace str | None

The namespace of a table that names none (project for a project file).

None

Returns:

Type Description
Table

The table.

Raises:

Type Description
TableError

If a field is missing or of the wrong kind, naming the source and the field.

Examples:

>>> parse_table({'namespace': 'project', 'functions': [{'lean': 'Real.cosh', 'arity': 1}]}, 'cosh.json')
Traceback (most recent call last):
...
veriphy.core._errors.TableError: cosh.json: missing field 'sage'
>>> table = parse_table({'namespace': 'project',
...     'functions': [{'lean': 'Real.cosh', 'sage': 'cosh', 'arity': 1, 'types': ['ℝ']}]}, 'cosh.json')
>>> table.functions[0].sage, table.functions[0].types
('cosh', ('ℝ',))
Source code in src/veriphy/toolkit/_table.py
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
def parse_table(data: object, source: str, default_namespace: str | None = None) -> Table:
    """Validate a table read from JSON against the schema and return it.

    Args:
        data: The parsed JSON.
        source: Where it came from (a file name), named in errors.
        default_namespace: The namespace of a table that names none (`project` for a project file).

    Returns:
        The table.

    Raises:
        TableError: If a field is missing or of the wrong kind, naming the source and the field.

    Examples:
        >>> parse_table({'namespace': 'project', 'functions': [{'lean': 'Real.cosh', 'arity': 1}]}, 'cosh.json')
        Traceback (most recent call last):
        ...
        veriphy.core._errors.TableError: cosh.json: missing field 'sage'
        >>> table = parse_table({'namespace': 'project',
        ...     'functions': [{'lean': 'Real.cosh', 'sage': 'cosh', 'arity': 1, 'types': ['ℝ']}]}, 'cosh.json')
        >>> table.functions[0].sage, table.functions[0].types
        ('cosh', ('ℝ',))
    """
    if not isinstance(data, dict):
        raise TableError(source, '<root>', 'must be a JSON object')
    return Table(
        namespace=_text(data, 'namespace', source, default_namespace),
        functions=tuple(_function(f, source) for f in _objects(data, 'functions', source)),
        constants=tuple(_constant(c, source) for c in _objects(data, 'constants', source)),
        coercions=_strings(data, 'coercions', source),
        rewrites=tuple(_rewrite(r, source) for r in _objects(data, 'rewrites', source)),
        conventions=tuple(_convention(c, source) for c in _objects(data, 'conventions', source)),
    )

pentagon_violations(data)

Return the number of pentagon equations the F-symbols fail.

Parameters:

Name Type Description Default
data Fusion

The fusion data.

required

Returns:

Type Description
int

The count.

Source code in src/veriphy/toolkit/_fusion.py
204
205
206
207
208
209
210
211
212
213
def pentagon_violations(data: Fusion) -> int:
    """Return the number of pentagon equations the F-symbols fail.

    Args:
        data: The fusion data.

    Returns:
        The count.
    """
    return sum(1 for lhs, rhs in _pentagon_sides(data) if lhs != rhs)

polynomial(p, problem)

Render a polynomial of the problem's ring as a Lean term over the atoms' text.

Parameters:

Name Type Description Default
p object

A polynomial in problem.ring, or anything the ring converts.

required
problem Problem

The problem whose atoms name the variables.

required

Returns:

Type Description
Lean

The term, 0 for the zero polynomial.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['f a', 'b'], 'goal': {'kind': 'false'}})
>>> str(polynomial('2*x0^2 - x0*x1/3', prob))
'2 * (f a) ^ 2 - 1 / 3 * (f a) * b'
Source code in src/veriphy/toolkit/_lean.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def polynomial(p: object, problem: Problem) -> Lean:
    """Render a polynomial of the problem's ring as a Lean term over the atoms' text.

    Args:
        p: A polynomial in `problem.ring`, or anything the ring converts.
        problem: The problem whose atoms name the variables.

    Returns:
        The term, `0` for the zero polynomial.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['f a', 'b'], 'goal': {'kind': 'false'}})
        >>> str(polynomial('2*x0^2 - x0*x1/3', prob))
        '2 * (f a) ^ 2 - 1 / 3 * (f a) * b'
    """
    poly: MPolynomial = problem.ring(p)
    if poly == 0:
        return term('0')
    names = [str(g) for g in problem.ring.gens()]
    text = ''
    for exponents, coeff in sorted(poly.dict().items(), reverse=True):
        mono = _render_monomial(tuple(exponents), names, problem)
        value = QQ(coeff)
        magnitude = str(rational(abs(value)))
        body = mono if mono and abs(value) == 1 else ' * '.join(part for part in (magnitude, mono) if part)
        if not text:
            text = f'-{body}' if value < 0 else body
        else:
            text += f" {'-' if value < 0 else '+'} {body}"
    return term(text)

positive_roots(cartan_type)

Return the positive roots as their coefficients on the simple roots, in Sage's labeling.

Parameters:

Name Type Description Default
cartan_type str

The type, as 'G2'.

required

Returns:

Type Description
list[list[int]]

One coefficient list per root.

Examples:

>>> sorted(positive_roots('G2'))
[[0, 1], [1, 0], [1, 1], [2, 1], [3, 1], [3, 2]]
Source code in src/veriphy/toolkit/_lie.py
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
def positive_roots(cartan_type: str) -> list[list[int]]:
    """Return the positive roots as their coefficients on the simple roots, in Sage's labeling.

    Args:
        cartan_type: The type, as `'G2'`.

    Returns:
        One coefficient list per root.

    Examples:
        >>> sorted(positive_roots('G2'))
        [[0, 1], [1, 0], [1, 1], [2, 1], [3, 1], [3, 2]]
    """
    letter, rank = _cartan_type(cartan_type)
    lattice = RootSystem([letter, rank]).root_lattice()
    return [[int(root[i]) for i in lattice.index_set()] for root in lattice.positive_roots()]

positivity()

Call positivity.

Returns:

Type Description
Lean

The tactic.

Examples:

>>> str(positivity())
'positivity'
Source code in src/veriphy/toolkit/_emit.py
213
214
215
216
217
218
219
220
221
222
223
def positivity() -> Lean:
    """Call `positivity`.

    Returns:
        The tactic.

    Examples:
        >>> str(positivity())
        'positivity'
    """
    return tactic('positivity')

positivstellensatz_proof(problem, squares=None, multiplier_degree=1)

Refute a real semialgebraic system: κ = Σ wₖ Pₖ + Σ hⱼ fⱼ with every Pₖ ≤ 0 and κ > 0.

The facts Pₖ are the inequality hypotheses, their pairwise products and the given squares (by default squares of variables and of their sums and differences); the multipliers hⱼ of the equalities are polynomials. An exact LP finds the weights; linear_combination checks the identity.

Parameters:

Name Type Description Default
problem Problem

The problem; its goal is False (state a negated goal as a hypothesis first).

required
squares Sequence[MPolynomial] | None

Polynomials whose squares may enter the certificate.

None
multiplier_degree int

The degree of the equality multipliers.

1

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If no certificate of this degree exists.

Source code in src/veriphy/toolkit/_algebraic.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def positivstellensatz_proof(
    problem: Problem,
    squares: Sequence[MPolynomial] | None = None,
    multiplier_degree: int = 1,
) -> Lean:
    """Refute a real semialgebraic system: `κ = Σ wₖ Pₖ + Σ hⱼ fⱼ` with every `Pₖ ≤ 0` and `κ > 0`.

    The facts `Pₖ` are the inequality hypotheses, their pairwise products and the given squares (by default squares of
    variables and of their sums and differences); the multipliers `hⱼ` of the equalities are polynomials. An exact LP
    finds the weights; `linear_combination` checks the identity.

    Args:
        problem: The problem; its goal is `False` (state a negated goal as a hypothesis first).
        squares: Polynomials whose squares may enter the certificate.
        multiplier_degree: The degree of the equality multipliers.

    Returns:
        The tactic.

    Raises:
        CertificateError: If no certificate of this degree exists.
    """
    ring = problem.ring
    ineqs = _facts(problem, ('le', 'lt'))
    eqs = _equalities(problem)
    # each fact: (polynomial P with P ≤ 0 or P < 0, strict, proof term)
    facts: list[tuple[MPolynomial, bool, Lean]] = [(h.poly, h.rel == 'lt', hyp(problem, h.name)) for h in ineqs]

    def nonneg(h: _Fact) -> str:
        return f'(sub_nonneg.mpr {h.name}.le)' if h.rel == 'lt' else f'(sub_nonneg.mpr {h.name})'

    for a, b in itertools.combinations_with_replacement(ineqs, 2):
        facts.append((-(a.poly * b.poly), False, term(f'mul_nonneg {nonneg(a)} {nonneg(b)}')))
    for s in squares if squares is not None else _linear_forms(ring):
        facts.append((-(ring(s) ** 2), False, term(f'sq_nonneg ({polynomial(ring(s), problem)})')))
    multipliers = [m for d in range(multiplier_degree + 1) for m in _monomials(ring, d)]
    columns: list[MPolynomial] = [p for p, _, _ in facts] + [m * h.poly for h in eqs for m in multipliers]
    n_facts = len(facts)
    monomials = sorted({m for p in columns for m in p.monomials()} - {ring(1)})
    rows = [([p.monomial_coefficient(m) for p in columns], 0) for m in monomials]
    constants = [p.constant_coefficient() for p in columns]
    nonneg_vars = list(range(n_facts))
    weights = solve_exact_lp(len(columns), eqs=rows, les=[([-c for c in constants], 1)], nonneg=nonneg_vars)
    strict = False
    if weights is None:
        strictness = [1 if s else 0 for _, s, _ in facts] + [0] * (len(columns) - n_facts)
        weights = solve_exact_lp(
            len(columns),
            eqs=[*rows, (constants, 0)],
            les=[([-s for s in strictness], 1)],
            nonneg=nonneg_vars,
        )
        strict = True
    if weights is None:
        message = 'no Positivstellensatz certificate of this degree; pass more squares or raise multiplier_degree'
        raise CertificateError(message)
    kappa = sum(w * c for w, c in zip(weights, constants, strict=True))
    weighted: list[tuple[object, Lean]] = [(w, proof) for w, (_, _, proof) in zip(weights, facts, strict=False) if w]
    eq_weights = weights[n_facts:]
    for j, h in enumerate(eqs):
        coefficient = sum(eq_weights[j * len(multipliers) + t] * m for t, m in enumerate(multipliers))
        if coefficient != 0:
            weighted.append((coefficient, hyp(problem, h.name)))
    uses_strict = strict or any(w and s for w, (_, s, _) in zip(weights, facts, strict=False))
    return refute(QQ(kappa), strict=uses_strict, weighted=weighted, problem=problem)

prime_certificate(p)

Return a proof term of Nat.Prime p from a Pratt certificate the kernel checks.

Parameters:

Name Type Description Default
p object

A prime number.

required

Returns:

Type Description
Lean

The proof term.

Raises:

Type Description
CertificateError

If p is not prime.

Examples:

>>> print(prime_certificate(7))
VeriphyCheck.prime_of_pratt (cert := [(3, 2, [(2, 1)]), (7, 3, [(2, 1), (3, 1)])]) (by decide +kernel)
Source code in src/veriphy/toolkit/_reflect.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def prime_certificate(p: object) -> Lean:
    """Return a proof term of `Nat.Prime p` from a Pratt certificate the kernel checks.

    Args:
        p: A prime number.

    Returns:
        The proof term.

    Raises:
        CertificateError: If `p` is not prime.

    Examples:
        >>> print(prime_certificate(7))
        VeriphyCheck.prime_of_pratt (cert := [(3, 2, [(2, 1)]), (7, 3, [(2, 1), (3, 1)])]) (by decide +kernel)
    """
    prime = Integer(p)
    if not prime.is_prime():
        message = f'{prime} is not prime'
        raise CertificateError(message)
    entries: list[tuple[int, int, list[tuple[int, int]]]] = []
    _pratt_entries(prime, entries, set())
    cert = ', '.join(f'({q}, {a}, [{", ".join(f"({f}, {e})" for f, e in fs)}])' for q, a, fs in entries)
    return term(f'VeriphyCheck.prime_of_pratt (cert := [{cert}]) (by decide +kernel)')

primitive(omega)

Return a function f with df = ω for a closed 1-form on ℝⁿ, by integrating along the coordinate axes.

Parameters:

Name Type Description Default
omega Sequence[object]

The components ω₀, ω₁, …, expressions in the coordinates x0, ….

required

Returns:

Type Description
object

The primitive.

Raises:

Type Description
CertificateError

If the form is not closed (∂ᵢ ωⱼ ≠ ∂ⱼ ωᵢ, naming the pair).

Examples:

>>> x0, x1, x2 = coordinates(3)
>>> primitive([2 * x0 * x1, x0**2, 2 * x2])
x0^2*x1 + x2^2
Source code in src/veriphy/toolkit/_geometry.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def primitive(omega: Sequence[object]) -> object:
    """Return a function `f` with `df = ω` for a closed 1-form on ℝⁿ, by integrating along the coordinate axes.

    Args:
        omega: The components `ω₀, ω₁, …`, expressions in the coordinates `x0, …`.

    Returns:
        The primitive.

    Raises:
        CertificateError: If the form is not closed (`∂ᵢ ωⱼ ≠ ∂ⱼ ωᵢ`, naming the pair).

    Examples:
        >>> x0, x1, x2 = coordinates(3)
        >>> primitive([2 * x0 * x1, x0**2, 2 * x2])
        x0^2*x1 + x2^2
    """
    xs = coordinates(len(omega))
    for i in range(len(omega)):
        for j in range(i + 1, len(omega)):
            if (SR(omega[j]).diff(xs[i]) - SR(omega[i]).diff(xs[j])).simplify_full() != 0:
                message = f'the form is not closed: ∂{i} ω{j} ≠ ∂{j} ω{i}'
                raise CertificateError(message)
    f = SR(0)
    for i, x in enumerate(xs):
        remainder = (SR(omega[i]) - f.diff(x)).simplify_full()
        f += remainder.integral(x)
    return f.simplify_full()

projector_facts(projectors, rho)

Return facts that the matrices are projectors onto summands of the representation.

Each Pₖ is idempotent (hp_k), distinct ones are orthogonal (ho_k_l), they sum to the identity (hsum), and each commutes with every representation matrix (hc_k_i).

Parameters:

Name Type Description Default
projectors Sequence[Matrix]

The projectors.

required
rho Rep

The representation's matrices.

required

Returns:

Type Description
Lean

The facts.

Raises:

Type Description
CertificateError

If a claimed identity fails in Sage.

Source code in src/veriphy/toolkit/_representation.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
def projector_facts(projectors: Sequence[Matrix], rho: Rep) -> Lean:
    """Return facts that the matrices are projectors onto summands of the representation.

    Each `Pₖ` is idempotent (`hp_k`), distinct ones are orthogonal (`ho_k_l`), they sum to the identity (`hsum`),
    and each commutes with every representation matrix (`hc_k_i`).

    Args:
        projectors: The projectors.
        rho: The representation's matrices.

    Returns:
        The facts.

    Raises:
        CertificateError: If a claimed identity fails in Sage.
    """
    one = identity_matrix(QQ, rho[0].nrows())
    terms = [rational_matrix_term(p) for p in projectors]
    lines = []
    for k, p in enumerate(projectors):
        if p * p != p:
            message = f'P{k} is not idempotent'
            raise CertificateError(message)
        lines.append(
            f'have hp_{k} : {terms[k]} * {terms[k]} = {terms[k]} := {PRODUCT}',
        )
        for ell in range(k + 1, len(projectors)):
            if p * projectors[ell] != 0:
                message = f'P{k} and P{ell} are not orthogonal'
                raise CertificateError(message)
            lines.append(
                f'have ho_{k}_{ell} : {terms[k]} * {terms[ell]} = 0 := {PRODUCT}',
            )
        for i, x in enumerate(rho):
            if p * x != x * p:
                message = f'P{k} does not commute with matrix {i}'
                raise CertificateError(message)
            lines.append(
                f'have hc_{k}_{i} : {terms[k]} * {rational_matrix_term(x)} = {rational_matrix_term(x)} * {terms[k]} := '
                f'{COMMUTE}',
            )
    if sum(projectors, 0 * one) != one:
        message = 'the projectors do not sum to the identity'
        raise CertificateError(message)
    lines.append(f'have hsum : {" + ".join(terms)} = 1 := {EQUAL}')
    return tactic('\n'.join(lines))

quadratic_context(d, name='hs')

Return ℚ(√d) inside ℝ: the generator Real.sqrt d, with Real.sqrt d ^ 2 = d from Real.sq_sqrt.

Parameters:

Name Type Description Default
d int

A positive integer that is not a square.

required
name str

The fact's name.

'hs'

Returns:

Type Description
FieldContext

The context.

Raises:

Type Description
CertificateError

If d is not a positive non-square integer.

Examples:

>>> print(quadratic_context(2).setup)
have hs : Real.sqrt 2 ^ 2 = 2 := Real.sq_sqrt (by norm_num)
Source code in src/veriphy/toolkit/_number_field.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def quadratic_context(d: int, name: str = 'hs') -> FieldContext:
    """Return ℚ(√d) inside ℝ: the generator `Real.sqrt d`, with `Real.sqrt d ^ 2 = d` from `Real.sq_sqrt`.

    Args:
        d: A positive integer that is not a square.
        name: The fact's name.

    Returns:
        The context.

    Raises:
        CertificateError: If `d` is not a positive non-square integer.

    Examples:
        >>> print(quadratic_context(2).setup)
        have hs : Real.sqrt 2 ^ 2 = 2 := Real.sq_sqrt (by norm_num)
    """
    if QQ(d) <= 0 or QQ(d).denominator() != 1 or QQ(d).is_square():
        message = f'ℚ(√d) needs a positive non-square integer d, got {d}'
        raise CertificateError(message)
    generator = f'Real.sqrt {d}'
    return FieldContext(
        generator,
        X**2 - d,
        name,
        tactic(f'have {name} : {generator} ^ 2 = {d} := Real.sq_sqrt (by norm_num)'),
    )

radical_proof(problem, max_power=8)

Prove p = 0 (the goal lhs = rhs) from equalities whose ideal contains a power pᵏ.

Parameters:

Name Type Description Default
problem Problem

The problem; its goal is a polynomial equality over a ring without zero divisors.

required
max_power int

The largest power tried.

8

Returns:

Type Description
Lean

The tactic: pᵏ = 0 by linear_combination, then pow_eq_zero_iff.

Raises:

Type Description
CertificateError

If no power up to max_power is in the ideal.

Source code in src/veriphy/toolkit/_algebraic.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def radical_proof(problem: Problem, max_power: int = 8) -> Lean:
    """Prove `p = 0` (the goal `lhs = rhs`) from equalities whose ideal contains a power `pᵏ`.

    Args:
        problem: The problem; its goal is a polynomial equality over a ring without zero divisors.
        max_power: The largest power tried.

    Returns:
        The tactic: `pᵏ = 0` by `linear_combination`, then `pow_eq_zero_iff`.

    Raises:
        CertificateError: If no power up to `max_power` is in the ideal.
    """
    goal = problem.goal
    if goal is None or goal.rel != 'eq' or goal.poly is None:
        message = 'the goal must be a polynomial equality'
        raise CertificateError(message)
    eqs = _equalities(problem)
    ideal = problem.ring.ideal([h.poly for h in eqs])
    for k in range(1, max_power + 1):
        if goal.poly**k in ideal:
            cofactors = (goal.poly**k).lift(ideal)
            weighted = [(c, hyp(problem, h.name)) for c, h in zip(cofactors, eqs, strict=True)]
            combination = linear_combination(weighted, problem)
            if k == 1:
                return tactic(str(combination))
            sides = f'({problem.lhs.text}) - ({problem.rhs.text})' if problem.lhs and problem.rhs else None
            if sides is None:
                message = 'the goal has no polynomial sides to state the power of'
                raise CertificateError(message)
            return tactic(
                f'have hk : ({sides}) ^ {k} = 0 := by {combination}\n'
                f'exact sub_eq_zero.mp ((pow_eq_zero_iff (n := {k}) (by norm_num)).mp hk)',
            )
    message = f'no power up to {max_power} of the goal is in the ideal of the equalities'
    raise CertificateError(message)

rational(q)

Render an exact rational as a Lean numeral term.

Parameters:

Name Type Description Default
q object

Anything Sage converts exactly to a rational.

required

Returns:

Type Description
Lean

n, -n, n / d or -(n / d).

Examples:

>>> [str(rational(x)) for x in (3, -3, QQ(3) / 2, QQ(-3) / 2)]
['3', '-3', '3 / 2', '-(3 / 2)']
Source code in src/veriphy/toolkit/_lean.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def rational(q: object) -> Lean:
    """Render an exact rational as a Lean numeral term.

    Args:
        q: Anything Sage converts exactly to a rational.

    Returns:
        `n`, `-n`, `n / d` or `-(n / d)`.

    Examples:
        >>> [str(rational(x)) for x in (3, -3, QQ(3) / 2, QQ(-3) / 2)]
        ['3', '-3', '3 / 2', '-(3 / 2)']
    """
    value = QQ(q)
    numerator, denominator = abs(value.numerator()), value.denominator()
    base = str(numerator) if denominator == 1 else f'{numerator} / {denominator}'
    if value >= 0:
        return term(base)
    return term(f'-{base}' if denominator == 1 else f'-({base})')

rational_matrix_term(m, carrier='ℚ')

Return a typed !![…] literal of a rational matrix.

Parameters:

Name Type Description Default
m Matrix

A matrix with rational entries.

required
carrier str

The Lean type of the entries.

'ℚ'

Returns:

Type Description
str

The Lean text.

Examples:

>>> rational_matrix_term(matrix(QQ, [[1, 1/2], [0, -1]]))
'(!![1, 1 / 2; 0, -1] : Matrix (Fin 2) (Fin 2) ℚ)'
Source code in src/veriphy/toolkit/_lie.py
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
def rational_matrix_term(m: Matrix, carrier: str = 'ℚ') -> str:
    """Return a typed `!![…]` literal of a rational matrix.

    Args:
        m: A matrix with rational entries.
        carrier: The Lean type of the entries.

    Returns:
        The Lean text.

    Examples:
        >>> rational_matrix_term(matrix(QQ, [[1, 1/2], [0, -1]]))
        '(!![1, 1 / 2; 0, -1] : Matrix (Fin 2) (Fin 2) ℚ)'
    """

    def entry(x: object) -> str:
        q = QQ(x)
        return str(q) if q.denominator() == 1 else f'{q.numerator()} / {q.denominator()}'

    rows = '; '.join(', '.join(entry(x) for x in row) for row in m.rows())
    return f'(!![{rows}] : Matrix (Fin {m.nrows()}) (Fin {m.ncols()}) {carrier})'

rcases(target, names, branches)

Split a disjunction proof into branches, naming the hypothesis of each.

Parameters:

Name Type Description Default
target Lean

A proof of a disjunction p₁ ∨ … ∨ pₙ.

required
names Sequence[str]

One hypothesis name per disjunct.

required
branches Sequence[Lean]

One tactic block per disjunct.

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
VeriphyError

If the numbers of names and branches differ.

Examples:

>>> print(rcases(term('le_or_lt x 0'), ['hx', 'hx'], [tactic('nlinarith'), tactic('positivity')]))
rcases le_or_lt x 0 with hx | hx
· nlinarith
· positivity
Source code in src/veriphy/toolkit/_emit.py
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
def rcases(target: Lean, names: Sequence[str], branches: Sequence[Lean]) -> Lean:
    """Split a disjunction proof into branches, naming the hypothesis of each.

    Args:
        target: A proof of a disjunction `p₁ ∨ … ∨ pₙ`.
        names: One hypothesis name per disjunct.
        branches: One tactic block per disjunct.

    Returns:
        The tactic.

    Raises:
        VeriphyError: If the numbers of names and branches differ.

    Examples:
        >>> print(rcases(term('le_or_lt x 0'), ['hx', 'hx'], [tactic('nlinarith'), tactic('positivity')]))
        rcases le_or_lt x 0 with hx | hx
        · nlinarith
        · positivity
    """
    if len(names) != len(branches):
        message = f'rcases has {len(names)} names for {len(branches)} branches'
        raise VeriphyError(message)
    pattern = ' | '.join(names)
    blocks = '\n'.join(f'· {_indent_block(_expect(b, "tactic", "a branch"))}' for b in branches)
    return tactic(f'rcases {_expect(target, "term", "a disjunction proof")} with {pattern}\n{blocks}')

read_problem(problem)

Bind a serialized Lean problem to Sage objects.

Parameters:

Name Type Description Default
problem object

The problem as Veriphy.Problem.toJson serializes it.

required

Returns:

Type Description
Problem

The bound problem.

Raises:

Type Description
ParamError

If the serialization is malformed.

Examples:

>>> bound = read_problem({'vars': ['x0'], 'atoms': ['x'],
...     'hyps': [{'name': 'h', 'poly': 'x0 - 1', 'rel': 'le', 'text': 'x ≤ 1'},
...              {'name': 'hs', 'text': 'Real.sin x ≤ 1'}],
...     'goal': {'kind': 'other', 'text': 'Real.cos x ≤ 1'}})
>>> [(h.name, h.poly, h.rel) for h in bound.hyps]
[('h', x0 - 1, 'le'), ('hs', None, None)]
>>> bound.goal.text
'Real.cos x ≤ 1'
Source code in src/veriphy/toolkit/_problem.py
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
def read_problem(problem: object) -> Problem:
    """Bind a serialized Lean problem to Sage objects.

    Args:
        problem: The problem as `Veriphy.Problem.toJson` serializes it.

    Returns:
        The bound problem.

    Raises:
        ParamError: If the serialization is malformed.

    Examples:
        >>> bound = read_problem({'vars': ['x0'], 'atoms': ['x'],
        ...     'hyps': [{'name': 'h', 'poly': 'x0 - 1', 'rel': 'le', 'text': 'x ≤ 1'},
        ...              {'name': 'hs', 'text': 'Real.sin x ≤ 1'}],
        ...     'goal': {'kind': 'other', 'text': 'Real.cos x ≤ 1'}})
        >>> [(h.name, h.poly, h.rel) for h in bound.hyps]
        [('h', x0 - 1, 'le'), ('hs', None, None)]
        >>> bound.goal.text
        'Real.cos x ≤ 1'
    """
    if not isinstance(problem, dict):
        raise ParamError(param='problem', reason='must be an object')
    names = _read_names(problem)
    ring = create_ring(names)
    goal = problem.get('goal') or {'kind': 'false'}
    goal = goal if isinstance(goal, dict) else {}
    kind = str(goal.get('kind'))
    if kind not in GOAL_KINDS:
        raise ParamError(param='problem', reason=f'unknown goal kind {kind!r}')
    atoms = problem.get('atoms', [])
    types = problem.get('atom_types', [])
    functions = _read_functions(problem.get('functions', []))
    symbols = {n: SR.var(n) for n in names}
    symbols.update({name: function(name) for name in functions})
    typed: list[tuple[Expression, str]] = []
    bound = Problem(
        ring=ring,
        hyps=_read_constraints(problem.get('hyps', []), 'constraint', ring, symbols, typed),
        goal_kind=kind,
        atoms={n: str(a) for n, a in zip(names, atoms if isinstance(atoms, list) else [], strict=False)},
        carrier=str(problem.get('carrier', '')),
        atom_types={n: str(t) for n, t in zip(names, types if isinstance(types, list) else [], strict=False)},
        domain=_read_domain(problem.get('domain', []), symbols),
        symbols=symbols,
        tables=_read_tables(problem),
        typed=typed,
        derivatives=_read_derivatives(problem.get('derivatives', []), symbols),
        functions=functions,
        definitions=_read_definitions(problem.get('definitions', []), symbols),
        exists_function=_read_exists_function(problem.get('exists_function'), symbols),
        matrix_indices=_read_matrix_indices(problem.get('matrix_indices')),
    )
    if kind in ('rel', 'other'):
        bound.goal = _read_constraint({**goal, 'name': None}, 'goal', ring, symbols, typed)
    if kind == 'rel' and 'lhs_poly' in goal:
        bound.lhs = Side(parse_polynomial(ring, goal.get('lhs_poly'), param='problem'), str(goal.get('lhs', '')))
        bound.rhs = Side(parse_polynomial(ring, goal.get('rhs_poly'), param='problem'), str(goal.get('rhs', '')))
    elif kind == 'exists':
        bound.binders = list(ring.gens()[: int(str(goal.get('binders', 0)))])
        bound.body = _read_constraints(goal.get('body', []), 'body', ring, symbols, typed)
    return bound

real_matrix_term(m)

Return a matrix with entries q √k as a Lean literal over ℝ.

Parameters:

Name Type Description Default
m Matrix

The matrix (rationals times square roots of integers).

required

Returns:

Type Description
str

The literal.

Source code in src/veriphy/toolkit/_operators.py
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
def real_matrix_term(m: Matrix) -> str:
    """Return a matrix with entries `q √k` as a Lean literal over ℝ.

    Args:
        m: The matrix (rationals times square roots of integers).

    Returns:
        The literal.
    """

    def entry(x: object) -> str:
        e = SR(x)
        if e in QQ:
            q = QQ(e)
            return str(q) if q.denominator() == 1 else f'({q.numerator()} / {q.denominator()})'
        squared = QQ(e**2)
        if squared.denominator() == 1 and e > 0:
            return f'Real.sqrt {squared}'
        message = f'cannot render {e} as a rational or a square root'
        raise CertificateError(message)

    rows = '; '.join(', '.join(entry(x) for x in row) for row in m.rows())
    return f'(!![{rows}] : Matrix (Fin {m.nrows()}) (Fin {m.ncols()}) ℝ)'

recurrence_proof(order, recurrence, initial)

Return a proof of a goal a n = f n by strong induction, from a recurrence of order d and d initial values.

Parameters:

Name Type Description Default
order int

The recurrence's order d.

required
recurrence str

The name of the hypothesis ∀ n, a (n + d) = ….

required
initial Sequence[str]

The names of the hypotheses a 0 = …, …, a (d - 1) = ….

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the number of initial conditions is not the order.

Examples:

>>> print(recurrence_proof(2, 'hrec', ['h0', 'h1']))
induction n using Nat.strong_induction_on with
| _ n ih =>
  match n with
  | 0 => rw [h0]; norm_num
  | 1 => rw [h1]; norm_num
  | m + 2 => rw [hrec, ih m (by omega), ih (m + 1) (by omega)]; ring
Source code in src/veriphy/toolkit/_summation.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def recurrence_proof(order: int, recurrence: str, initial: Sequence[str]) -> Lean:
    """Return a proof of a goal `a n = f n` by strong induction, from a recurrence of order `d` and `d` initial values.

    Args:
        order: The recurrence's order `d`.
        recurrence: The name of the hypothesis `∀ n, a (n + d) = …`.
        initial: The names of the hypotheses `a 0 = …`, …, `a (d - 1) = …`.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the number of initial conditions is not the order.

    Examples:
        >>> print(recurrence_proof(2, 'hrec', ['h0', 'h1']))
        induction n using Nat.strong_induction_on with
        | _ n ih =>
          match n with
          | 0 => rw [h0]; norm_num
          | 1 => rw [h1]; norm_num
          | m + 2 => rw [hrec, ih m (by omega), ih (m + 1) (by omega)]; ring
    """
    if len(initial) != order:
        message = f'a recurrence of order {order} needs {order} initial conditions, got {len(initial)}'
        raise CertificateError(message)
    values = ''.join(f', ih {"m" if i == 0 else f"(m + {i})"} (by omega)' for i in range(order))
    cases = [f'  | {i} => rw [{h}]; norm_num' for i, h in enumerate(initial)]
    lines = [
        'induction n using Nat.strong_induction_on with',
        '| _ n ih =>',
        '  match n with',
        *cases,
        f'  | m + {order} => rw [{recurrence}{values}]; ring',
    ]
    return tactic('\n'.join(lines))

refute(kappa, *, strict, weighted, problem)

Close False from a combination of hypotheses that reduces to the contradictory constant κ.

The combination proves κ ≤ 0 (or κ < 0 when strict), which norm_num refutes.

Parameters:

Name Type Description Default
kappa object

The constant the combination reduces to, positive (or zero when strict).

required
strict bool

Whether the combination uses a strict hypothesis.

required
weighted Sequence[tuple[object, Lean]]

Pairs of a weight and a hypothesis proof, as for combination.

required
problem Problem

The problem, which must name its carrier type.

required

Returns:

Type Description
Lean

The tactic block.

Raises:

Type Description
VeriphyError

If the problem does not name its carrier type.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'carrier': 'ℚ', 'goal': {'kind': 'false'}})
>>> print(refute(2, strict=False, weighted=[(1, term('h1')), (-1, term('h2'))], problem=prob))
have hκ : (2 : ℚ) ≤ 0 := by linear_combination h1 + (-1) * h2
norm_num at hκ
Source code in src/veriphy/toolkit/_emit.py
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
def refute(kappa: object, *, strict: bool, weighted: Sequence[tuple[object, Lean]], problem: Problem) -> Lean:
    """Close `False` from a combination of hypotheses that reduces to the contradictory constant `κ`.

    The combination proves `κ ≤ 0` (or `κ < 0` when `strict`), which `norm_num` refutes.

    Args:
        kappa: The constant the combination reduces to, positive (or zero when `strict`).
        strict: Whether the combination uses a strict hypothesis.
        weighted: Pairs of a weight and a hypothesis proof, as for `combination`.
        problem: The problem, which must name its carrier type.

    Returns:
        The tactic block.

    Raises:
        VeriphyError: If the problem does not name its carrier type.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'carrier': 'ℚ', 'goal': {'kind': 'false'}})
        >>> print(refute(2, strict=False, weighted=[(1, term('h1')), (-1, term('h2'))], problem=prob))
        have hκ : (2 : ℚ) ≤ 0 := by linear_combination h1 + (-1) * h2
        norm_num at hκ
    """
    if not problem.carrier:
        message = 'refute needs the problem carrier type (such as ℚ) to state the constant'
        raise VeriphyError(message)
    name = fresh_name(problem, 'hκ')
    relation = '<' if strict else '≤'
    statement = f'have {name} : ({rational(QQ(kappa))} : {problem.carrier}) {relation} 0'
    return steps(
        tactic(f'{statement} := by linear_combination {combination(weighted, problem)}'),
        norm_num(at=name),
    )

render_element(x, context)

Return a field element as Lean text in the context's generator (a rational function for a parameter).

Parameters:

Name Type Description Default
x NumberFieldElement | FractionFieldElement | int

An element of the field (a rational function in the parameter for ℚ(α)).

required
context FieldContext

The field's context.

required

Returns:

Type Description
str

The text.

Examples:

>>> from sage.all import QuadraticField
>>> render_element(QuadraticField(2, 's').gen() / 2, quadratic_context(2))
'(1 / 2) * (Real.sqrt 2)'
Source code in src/veriphy/toolkit/_number_field.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def render_element(x: NumberFieldElement | FractionFieldElement | int, context: FieldContext) -> str:
    """Return a field element as Lean text in the context's generator (a rational function for a parameter).

    Args:
        x: An element of the field (a rational function in the parameter for ℚ(α)).
        context: The field's context.

    Returns:
        The text.

    Examples:
        >>> from sage.all import QuadraticField
        >>> render_element(QuadraticField(2, 's').gen() / 2, quadratic_context(2))
        '(1 / 2) * (Real.sqrt 2)'
    """
    if context.modulus is None:
        value = FractionField(X.parent())(x) if not isinstance(x, FractionFieldElement) else x
        numerator, denominator = value.numerator(), value.denominator()
        top = render_polynomial(X.parent()(list(numerator)), context.generator)
        if denominator == 1:
            return top
        return f'({top}) / ({render_polynomial(X.parent()(list(denominator)), context.generator)})'
    return render_polynomial(lift(x), context.generator)

render_polynomial(p, generator)

Return a polynomial over ℚ in x as Lean text in the generator, highest degree first.

Parameters:

Name Type Description Default
p Polynomial

The polynomial.

required
generator str

The generator's Lean text.

required

Returns:

Type Description
str

The text, 0 for the zero polynomial.

Examples:

>>> render_polynomial(X**4 + X**3 - X / 2 + 1, 'ζ')
'ζ ^ 4 + ζ ^ 3 - (1 / 2) * ζ + 1'
>>> render_polynomial(-X, 'Real.sqrt 2')
'-(Real.sqrt 2)'
Source code in src/veriphy/toolkit/_number_field.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def render_polynomial(p: Polynomial, generator: str) -> str:
    """Return a polynomial over ℚ in `x` as Lean text in the generator, highest degree first.

    Args:
        p: The polynomial.
        generator: The generator's Lean text.

    Returns:
        The text, `0` for the zero polynomial.

    Examples:
        >>> render_polynomial(X**4 + X**3 - X / 2 + 1, 'ζ')
        'ζ ^ 4 + ζ ^ 3 - (1 / 2) * ζ + 1'
        >>> render_polynomial(-X, 'Real.sqrt 2')
        '-(Real.sqrt 2)'
    """
    g = generator if ' ' not in generator else f'({generator})'
    text = ''
    for k in sorted(p.exponents(), reverse=True):
        c = QQ(p[k])
        monomial = '' if k == 0 else (g if k == 1 else f'{g} ^ {k}')
        size = abs(c)
        if not monomial:
            body = _rational(size)
        elif size == 1:
            body = monomial
        else:
            body = f'{_rational(size)} * {monomial}'
        sign = '-' if c < 0 else '+'
        text = f'{text} {sign} {body}' if text else (f'-{body}' if c < 0 else body)
    return text or '0'

representation_relations(rho, table, names=None, pairs=None)

Return facts ⁅ρ(eᵢ), ρ(eⱼ)⁆ = Σ cᵢⱼₖ • ρ(eₖ): the matrices satisfy the algebra's bracket relations.

Sage checks each relation first; the kernel checks it again (VeriphyCheck.bracket_eq_of_check).

Parameters:

Name Type Description Default
rho Rep

The representation's matrices, on the algebra's basis.

required
table Table

The algebra's structure constants on that basis.

required
names Sequence[str] | None

Lean names for the matrices; literals when None.

None
pairs Sequence[tuple[int, int]] | None

The pairs to state; every i < j when None.

None

Returns:

Type Description
Lean

The facts, hr_i_j.

Raises:

Type Description
CertificateError

If a relation fails in Sage, naming it.

Source code in src/veriphy/toolkit/_representation.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
def representation_relations(
    rho: Rep,
    table: Table,
    names: Sequence[str] | None = None,
    pairs: Sequence[tuple[int, int]] | None = None,
) -> Lean:
    """Return facts `⁅ρ(eᵢ), ρ(eⱼ)⁆ = Σ cᵢⱼₖ • ρ(eₖ)`: the matrices satisfy the algebra's bracket relations.

    Sage checks each relation first; the kernel checks it again (`VeriphyCheck.bracket_eq_of_check`).

    Args:
        rho: The representation's matrices, on the algebra's basis.
        table: The algebra's structure constants on that basis.
        names: Lean names for the matrices; literals when `None`.
        pairs: The pairs to state; every `i < j` when `None`.

    Returns:
        The facts, `hr_i_j`.

    Raises:
        CertificateError: If a relation fails in Sage, naming it.
    """
    terms = [_term(m, names[i] if names else None) for i, m in enumerate(rho)]
    chosen = pairs if pairs is not None else list(itertools.combinations(range(len(rho)), 2))
    lines = []
    for i, j in chosen:
        bracket = rho[i] * rho[j] - rho[j] * rho[i]
        expected = sum((QQ(c) * m for c, m in zip(table[i][j], rho, strict=True)), 0 * rho[0])
        if bracket != expected:
            message = f'the matrices {i} and {j} do not satisfy the bracket relation of the algebra'
            raise CertificateError(message)
        rhs = smul_combination(table[i][j], terms)
        lines.append(
            f'have hr_{i}_{j} : ⁅{terms[i]}, {terms[j]}⁆ = {rhs} := {BRACKET}',
        )
    return tactic('\n'.join(lines))

require_domain(problem, kinds=DOMAIN_KINDS, proofs=None)

Discharge the problem's domain conditions of the given kinds, by hypothesis or by a supplied proof.

Parameters:

Name Type Description Default
problem Problem

The problem.

required
kinds Sequence[str]

The condition kinds required (ne, nonneg, pos).

DOMAIN_KINDS
proofs Mapping[str, Lean | str] | None

A tactic proving a condition, by the condition's text, for conditions no hypothesis discharges.

None

Returns:

Type Description
tuple[list[str], list[Lean]]

The names of facts stating the conditions, and the have steps introducing the supplied proofs.

Raises:

Type Description
CertificateError

If a required condition is neither discharged nor supplied, naming each one.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['x'], 'goal': {'kind': 'false'},
...     'domain': [{'expr': 'x0', 'kind': 'pos', 'text': '0 < x'}]})
>>> require_domain(prob)
Traceback (most recent call last):
...
veriphy.core._errors.CertificateError: undischarged domain conditions: 0 < x (pass their proofs)
>>> names, haves = require_domain(prob, proofs={'0 < x': 'positivity'})
>>> names, [str(h) for h in haves]
(['hd'], ['have hd : 0 < x := by positivity'])
Source code in src/veriphy/toolkit/_symbolic_emit.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
def require_domain(
    problem: Problem,
    kinds: Sequence[str] = DOMAIN_KINDS,
    proofs: Mapping[str, Lean | str] | None = None,
) -> tuple[list[str], list[Lean]]:
    """Discharge the problem's domain conditions of the given kinds, by hypothesis or by a supplied proof.

    Args:
        problem: The problem.
        kinds: The condition kinds required (`ne`, `nonneg`, `pos`).
        proofs: A tactic proving a condition, by the condition's text, for conditions no hypothesis discharges.

    Returns:
        The names of facts stating the conditions, and the `have` steps introducing the supplied proofs.

    Raises:
        CertificateError: If a required condition is neither discharged nor supplied, naming each one.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['x'], 'goal': {'kind': 'false'},
        ...     'domain': [{'expr': 'x0', 'kind': 'pos', 'text': '0 < x'}]})
        >>> require_domain(prob)
        Traceback (most recent call last):
        ...
        veriphy.core._errors.CertificateError: undischarged domain conditions: 0 < x (pass their proofs)
        >>> names, haves = require_domain(prob, proofs={'0 < x': 'positivity'})
        >>> names, [str(h) for h in haves]
        (['hd'], ['have hd : 0 < x := by positivity'])
    """
    proofs = proofs or {}
    names: list[str] = []
    haves: list[Lean] = []
    missing: list[Condition] = []
    for c in problem.domain:
        if c.kind not in kinds:
            continue
        if c.discharged_by:
            names.append(c.discharged_by)
        elif c.text in proofs:
            name = fresh_name(problem, 'hd', names)
            names.append(name)
            haves.append(tactic(f'have {name} : {c.text} := by {proofs[c.text]}'))
        else:
            missing.append(c)
    if missing:
        listed = '; '.join(c.text for c in missing)
        message = f'undischarged domain conditions: {listed} (pass their proofs)'
        raise CertificateError(message)
    return names, haves

restrict(rho, basis)

Return the matrices of the representation on an invariant subspace, in the given basis (columns).

Parameters:

Name Type Description Default
rho Rep

The representation.

required
basis Matrix

Columns spanning an invariant subspace.

required

Returns:

Type Description
Rep

The restricted matrices.

Raises:

Type Description
CertificateError

If the subspace is not invariant.

Source code in src/veriphy/toolkit/_representation.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def restrict(rho: Rep, basis: Matrix) -> Rep:
    """Return the matrices of the representation on an invariant subspace, in the given basis (columns).

    Args:
        rho: The representation.
        basis: Columns spanning an invariant subspace.

    Returns:
        The restricted matrices.

    Raises:
        CertificateError: If the subspace is not invariant.
    """
    restricted = []
    for i, x in enumerate(rho):
        try:
            restricted.append(basis.solve_right(x * basis))
        except ValueError:
            message = f'the subspace is not invariant under the matrix {i}'
            raise CertificateError(message) from None
    return restricted

rewrite_by(lhs, rhs, justification, problem)

Rewrite lhs into rhs, justified by a tactic: rw [show lhs = rhs by tac].

Parameters:

Name Type Description Default
lhs object

The expression to rewrite, a Lean term or a polynomial of the problem.

required
rhs object

Its replacement.

required
justification Lean

A tactic proving lhs = rhs, such as ring1.

required
problem Problem

The problem.

required

Returns:

Type Description
Lean

The tactic.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0'], 'atoms': ['x'], 'goal': {'kind': 'false'}})
>>> str(rewrite_by('x0^2 - 1', term('(x - 1) * (x + 1)'), tactic('ring1'), prob))
'rw [show x ^ 2 - 1 = (x - 1) * (x + 1) by ring1]'
Source code in src/veriphy/toolkit/_emit.py
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def rewrite_by(lhs: object, rhs: object, justification: Lean, problem: Problem) -> Lean:
    """Rewrite `lhs` into `rhs`, justified by a tactic: `rw [show lhs = rhs by tac]`.

    Args:
        lhs: The expression to rewrite, a Lean term or a polynomial of the problem.
        rhs: Its replacement.
        justification: A tactic proving `lhs = rhs`, such as `ring1`.
        problem: The problem.

    Returns:
        The tactic.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0'], 'atoms': ['x'], 'goal': {'kind': 'false'}})
        >>> str(rewrite_by('x0^2 - 1', term('(x - 1) * (x + 1)'), tactic('ring1'), prob))
        'rw [show x ^ 2 - 1 = (x - 1) * (x + 1) by ring1]'
    """
    proof = _expect(justification, 'tactic', 'a rewrite justification')
    return tactic(f'rw [show {_as_term(lhs, problem)} = {_as_term(rhs, problem)} by {proof}]')

rewrite_candidates(problem)

Return the rewrites the problem's tables (core, packs, project) offer for its goal, in table order.

An entry applies when the goal mentions every function it names, and, when it has a pattern, when the pattern matches the goal's symbolic form; the pattern's bindings then fill the side conditions.

Parameters:

Name Type Description Default
problem Problem

The problem.

required

Returns:

Type Description
list[RewriteCandidate]

The applicable rewrites.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'y'], 'carrier': 'ℝ',
...     'hyps': [{'name': 'hx', 'poly': 'x0', 'rel': 'ne', 'text': 'x ≠ 0'},
...              {'name': 'hy', 'poly': 'x1', 'rel': 'ne', 'text': 'y ≠ 0'}],
...     'goal': {'kind': 'other', 'rel': 'eq', 'expr': '(log((x0 * x1))) - ((log(x0) + log(x1)))',
...              'text': 'Real.log (x * y) = Real.log x + Real.log y'}})
>>> [str(c.emit(prob)) for c in rewrite_candidates(prob)]
['rw [Real.log_mul (by assumption) (by assumption)]']
Source code in src/veriphy/toolkit/_symbolic_emit.py
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
def rewrite_candidates(problem: Problem) -> list[RewriteCandidate]:
    """Return the rewrites the problem's tables (core, packs, project) offer for its goal, in table order.

    An entry applies when the goal mentions every function it names, and, when it has a pattern, when the pattern
    matches the goal's symbolic form; the pattern's bindings then fill the side conditions.

    Args:
        problem: The problem.

    Returns:
        The applicable rewrites.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'y'], 'carrier': 'ℝ',
        ...     'hyps': [{'name': 'hx', 'poly': 'x0', 'rel': 'ne', 'text': 'x ≠ 0'},
        ...              {'name': 'hy', 'poly': 'x1', 'rel': 'ne', 'text': 'y ≠ 0'}],
        ...     'goal': {'kind': 'other', 'rel': 'eq', 'expr': '(log((x0 * x1))) - ((log(x0) + log(x1)))',
        ...              'text': 'Real.log (x * y) = Real.log x + Real.log y'}})
        >>> [str(c.emit(prob)) for c in rewrite_candidates(prob)]
        ['rw [Real.log_mul (by assumption) (by assumption)]']
    """
    goal = problem.goal
    text = goal.text if goal is not None else ''
    expr = goal.expr if goal is not None and isinstance(goal.expr, Expression) else None
    found = []
    for table in problem.tables:
        for entry in table.rewrites:
            if not all(_mentions(f, text) for f in entry.functions):
                continue
            side = entry.side
            if entry.pattern is not None:
                bindings = _bindings(entry.pattern, expr) if expr is not None else None
                if bindings is None:
                    continue
                side = tuple(_fill(t, bindings, problem) for t in side)
            found.append(RewriteCandidate(entry.lemma, entry.reverse, side, table.namespace))
    return found

root_fact(which)

Return the fact hζ : ζ ^ m = r for the field's standard root: 1, √5 in ℝ or exp (2πi/16) in ℂ.

Parameters:

Name Type Description Default
which str

'rational', 'sqrt5' or 'zeta16'.

required

Returns:

Type Description
Lean

The fact.

Examples:

>>> print(root_fact('sqrt5'))
have hζ : Real.sqrt 5 ^ 2 = algebraMap ℚ _ (5) := by
  rw [Real.sq_sqrt (by norm_num)]; simp
Source code in src/veriphy/toolkit/_fusion.py
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
def root_fact(which: str) -> Lean:
    """Return the fact `hζ : ζ ^ m = r` for the field's standard root: `1`, `√5` in ℝ or `exp (2πi/16)` in ℂ.

    Args:
        which: `'rational'`, `'sqrt5'` or `'zeta16'`.

    Returns:
        The fact.

    Examples:
        >>> print(root_fact('sqrt5'))
        have hζ : Real.sqrt 5 ^ 2 = algebraMap ℚ _ (5) := by
          rw [Real.sq_sqrt (by norm_num)]; simp
    """
    m, r, _ = FIELDS[which]
    zeta, proof = ROOTS[which]
    return tactic(f'have hζ : {zeta} ^ {m} = algebraMap ℚ _ ({r}) := by\n  {proof}')

root_proof(p_lean, a, b, p)

Return a proof of ∃! x : ℝ, (a < x ∧ x < b) ∧ p x = 0 for an interval from isolate_roots.

Existence is the intermediate value theorem with the endpoint signs; uniqueness is strict monotonicity on [a, b], from the derivative's sign, which nlinarith checks on the interval.

Parameters:

Name Type Description Default
p_lean str

The polynomial as the goal writes it, as a function of x (x ^ 3 - 2 * x - 1).

required
a object

The interval's left end.

required
b object

The interval's right end.

required
p Polynomial

The polynomial (Sage), to read the signs and the derivative.

required

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the signs at the ends agree, or p' vanishes in the interval.

Source code in src/veriphy/toolkit/_numerics.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def root_proof(p_lean: str, a: object, b: object, p: Polynomial) -> Lean:
    """Return a proof of `∃! x : ℝ, (a < x ∧ x < b) ∧ p x = 0` for an interval from `isolate_roots`.

    Existence is the intermediate value theorem with the endpoint signs; uniqueness is strict monotonicity on
    `[a, b]`, from the derivative's sign, which `nlinarith` checks on the interval.

    Args:
        p_lean: The polynomial as the goal writes it, as a function of `x` (`x ^ 3 - 2 * x - 1`).
        a: The interval's left end.
        b: The interval's right end.
        p: The polynomial (Sage), to read the signs and the derivative.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the signs at the ends agree, or `p'` vanishes in the interval.
    """
    a, b = QQ(a), QQ(b)
    p = PolynomialRing(QQ, 'x')(p)
    if p(a) * p(b) >= 0:
        message = f'p({a}) and p({b}) do not have opposite signs: no root is certified on the interval'
        raise CertificateError(message)
    if _critical_in(p, a, b):
        message = "p' vanishes in the interval: split it with isolate_roots"
        raise CertificateError(message)
    increasing = p(a) < 0
    mono = 'StrictMonoOn' if increasing else 'StrictAntiOn'
    of_deriv = 'strictMonoOn_of_deriv_pos' if increasing else 'strictAntiOn_of_deriv_neg'
    ivt = 'intermediate_value_Ioo' if increasing else 'intermediate_value_Ioo\''
    f = f'(fun x : ℝ => {p_lean})'
    derivative = str(p.derivative()).replace('^', ' ^ ').replace('*', ' * ')
    lines = [
        f'have hc : ContinuousOn {f} (Set.Icc {_decimal(a)} {_decimal(b)}) := by fun_prop',
        f'have hmono : {mono} {f} (Set.Icc {_decimal(a)} {_decimal(b)}) := by',
        f'  refine {of_deriv} (convex_Icc _ _) hc fun x hx => ?_',
        '  rw [interior_Icc] at hx',
        f'  have hd : deriv {f} x = {derivative} := by',
        '    simp (disch := fun_prop) [deriv_fun_sub, deriv_fun_add]',
        '    all_goals ring',
        '  rw [hd]; nlinarith [hx.1, hx.2, sq_nonneg x]',
        (
            f'obtain ⟨x, hx, hx0⟩ := {ivt} (by norm_num : {_decimal(a)} ≤ {_decimal(b)}) hc '
            '(by norm_num : (0 : ℝ) ∈ Set.Ioo _ _)'
        ),
        'refine ⟨x, ⟨hx, hx0⟩, fun y ⟨hy, hy0⟩ => ?_⟩',
        'exact hmono.injOn ⟨hy.1.le, hy.2.le⟩ ⟨hx.1.le, hx.2.le⟩ (hy0.trans hx0.symm)',
    ]
    return tactic('\n'.join(lines))

smith_certificate(a)

Return the Smith normal form D = U A V of a square integer matrix, with facts proving it.

Parameters:

Name Type Description Default
a Matrix

A square integer matrix.

required

Returns:

Type Description
Matrix

D, U, V and the facts hsmith : U * A * V = D, hU : U * U⁻¹ = 1, hV : V * V⁻¹ = 1 (the inverses

Matrix

are integer matrices, so U and V are unimodular).

Raises:

Type Description
CertificateError

If the matrix is not square and integral.

Examples:

>>> d, u, v, facts = smith_certificate(matrix(ZZ, [[2, 4], [6, 8]]))
>>> d.diagonal()
[2, 4]
Source code in src/veriphy/toolkit/_normal_form.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def smith_certificate(a: Matrix) -> tuple[Matrix, Matrix, Matrix, Lean]:
    """Return the Smith normal form `D = U A V` of a square integer matrix, with facts proving it.

    Args:
        a: A square integer matrix.

    Returns:
        `D`, `U`, `V` and the facts `hsmith : U * A * V = D`, `hU : U * U⁻¹ = 1`, `hV : V * V⁻¹ = 1` (the inverses
        are integer matrices, so `U` and `V` are unimodular).

    Raises:
        CertificateError: If the matrix is not square and integral.

    Examples:
        >>> d, u, v, facts = smith_certificate(matrix(ZZ, [[2, 4], [6, 8]]))
        >>> d.diagonal()
        [2, 4]
    """
    if not a.is_square() or not all(x in ZZ for x in a.list()):
        message = 'the Smith form certificate needs a square integer matrix'
        raise CertificateError(message)
    d, u, v = matrix(ZZ, a).smith_form()
    terms = {name: rational_matrix_term(m, 'ℤ') for name, m in (('A', a), ('D', d), ('U', u), ('V', v))}
    facts = [
        f'have hsmith : {terms["U"]} * {terms["A"]} * {terms["V"]} = {terms["D"]} := {EQUAL}',
        f'have hU : {terms["U"]} * {rational_matrix_term(u.inverse(), "ℤ")} = 1 := {PRODUCT}',
        f'have hV : {terms["V"]} * {rational_matrix_term(v.inverse(), "ℤ")} = 1 := {PRODUCT}',
    ]
    return d, u, v, tactic('\n'.join(facts))

smul_combination(coefficients, names)

Return the Lean text of Σ cₖ • nameₖ, leaving out zero terms and unit coefficients.

Parameters:

Name Type Description Default
coefficients Sequence[object]

Rational coefficients.

required
names Sequence[str]

The Lean terms they multiply.

required

Returns:

Type Description
str

The text, 0 when every coefficient is zero.

Examples:

>>> smul_combination([2, 0, -1], ['e', 'f', 'h'])
'2 • e - h'
>>> smul_combination([QQ(1) / 2, 1], ['x', 'y'])
'(1 / 2 : ℚ) • x + y'
>>> smul_combination([0, 0], ['x', 'y'])
'0'
Source code in src/veriphy/toolkit/_lie.py
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def smul_combination(coefficients: Sequence[object], names: Sequence[str]) -> str:
    """Return the Lean text of `Σ cₖ • nameₖ`, leaving out zero terms and unit coefficients.

    Args:
        coefficients: Rational coefficients.
        names: The Lean terms they multiply.

    Returns:
        The text, `0` when every coefficient is zero.

    Examples:
        >>> smul_combination([2, 0, -1], ['e', 'f', 'h'])
        '2 • e - h'
        >>> smul_combination([QQ(1) / 2, 1], ['x', 'y'])
        '(1 / 2 : ℚ) • x + y'
        >>> smul_combination([0, 0], ['x', 'y'])
        '0'
    """
    text = ''
    for c, name in zip(coefficients, names, strict=True):
        q = QQ(c)
        if q == 0:
            continue
        sign = '-' if q < 0 else '+'
        size = abs(q)
        term = name if size == 1 else f'{_coefficient(size)} • {name}'
        text = (f'-{term}' if sign == '-' else term) if not text else f'{text} {sign} {term}'
    return text or '0'

so7_spinor_gammas()

Return seven anticommuting real antisymmetric 8 by 8 matrices squaring to -1 (the spinor of so(7)).

Returns:

Type Description
list[Matrix]

The gamma matrices, tensor products of 1, σ₁, σ₃ and ε with an odd number of ε

list[Matrix]

(Cl(0,7) is real, of dimension 8 on each of its two summands).

Raises:

Type Description
CertificateError

If no such set is found (it always is).

Source code in src/veriphy/toolkit/_superalgebra.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
def so7_spinor_gammas() -> list[Matrix]:
    """Return seven anticommuting real antisymmetric 8 by 8 matrices squaring to `-1` (the spinor of so(7)).

    Returns:
        The gamma matrices, tensor products of `1`, `σ₁`, `σ₃` and `ε` with an odd number of `ε`
        (`Cl(0,7)` is real, of dimension 8 on each of its two summands).

    Raises:
        CertificateError: If no such set is found (it always is).
    """
    one, x, z = identity_matrix(QQ, 2), matrix(QQ, [[0, 1], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
    candidates = []
    for factors in itertools.product([one, x, z, EPSILON], repeat=3):
        m = _kron(*factors)
        if m.is_skew_symmetric() and m * m == -identity_matrix(QQ, 8):
            candidates.append(m)

    def extend(chosen: list[Matrix], start: int) -> list[Matrix] | None:
        if len(chosen) == SO7_VECTOR:
            return chosen
        for k in range(start, len(candidates)):
            g = candidates[k]
            if all(g * h + h * g == 0 for h in chosen):
                found = extend([*chosen, g], k + 1)
                if found is not None:
                    return found
        return None

    gammas = extend([], 0)
    if gammas is None:
        message = 'no Clifford basis found'
        raise CertificateError(message)
    return gammas

solve_exact_lp(nvars, eqs=(), les=(), objective=None, nonneg=False, *, maximize=False)

Solve a linear program exactly over the rationals.

A row (coeffs, const) means sum(coeffs[j] * t_j) + const, which is = 0 in eqs and <= 0 in les. A row with only zero coefficients is decided directly, and a problem with no variables or no live rows never reaches the solver backend.

Parameters:

Name Type Description Default
nvars int

The number of variables t_0 .. t_{nvars-1}.

required
eqs Iterable[Row]

The equality rows.

()
les Iterable[Row]

The inequality rows.

()
objective Sequence[object] | None

The objective coefficients, or None for a feasibility problem.

None
nonneg bool | Iterable[int]

True for all variables nonnegative, or the indices of the nonnegative ones.

False
maximize bool

Whether to maximize the objective instead of minimizing it.

False

Returns:

Type Description
list[Rational] | None

The exact solution, or None if the problem is infeasible or unbounded.

Examples:

>>> solve_exact_lp(1, eqs=[([3], -1)])
[1/3]
>>> solve_exact_lp(1, les=[([1], 0), ([-1], 1)]) is None
True
Source code in src/veriphy/toolkit/_lp.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def solve_exact_lp(
    nvars: int,
    eqs: Iterable[Row] = (),
    les: Iterable[Row] = (),
    objective: Sequence[object] | None = None,
    nonneg: bool | Iterable[int] = False,
    *,
    maximize: bool = False,
) -> list[Rational] | None:
    """Solve a linear program exactly over the rationals.

    A row `(coeffs, const)` means `sum(coeffs[j] * t_j) + const`, which is `= 0` in `eqs` and `<= 0` in `les`. A row
    with only zero coefficients is decided directly, and a problem with no variables or no live rows never reaches the
    solver backend.

    Args:
        nvars: The number of variables `t_0 .. t_{nvars-1}`.
        eqs: The equality rows.
        les: The inequality rows.
        objective: The objective coefficients, or `None` for a feasibility problem.
        nonneg: `True` for all variables nonnegative, or the indices of the nonnegative ones.
        maximize: Whether to maximize the objective instead of minimizing it.

    Returns:
        The exact solution, or `None` if the problem is infeasible or unbounded.

    Examples:
        >>> solve_exact_lp(1, eqs=[([3], -1)])
        [1/3]
        >>> solve_exact_lp(1, les=[([1], 0), ([-1], 1)]) is None
        True
    """
    live_eqs, eqs_hold = _split_rows(eqs, equality=True)
    live_les, les_hold = _split_rows(les, equality=False)
    if not (eqs_hold and les_hold):
        return None
    if nvars == 0 or not (live_eqs or live_les):
        return [QQ(0)] * nvars
    lp = MixedIntegerLinearProgram(solver='PPL', maximization=maximize)
    t = lp.new_variable()
    for j in range(nvars) if nonneg is True else (nonneg or ()):
        lp.add_constraint(t[j] >= 0)
    for coeffs, const in live_eqs:
        lp.add_constraint(lp.sum(c * t[j] for j, c in enumerate(coeffs) if c != 0) == -const)
    for coeffs, const in live_les:
        lp.add_constraint(lp.sum(c * t[j] for j, c in enumerate(coeffs) if c != 0) <= -const)
    weights = [QQ(c) for c in objective] if objective is not None else []
    lp.set_objective(lp.sum(c * t[j] for j, c in enumerate(weights) if c != 0) if any(weights) else None)
    try:
        lp.solve()
    except MIPSolverException:
        return None
    return [QQ(lp.get_values(t[j])) for j in range(nvars)]

solve_exists(problem, proofs=None, solution=None)

Prove ∃ x, (∀ t, ODE) ∧ conditions by solving the equation with Sage and checking the solution.

Sage's desolve finds the solution with the initial conditions (or solution is used, a body in s_); the proof supplies it as the witness, states its derivatives as facts, and closes each conjunct.

Parameters:

Name Type Description Default
problem Problem

The problem; its goal is an existential over an unknown function.

required
proofs Mapping[str, str] | None

Tactics proving nonzero conditions no hypothesis states, by their Lean text.

None
solution Expression | None

A solution body in the variable s_, instead of Sage's.

None

Returns:

Type Description
Lean

The tactic.

Raises:

Type Description
CertificateError

If the goal is not such an existential, or Sage finds no solution.

Source code in src/veriphy/toolkit/_ode.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
def solve_exists(
    problem: Problem,
    proofs: Mapping[str, str] | None = None,
    solution: Expression | None = None,
) -> Lean:
    """Prove `∃ x, (∀ t, ODE) ∧ conditions` by solving the equation with Sage and checking the solution.

    Sage's `desolve` finds the solution with the initial conditions (or `solution` is used, a body in `s_`); the proof
    supplies it as the witness, states its derivatives as facts, and closes each conjunct.

    Args:
        problem: The problem; its goal is an existential over an unknown function.
        proofs: Tactics proving nonzero conditions no hypothesis states, by their Lean text.
        solution: A solution body in the variable `s_`, instead of Sage's.

    Returns:
        The tactic.

    Raises:
        CertificateError: If the goal is not such an existential, or Sage finds no solution.
    """
    ef = problem.exists_function
    if ef is None:
        message = 'the goal is not an existential over an unknown function'
        raise CertificateError(message)
    f = problem.symbols[ef.function]
    t, s = SR.var('t'), SR.var('s_')
    equations = [e.subs({v: t}) for v, e in ef.conjuncts if v is not None and e is not None]
    conditions = [e for v, e in ef.conjuncts if v is None and e is not None]
    if not equations:
        message = 'the existential has no equation `∀ t, …` in the function'
        raise CertificateError(message)
    order = max(derivative_order(e, ef.function) for e in equations)
    if solution is None:
        ics = _initial_values(conditions, ef.function, order) if conditions else None
        assumed = _assumptions(problem)
        assume(*assumed) if assumed else None
        try:
            found = desolve(equations[0] == 0, f(t), ics=ics, ivar=t)
        finally:
            forget(*assumed) if assumed else None
        solution = SR(found).subs({t: s})
    facts, names = derivative_chain(solution, s, order, problem, proofs=proofs)
    witness = _lambda(solution, s, problem)
    listed = ', '.join(names)
    # each proof is parenthesized: a printed replacement may break its line after `;`, which `by` alone rejects
    parts = []
    for v, _ in ef.conjuncts:
        rewrite = f'simp only [{listed}]; ' if names else ''
        if v is not None:
            parts.append(f'fun {str(v).rstrip("_")} => by ({rewrite}{EQUATION_CLOSE})')
        else:
            parts.append(f'by ({CONDITION_CLOSE.format(names=listed)})' if names else 'by norm_num')
    lines = [*facts, tactic(f'exact ⟨{witness}, {", ".join(parts)}⟩')]
    return tactic('\n'.join(str(line) for line in lines))

solve_recurrence(coefficients, initial)

Return the closed form of a(n + d) = c₀ a(n) + … + c_{d-1} a(n + d - 1) with initial values.

Parameters:

Name Type Description Default
coefficients Sequence[object]

c₀, …, c_{d-1}, constants or polynomials in n.

required
initial Sequence[object]

a(0), …, a(d - 1).

required

Returns:

Type Description
Expr

The closed form in n.

Raises:

Type Description
CertificateError

If sympy finds none.

Examples:

>>> solve_recurrence([-6, 5], [0, 1])
-2**n + 3**n
Source code in src/veriphy/toolkit/_summation.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def solve_recurrence(coefficients: Sequence[object], initial: Sequence[object]) -> sp.Expr:
    """Return the closed form of `a(n + d) = c₀ a(n) + … + c_{d-1} a(n + d - 1)` with initial values.

    Args:
        coefficients: `c₀, …, c_{d-1}`, constants or polynomials in `n`.
        initial: `a(0), …, a(d - 1)`.

    Returns:
        The closed form in `n`.

    Raises:
        CertificateError: If sympy finds none.

    Examples:
        >>> solve_recurrence([-6, 5], [0, 1])
        -2**n + 3**n
    """
    a = sp.Function('a')
    d = len(coefficients)
    equation = a(N + d) - sum(_sympy(c) * a(N + i) for i, c in enumerate(coefficients))
    solution = sp.rsolve(equation, a(N), {a(i): v for i, v in enumerate(initial)})
    if solution is None:
        message = 'no closed form for the recurrence; guess one with guess_recurrence and check it'
        raise CertificateError(message)
    return sp.simplify(solution)

sos_decomposition(p)

Find an exact sum of squares p = Σ cᵢ qᵢ² with rational cᵢ > 0, through a numeric SDP.

The Gram matrix is parametrized exactly over QQ. The SDP maximizes its smallest eigenvalue margin, and the weights are rounded to rationals at increasing precision until the exact matrix is positive semidefinite. The squares come from its exact LDLᵀ factorization and are checked to sum to p. A polynomial with real zeros whose Gram matrices all lie on the boundary of the positive semidefinite cone with an irrational kernel cannot be rounded exactly, and is reported as a failure. Supply candidate squares to the exact LP instead.

Parameters:

Name Type Description Default
p MPolynomial

A polynomial over QQ.

required

Returns:

Type Description
list[tuple[Rational, MPolynomial]]

The (cᵢ, qᵢ) pairs.

Raises:

Type Description
CertificateError

If no exact sum of squares is found.

Examples:

>>> from veriphy.toolkit import create_ring
>>> x, y = create_ring(['x', 'y']).gens()
>>> p = x**4 + 2*x**2*y**2 + y**4 - x**2 - 2*x*y - y**2 + QQ(11) / 10
>>> sum(c * q**2 for c, q in sos_decomposition(p)) == p
True
Source code in src/veriphy/toolkit/_sos.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def sos_decomposition(p: MPolynomial) -> list[tuple[Rational, MPolynomial]]:
    """Find an exact sum of squares `p = Σ cᵢ qᵢ²` with rational `cᵢ > 0`, through a numeric SDP.

    The Gram matrix is parametrized exactly over QQ. The SDP maximizes its smallest eigenvalue margin, and the
    weights are rounded to rationals at increasing precision until the exact matrix is positive semidefinite. The
    squares come from its exact LDLᵀ factorization and are checked to sum to `p`. A polynomial with real zeros whose
    Gram matrices all lie on the boundary of the positive semidefinite cone with an irrational kernel cannot be
    rounded exactly, and is reported as a failure. Supply candidate squares to the exact LP instead.

    Args:
        p: A polynomial over QQ.

    Returns:
        The `(cᵢ, qᵢ)` pairs.

    Raises:
        CertificateError: If no exact sum of squares is found.

    Examples:
        >>> from veriphy.toolkit import create_ring
        >>> x, y = create_ring(['x', 'y']).gens()
        >>> p = x**4 + 2*x**2*y**2 + y**4 - x**2 - 2*x*y - y**2 + QQ(11) / 10
        >>> sum(c * q**2 for c, q in sos_decomposition(p)) == p
        True
    """
    if p == 0:
        return []
    basis = _half_basis(p)
    q0, directions = _gram_space(p, basis)
    weights, margin = _maximize_margin(q0, directions)
    if margin < MARGIN_TOLERANCE and directions:
        message = f'the polynomial is not a sum of squares (best Gram margin {margin:.3g} < 0)'
        raise CertificateError(message)
    for denominator in DENOMINATORS:
        q = q0 + sum((_round(w, denominator) * n for w, n in zip(weights, directions, strict=True)), q0.parent()(0))
        if q.is_positive_semidefinite():
            squares = _squares(q, basis)
            if sum(c * s**2 for c, s in squares) == p:
                return squares
    message = 'rounding the SDP solution did not give an exact positive semidefinite Gram matrix'
    raise CertificateError(message)

sparse_rows_term(table, ring=None)

Return the rows of a table, the nonzero (k, c) of each [eᵢ, eⱼ}, as a Lean term.

Parameters:

Name Type Description Default
table Table

Rational structure constants.

required
ring str | None

ℤ or ℚ; ℤ when every constant is an integer and None is given.

None

Returns:

Type Description
str

The term Fin n → Fin n → List (Fin n × R).

Examples:

>>> rows = sparse_rows_term([[[0, 0], [0, 1]], [[0, -1], [0, 0]]])
>>> rows.startswith('(fun (i j : Fin 2) => ((([[[], [(1, 1)]], [[(1, -1)], []]] : List (List (List (Fin 2 × ℤ')
True
Source code in src/veriphy/toolkit/_superalgebra.py
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
def sparse_rows_term(table: Table, ring: str | None = None) -> str:
    """Return the rows of a table, the nonzero `(k, c)` of each `[eᵢ, eⱼ}`, as a Lean term.

    Args:
        table: Rational structure constants.
        ring: `ℤ` or `ℚ`; `ℤ` when every constant is an integer and `None` is given.

    Returns:
        The term `Fin n → Fin n → List (Fin n × R)`.

    Examples:
        >>> rows = sparse_rows_term([[[0, 0], [0, 1]], [[0, -1], [0, 0]]])
        >>> rows.startswith('(fun (i j : Fin 2) => ((([[[], [(1, 1)]], [[(1, -1)], []]] : List (List (List (Fin 2 × ℤ')
        True
    """
    n = len(table)
    integral = all(QQ(c).denominator() == 1 for plane in table for row in plane for c in row)
    chosen = ring or ('ℤ' if integral else 'ℚ')

    def coefficient(c: object) -> str:
        q = QQ(c)
        return str(q) if q.denominator() == 1 else f'{q.numerator()} / {q.denominator()}'

    def row(entries: Sequence[object]) -> str:
        return '[' + ', '.join(f'({k}, {coefficient(c)})' for k, c in enumerate(entries) if c != 0) + ']'

    # nested lists indexed by `i.val`: the kernel walks a list literal faster than it unfolds `![…]` vectors
    rows = ', '.join('[' + ', '.join(row(table[i][j]) for j in range(n)) + ']' for i in range(n))
    entry = f'Fin {n} × {chosen}'
    return f'(fun (i j : Fin {n}) => ((([{rows}] : List (List (List ({entry})))).getD i.val []).getD j.val []))'

spin_representation(two_j)

Return [e, f, h] of 𝔰𝔩₂ on spin j = two_j / 2, in the basis vₖ = fᵏ v / k! (k = 0 … 2j).

Parameters:

Name Type Description Default
two_j int

Twice the spin.

required

Returns:

Type Description
Rep

The three matrices, h vₖ = (2j - 2k) vₖ, f vₖ = (k + 1) vₖ₊₁, e vₖ = (2j - k + 1) vₖ₋₁.

Examples:

>>> [m.list() for m in spin_representation(1)]
[[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, -1]]
Source code in src/veriphy/toolkit/_representation.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def spin_representation(two_j: int) -> Rep:
    """Return `[e, f, h]` of 𝔰𝔩₂ on spin `j = two_j / 2`, in the basis `vₖ = fᵏ v / k!` (`k = 0 … 2j`).

    Args:
        two_j: Twice the spin.

    Returns:
        The three matrices, `h vₖ = (2j - 2k) vₖ`, `f vₖ = (k + 1) vₖ₊₁`, `e vₖ = (2j - k + 1) vₖ₋₁`.

    Examples:
        >>> [m.list() for m in spin_representation(1)]
        [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, -1]]
    """
    n = two_j + 1
    e, f, h = (zero_matrix(QQ, n) for _ in range(3))
    for k in range(n):
        h[k, k] = two_j - 2 * k
        if k + 1 < n:
            f[k + 1, k] = k + 1
        if k > 0:
            e[k - 1, k] = two_j - k + 1
    return [e, f, h]

sq_nonneg(q, problem)

Build the proof term sq_nonneg (q).

Parameters:

Name Type Description Default
q object

The expression squared, a Lean term or a polynomial of the problem.

required
problem Problem

The problem.

required

Returns:

Type Description
Lean

The term.

Examples:

>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['a', 'b'], 'goal': {'kind': 'false'}})
>>> str(sq_nonneg('x0 - x1', prob))
'sq_nonneg (a - b)'
Source code in src/veriphy/toolkit/_emit.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
def sq_nonneg(q: object, problem: Problem) -> Lean:
    """Build the proof term `sq_nonneg (q)`.

    Args:
        q: The expression squared, a Lean term or a polynomial of the problem.
        problem: The problem.

    Returns:
        The term.

    Examples:
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['a', 'b'], 'goal': {'kind': 'false'}})
        >>> str(sq_nonneg('x0 - x1', prob))
        'sq_nonneg (a - b)'
    """
    return term(f'sq_nonneg ({_as_term(q, problem)})')

steps(*blocks)

Sequence tactic blocks, one after another.

Parameters:

Name Type Description Default
*blocks Lean

The tactics, in order.

()

Returns:

Type Description
Lean

The combined tactic block.

Raises:

Type Description
VeriphyError

If there are no blocks.

Examples:

>>> print(steps(tactic('intro x'), tactic('positivity')))
intro x
positivity
Source code in src/veriphy/toolkit/_emit.py
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
def steps(*blocks: Lean) -> Lean:
    """Sequence tactic blocks, one after another.

    Args:
        *blocks: The tactics, in order.

    Returns:
        The combined tactic block.

    Raises:
        VeriphyError: If there are no blocks.

    Examples:
        >>> print(steps(tactic('intro x'), tactic('positivity')))
        intro x
        positivity
    """
    if not blocks:
        message = 'steps needs at least one tactic'
        raise VeriphyError(message)
    return tactic('\n'.join(_expect(b, 'tactic', 'a step').text for b in blocks))

structure_constants(basis)

Return c[i][j][k], the coefficient of eₖ in ⁅eᵢ, eⱼ⁆ = eᵢ eⱼ - eⱼ eᵢ.

Parameters:

Name Type Description Default
basis Sequence[Matrix]

Square matrices of one size, over ℚ, a number field or Sage's symbolic ring.

required

Returns:

Type Description
Table

The table, with entries in the matrices' ring.

Raises:

Type Description
CertificateError

If the basis is dependent, or not closed under the bracket (naming the first pair whose bracket leaves the span).

Examples:

>>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
>>> c = structure_constants([e, f, h])
>>> c[2][0], c[2][1], c[0][1]
([2, 0, 0], [0, -2, 0], [0, 0, 1])
>>> structure_constants([e, f])
Traceback (most recent call last):
...
veriphy.core._errors.CertificateError: the basis is not closed under the bracket: ⁅e0, e1⁆ is not in its span
Source code in src/veriphy/toolkit/_lie.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def structure_constants(basis: Sequence[Matrix]) -> Table:
    """Return `c[i][j][k]`, the coefficient of `eₖ` in `⁅eᵢ, eⱼ⁆ = eᵢ eⱼ - eⱼ eᵢ`.

    Args:
        basis: Square matrices of one size, over ℚ, a number field or Sage's symbolic ring.

    Returns:
        The table, with entries in the matrices' ring.

    Raises:
        CertificateError: If the basis is dependent, or not closed under the bracket (naming the first pair whose
            bracket leaves the span).

    Examples:
        >>> e, f, h = matrix(QQ, [[0, 1], [0, 0]]), matrix(QQ, [[0, 0], [1, 0]]), matrix(QQ, [[1, 0], [0, -1]])
        >>> c = structure_constants([e, f, h])
        >>> c[2][0], c[2][1], c[0][1]
        ([2, 0, 0], [0, -2, 0], [0, 0, 1])
        >>> structure_constants([e, f])
        Traceback (most recent call last):
        ...
        veriphy.core._errors.CertificateError: the basis is not closed under the bracket: ⁅e0, e1⁆ is not in its span
    """
    rows = _coordinates(basis)
    ring = rows.base_ring()
    table: Table = []
    for i, a in enumerate(basis):
        table.append([])
        for j, b in enumerate(basis):
            bracket = vector(ring, (a * b - b * a).list())
            try:
                solution = rows.solve_left(bracket)
            except ValueError:
                message = f'the basis is not closed under the bracket: ⁅e{i}, e{j}⁆ is not in its span'
                raise CertificateError(message) from None
            table[i].append(list(solution))
    return table

structure_constants_term(table)

Return a structure-constant table as a Lean term Fin n → Fin n → Fin n → ℤ (or ℚ when not integral).

VeriphyCheck.jacobi_of_check (by decide +kernel) and bracket_self_of_check then prove the Lie axioms for it.

Parameters:

Name Type Description Default
table Sequence[Sequence[Sequence[object]]]

table[i][j][k], the coefficient of e_k in ⁅e_i, e_j⁆, rational.

required

Returns:

Type Description
Lean

The term.

Examples:

>>> print(structure_constants_term([[[0, 0], [0, 1]], [[0, -1], [0, 0]]]))
(![![![0, 0], ![0, 1]], ![![0, -1], ![0, 0]]] : Fin 2 → Fin 2 → Fin 2 → ℤ)
>>> print(structure_constants_term([[[0, 0], [0, 1/2]], [[0, -1/2], [0, 0]]]))
(![![![0, 0], ![0, (1 / 2)]], ![![0, (-1 / 2)], ![0, 0]]] : Fin 2 → Fin 2 → Fin 2 → ℚ)
Source code in src/veriphy/toolkit/_reflect.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
def structure_constants_term(table: Sequence[Sequence[Sequence[object]]]) -> Lean:
    """Return a structure-constant table as a Lean term `Fin n → Fin n → Fin n → ℤ` (or `ℚ` when not integral).

    `VeriphyCheck.jacobi_of_check (by decide +kernel)` and `bracket_self_of_check` then prove the Lie axioms for it.

    Args:
        table: `table[i][j][k]`, the coefficient of `e_k` in `⁅e_i, e_j⁆`, rational.

    Returns:
        The term.

    Examples:
        >>> print(structure_constants_term([[[0, 0], [0, 1]], [[0, -1], [0, 0]]]))
        (![![![0, 0], ![0, 1]], ![![0, -1], ![0, 0]]] : Fin 2 → Fin 2 → Fin 2 → ℤ)
        >>> print(structure_constants_term([[[0, 0], [0, 1/2]], [[0, -1/2], [0, 0]]]))
        (![![![0, 0], ![0, (1 / 2)]], ![![0, (-1 / 2)], ![0, 0]]] : Fin 2 → Fin 2 → Fin 2 → ℚ)
    """
    n = len(table)
    ring = 'ℤ' if all(QQ(x).denominator() == 1 for plane in table for row in plane for x in row) else 'ℚ'

    def rat(x: object) -> str:
        q = QQ(x)
        return str(q) if q.denominator() == 1 else f'({q.numerator()} / {q.denominator()})'

    rows = ', '.join('![' + ', '.join('![' + ', '.join(rat(x) for x in r) + ']' for r in m) + ']' for m in table)
    return term(f'(![{rows}] : Fin {n} → Fin {n} → Fin {n} → {ring})')

sum_by_induction(variable, index_set='range', lower=0)

Prove ∑ k ∈ s n, f k = F n by induction on n, each case by push_cast and ring1.

The index set s n is Finset.range n, Finset.Icc lower n or Finset.Ico lower n, with lower 0 or 1 (a sum from elsewhere is first rewritten to start there, for example with Finset.sum_Ico_eq_sum_range).

Parameters:

Name Type Description Default
variable str

The name of the natural-number variable n.

required
index_set str

range, Icc or Ico.

'range'
lower int

The lower bound of an Icc or Ico sum, 0 or 1.

0

Returns:

Type Description
Lean

The tactic block.

Raises:

Type Description
VeriphyError

If the index set or lower bound is not supported.

Examples:

>>> print(sum_by_induction('n'))
induction n with
| zero => rw [Finset.sum_range_zero]; (try push_cast); ring1
| succ n ih => rw [Finset.sum_range_succ, ih]; (try push_cast); ring1
>>> print(sum_by_induction('n', 'Icc', 1))
induction n with
| zero => simp
| succ n ih => rw [Finset.sum_Icc_succ_top (by omega), ih]; (try push_cast); ring1
Source code in src/veriphy/toolkit/_emit.py
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
def sum_by_induction(variable: str, index_set: str = 'range', lower: int = 0) -> Lean:
    """Prove `∑ k ∈ s n, f k = F n` by induction on `n`, each case by `push_cast` and `ring1`.

    The index set `s n` is `Finset.range n`, `Finset.Icc lower n` or `Finset.Ico lower n`, with `lower` 0 or 1 (a
    sum from elsewhere is first rewritten to start there, for example with `Finset.sum_Ico_eq_sum_range`).

    Args:
        variable: The name of the natural-number variable `n`.
        index_set: `range`, `Icc` or `Ico`.
        lower: The lower bound of an `Icc` or `Ico` sum, 0 or 1.

    Returns:
        The tactic block.

    Raises:
        VeriphyError: If the index set or lower bound is not supported.

    Examples:
        >>> print(sum_by_induction('n'))
        induction n with
        | zero => rw [Finset.sum_range_zero]; (try push_cast); ring1
        | succ n ih => rw [Finset.sum_range_succ, ih]; (try push_cast); ring1
        >>> print(sum_by_induction('n', 'Icc', 1))
        induction n with
        | zero => simp
        | succ n ih => rw [Finset.sum_Icc_succ_top (by omega), ih]; (try push_cast); ring1
    """
    if index_set == 'range':
        return tactic(
            f'induction {variable} with\n'
            '| zero => rw [Finset.sum_range_zero]; (try push_cast); ring1\n'
            f'| succ {variable} ih => rw [Finset.sum_range_succ, ih]; (try push_cast); ring1',
        )
    if index_set not in ('Icc', 'Ico') or lower not in (0, 1):
        message = (
            f'sum_by_induction handles range, and Icc or Ico from 0 or 1, not {index_set} from {lower}; '
            'rewrite the sum with Finset.sum_Ico_eq_sum_range first'
        )
        raise VeriphyError(message)
    if index_set == 'Icc' or lower == 0:
        return tactic(
            f'induction {variable} with\n'
            '| zero => simp\n'
            f'| succ {variable} ih => rw [Finset.sum_{index_set}_succ_top (by omega), ih]; (try push_cast); ring1',
        )
    return tactic(
        f'induction {variable} with\n'
        '| zero => simp\n'
        f'| succ {variable} ih =>\n'
        f'  rcases Nat.eq_zero_or_pos {variable} with h | h\n'
        '  · subst h; simp\n'
        '  · rw [Finset.sum_Ico_succ_top h, ih]; (try push_cast); ring1',
    )

super_casimir(basis, parities, form)

Return the quadratic Casimir Σ (B⁻¹) eᵢ eⱼ for an invariant form, checked to commute with every basis element.

The order of the inverse form's indices differs between conventions for superalgebras; Sage tries both and keeps the one that commutes.

Parameters:

Name Type Description Default
basis Sequence[Matrix]

Homogeneous supermatrices.

required
parities Sequence[int]

Their parities.

required
form Matrix

An invariant nondegenerate form on the basis.

required

Returns:

Type Description
Matrix

The Casimir (an even supermatrix).

Raises:

Type Description
CertificateError

If the form is degenerate, or neither ordering commutes.

Source code in src/veriphy/toolkit/_superalgebra.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def super_casimir(basis: Sequence[Matrix], parities: Sequence[int], form: Matrix) -> Matrix:
    """Return the quadratic Casimir `Σ (B⁻¹) eᵢ eⱼ` for an invariant form, checked to commute with every basis element.

    The order of the inverse form's indices differs between conventions for superalgebras; Sage tries both and keeps
    the one that commutes.

    Args:
        basis: Homogeneous supermatrices.
        parities: Their parities.
        form: An invariant nondegenerate form on the basis.

    Returns:
        The Casimir (an even supermatrix).

    Raises:
        CertificateError: If the form is degenerate, or neither ordering commutes.
    """
    if form.det() == 0:
        message = 'the invariant form is degenerate'
        raise CertificateError(message)
    inverse = form.inverse()
    n = len(basis)
    for transposed in (False, True):
        c = sum(
            (
                (inverse[j, i] if transposed else inverse[i, j]) * basis[i] * basis[j]
                for i in range(n)
                for j in range(n)
            ),
            0 * basis[0],
        )
        if all(c * x == x * c for x in basis):
            return c
    message = 'no ordering of the inverse form gives a commuting Casimir (is the form invariant?)'
    raise CertificateError(message)

super_jacobi_certificate(table, parities, name='hsuper')

Return a fact that the structure constants satisfy the super-Jacobi identity on every basis triple.

Sage checks it first; the kernel checks it again on the sparse table (VeriphyCheck.Sparse.superJacobiCheck), at a cost that follows the nonzero products of constants.

Parameters:

Name Type Description Default
table Table

Rational structure constants (integers check fastest; see integral_rescaling).

required
parities Sequence[int]

The basis parities.

required
name str

The fact's name.

'hsuper'

Returns:

Type Description
Lean

The fact ∀ i j k m, VeriphyCheck.superJacobiAt (VeriphyCheck.Sparse.table r) p i j k m = 0.

Raises:

Type Description
CertificateError

If the identity fails in Sage, naming a violation.

Source code in src/veriphy/toolkit/_superalgebra.py
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
def super_jacobi_certificate(table: Table, parities: Sequence[int], name: str = 'hsuper') -> Lean:
    """Return a fact that the structure constants satisfy the super-Jacobi identity on every basis triple.

    Sage checks it first; the kernel checks it again on the sparse table (`VeriphyCheck.Sparse.superJacobiCheck`),
    at a cost that follows the nonzero products of constants.

    Args:
        table: Rational structure constants (integers check fastest; see `integral_rescaling`).
        parities: The basis parities.
        name: The fact's name.

    Returns:
        The fact `∀ i j k m, VeriphyCheck.superJacobiAt (VeriphyCheck.Sparse.table r) p i j k m = 0`.

    Raises:
        CertificateError: If the identity fails in Sage, naming a violation.
    """
    violations = super_jacobi_violations(table, parities)
    if violations:
        message = f'super-Jacobi fails at (i, j, k, m) = {violations[0]} and {len(violations) - 1} other places'
        raise CertificateError(message)
    return tactic(
        f'have {name} := VeriphyCheck.Sparse.superJacobi_of_check (r := {sparse_rows_term(table)}) '
        f'(p := {_parities_term(parities)}) (by decide +kernel)',
    )

super_jacobi_family_certificate(table0, table1, parities, name='hfamily', field='ℝ')

Return a fact that c₀ + α c₁ satisfies super-Jacobi for every α in any field of characteristic zero.

The super-Jacobi sums are quadratic in α, so the kernel checks at α = 0, 1 and -1 decide them.

Parameters:

Name Type Description Default
table0 Table

The constant part of the structure constants.

required
table1 Table

The part linear in α.

required
parities Sequence[int]

The basis parities.

required
name str

The fact's name.

'hfamily'
field str

The field of characteristic zero α ranges over.

'ℝ'

Returns:

Type Description
Lean

The fact ∀ α i j k m, VeriphyCheck.superJacobiAt (c₀ + α c₁) p i j k m = 0, over the sparse tables.

Raises:

Type Description
CertificateError

If the identity fails in Sage at one of the three values.

Source code in src/veriphy/toolkit/_superalgebra.py
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
def super_jacobi_family_certificate(
    table0: Table,
    table1: Table,
    parities: Sequence[int],
    name: str = 'hfamily',
    field: str = 'ℝ',
) -> Lean:
    """Return a fact that `c₀ + α c₁` satisfies super-Jacobi for every `α` in any field of characteristic zero.

    The super-Jacobi sums are quadratic in `α`, so the kernel checks at `α = 0`, `1` and `-1` decide them.

    Args:
        table0: The constant part of the structure constants.
        table1: The part linear in `α`.
        parities: The basis parities.
        name: The fact's name.
        field: The field of characteristic zero `α` ranges over.

    Returns:
        The fact `∀ α i j k m, VeriphyCheck.superJacobiAt (c₀ + α c₁) p i j k m = 0`, over the sparse tables.

    Raises:
        CertificateError: If the identity fails in Sage at one of the three values.
    """
    n = len(table0)
    for t in (0, 1, -1):
        shifted = [[[table0[i][j][k] + t * table1[i][j][k] for k in range(n)] for j in range(n)] for i in range(n)]
        if super_jacobi_violations(shifted, parities):
            message = f'super-Jacobi fails at α = {t}'
            raise CertificateError(message)
    checks = ' '.join(['(by decide +kernel)'] * 3)
    return tactic(
        f'have {name} := VeriphyCheck.Sparse.superJacobi_affine (r₀ := {sparse_rows_term(table0, "ℚ")}) '
        f'(r₁ := {sparse_rows_term(table1, "ℚ")}) (p := {_parities_term(parities)}) {checks} (K := {field})',
    )

super_jacobi_violations(table, parities)

Return the basis triples and coordinates (i, j, k, m) where the super-Jacobi identity fails.

The identity is (-1)^(|i||k|) [eᵢ, [eⱼ, eₖ}} + (-1)^(|j||i|) [eⱼ, [eₖ, eᵢ}} + (-1)^(|k||j|) [eₖ, [eᵢ, eⱼ}} = 0.

Parameters:

Name Type Description Default
table Table

Structure constants.

required
parities Sequence[int]

The basis parities.

required

Returns:

Type Description
list[tuple[int, int, int, int]]

The violations, empty for a Lie superalgebra.

Source code in src/veriphy/toolkit/_superalgebra.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
def super_jacobi_violations(table: Table, parities: Sequence[int]) -> list[tuple[int, int, int, int]]:
    """Return the basis triples and coordinates `(i, j, k, m)` where the super-Jacobi identity fails.

    The identity is `(-1)^(|i||k|) [eᵢ, [eⱼ, eₖ}} + (-1)^(|j||i|) [eⱼ, [eₖ, eᵢ}} + (-1)^(|k||j|) [eₖ, [eᵢ, eⱼ}} = 0`.

    Args:
        table: Structure constants.
        parities: The basis parities.

    Returns:
        The violations, empty for a Lie superalgebra.
    """
    n = len(table)
    support = [[[(ell, x) for ell, x in enumerate(table[i][j]) if x != 0] for j in range(n)] for i in range(n)]

    def nested(a: int, b: int, c: int) -> list[RingElement]:
        result = [0] * n
        for ell, x in support[b][c]:
            for m, y in support[a][ell]:
                result[m] += x * y
        return result

    violations = []
    for i, j, k in itertools.product(range(n), repeat=3):
        p = parities
        terms = [
            ((-1) ** (p[i] * p[k]), nested(i, j, k)),
            ((-1) ** (p[j] * p[i]), nested(j, k, i)),
            ((-1) ** (p[k] * p[j]), nested(k, i, j)),
        ]
        for m in range(n):
            if sum(s * t[m] for s, t in terms) != 0:
                violations.append((i, j, k, m))
    return violations

super_relations(basis, parities, names)

Return facts stating each graded bracket of a basis of supermatrices, checked by the kernel.

An odd pair gets the anticommutator x * y + y * x = …, any other the commutator x * y - y * x = ….

Parameters:

Name Type Description Default
basis Sequence[Matrix]

Homogeneous supermatrices over ℚ.

required
parities Sequence[int]

Their parities.

required
names Sequence[str]

Their Lean names (definitions).

required

Returns:

Type Description
Lean

The facts hs_i_j for i ≤ j.

Source code in src/veriphy/toolkit/_superalgebra.py
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
def super_relations(basis: Sequence[Matrix], parities: Sequence[int], names: Sequence[str]) -> Lean:
    """Return facts stating each graded bracket of a basis of supermatrices, checked by the kernel.

    An odd pair gets the anticommutator `x * y + y * x = …`, any other the commutator `x * y - y * x = …`.

    Args:
        basis: Homogeneous supermatrices over ℚ.
        parities: Their parities.
        names: Their Lean names (definitions).

    Returns:
        The facts `hs_i_j` for `i ≤ j`.
    """
    table = super_structure_constants(basis, parities)
    lines = []
    for i, j in itertools.combinations_with_replacement(range(len(basis)), 2):
        sign = '+' if parities[i] and parities[j] else '-'
        rhs = smul_combination(table[i][j], names)
        lines.append(
            f'have hs_{i}_{j} : {names[i]} * {names[j]} {sign} {names[j]} * {names[i]} = {rhs} := '
            'VeriphyCheck.eq_of_check (by decide +kernel)',
        )
    return tactic('\n'.join(lines))

super_structure_constants(basis, parities)

Return the structure constants of the graded commutator on a basis of supermatrices.

Parameters:

Name Type Description Default
basis Sequence[Matrix]

Homogeneous supermatrices.

required
parities Sequence[int]

Their parities.

required

Returns:

Type Description
Table

c[i][j][k], the coefficient of eₖ in [eᵢ, eⱼ}.

Raises:

Type Description
CertificateError

If a bracket leaves the span, or has the wrong parity (naming the pair).

Examples:

>>> h, e = matrix(QQ, [[1, 0], [0, 1]]), matrix(QQ, [[0, 1], [0, 0]])
>>> f = matrix(QQ, [[0, 0], [1, 0]])
>>> c = super_structure_constants([h, e, f], [0, 1, 1])
>>> c[1][2]
[1, 0, 0]
Source code in src/veriphy/toolkit/_superalgebra.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def super_structure_constants(basis: Sequence[Matrix], parities: Sequence[int]) -> Table:
    """Return the structure constants of the graded commutator on a basis of supermatrices.

    Args:
        basis: Homogeneous supermatrices.
        parities: Their parities.

    Returns:
        `c[i][j][k]`, the coefficient of `eₖ` in `[eᵢ, eⱼ}`.

    Raises:
        CertificateError: If a bracket leaves the span, or has the wrong parity (naming the pair).

    Examples:
        >>> h, e = matrix(QQ, [[1, 0], [0, 1]]), matrix(QQ, [[0, 1], [0, 0]])
        >>> f = matrix(QQ, [[0, 0], [1, 0]])
        >>> c = super_structure_constants([h, e, f], [0, 1, 1])
        >>> c[1][2]
        [1, 0, 0]
    """
    rows = matrix([m.list() for m in basis])
    if rows.rank() < len(basis):
        message = 'the basis supermatrices are linearly dependent'
        raise CertificateError(message)
    table: Table = []
    for i, (a, pa) in enumerate(zip(basis, parities, strict=True)):
        table.append([])
        for j, (b, pb) in enumerate(zip(basis, parities, strict=True)):
            bracket = graded_commutator(a, b, pa, pb)
            try:
                coefficients = list(rows.solve_left(vector(bracket.list())))
            except ValueError:
                message = f'the basis is not closed under the graded bracket: [e{i}, e{j}}} is not in its span'
                raise CertificateError(message) from None
            if any(c != 0 and parities[k] != (pa + pb) % 2 for k, c in enumerate(coefficients)):
                message = f'[e{i}, e{j}}} has the wrong parity'
                raise CertificateError(message)
            table[i].append(coefficients)
    return table

supertrace(m, even_dim)

Return the supertrace tr A - tr D of a supermatrix [[A, B], [C, D]] with A of size even_dim.

Parameters:

Name Type Description Default
m Matrix

The supermatrix.

required
even_dim int

The dimension of the even part.

required

Returns:

Type Description
object

The supertrace.

Examples:

>>> supertrace(identity_matrix(QQ, 3), 1)
-1
Source code in src/veriphy/toolkit/_superalgebra.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def supertrace(m: Matrix, even_dim: int) -> object:
    """Return the supertrace `tr A - tr D` of a supermatrix `[[A, B], [C, D]]` with `A` of size `even_dim`.

    Args:
        m: The supermatrix.
        even_dim: The dimension of the even part.

    Returns:
        The supertrace.

    Examples:
        >>> supertrace(identity_matrix(QQ, 3), 1)
        -1
    """
    return sum(m[i, i] for i in range(even_dim)) - sum(m[i, i] for i in range(even_dim, m.nrows()))

supertrace_form(basis, even_dim)

Return the supertrace form B[i][j] = str(eᵢ eⱼ) of a basis of supermatrices.

Parameters:

Name Type Description Default
basis Sequence[Matrix]

Homogeneous supermatrices.

required
even_dim int

The dimension of the even part of the superspace.

required

Returns:

Type Description
Matrix

The form's matrix.

Source code in src/veriphy/toolkit/_superalgebra.py
129
130
131
132
133
134
135
136
137
138
139
def supertrace_form(basis: Sequence[Matrix], even_dim: int) -> Matrix:
    """Return the supertrace form `B[i][j] = str(eᵢ eⱼ)` of a basis of supermatrices.

    Args:
        basis: Homogeneous supermatrices.
        even_dim: The dimension of the even part of the superspace.

    Returns:
        The form's matrix.
    """
    return matrix(QQ, [[supertrace(a * b, even_dim) for b in basis] for a in basis])

symbolic(expr, problem, lean_type=None)

Render a Sage symbolic expression over the problem's variables as a Lean term over its atoms.

Functions render through the problem's translation tables, dispatching on type: exp, log, sin, cos, tan render as their Complex versions when the argument mentions I or a ℂ atom and as Real versions otherwise; abs renders as |·|, pi as Real.pi, I as Complex.I. The term is rendered at lean_type (the problem's carrier by default) and ascribed to it; integer-valued operators (factorial, binomial, gcd, remainders, floor, ceil) and sub-expressions Lean computes at ℕ or ℤ render at that type and are cast. A function outside the tables, a symbol that is not an atom, or a fraction or ℕ subtraction rendered at ℕ or ℤ raises CertificateError.

Parameters:

Name Type Description Default
expr object

A symbolic expression, or anything Sage's symbolic ring converts, over problem.symbols.

required
problem Problem

The problem whose atoms name the variables.

required
lean_type str | None

The Lean type of the term, the problem's carrier when None; empty for no ascription.

None

Returns:

Type Description
Lean

The term.

Examples:

>>> from sage.all import exp, sqrt
>>> from veriphy.toolkit import read_problem
>>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'z'], 'atom_types': ['ℝ', 'ℂ'],
...     'goal': {'kind': 'false'}})
>>> x0, x1 = prob.symbols['x0'], prob.symbols['x1']
>>> str(symbolic(abs(x0 - 1), prob)), str(symbolic(exp(x1), prob)), str(symbolic(sqrt(x0) / (x0**2 + 1), prob))
('|x - 1|', 'Complex.exp z', '(Real.sqrt x) / (x ^ 2 + 1)')
>>> nat = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'n'], 'atom_types': ['ℝ', 'ℕ'], 'carrier': 'ℝ',
...     'goal': {'expr': "(typed(mod(x1, 7), 'ℕ') + x0) - (x0)", 'kind': 'other', 'rel': 'le', 'text': ''}})
>>> str(symbolic(nat.goal.expr + nat.symbols['x0'], nat))
'(x + ((n % 7 : ℕ) : ℝ) : ℝ)'
>>> str(symbolic(nat.symbols['x1'] + 1, nat, lean_type='ℕ'))
'(n + 1 : ℕ)'
Source code in src/veriphy/toolkit/_symbolic_emit.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def symbolic(expr: object, problem: Problem, lean_type: str | None = None) -> Lean:
    """Render a Sage symbolic expression over the problem's variables as a Lean term over its atoms.

    Functions render through the problem's translation tables, dispatching on type: `exp`, `log`, `sin`, `cos`,
    `tan` render as their `Complex` versions when the argument mentions `I` or a ℂ atom and as `Real` versions
    otherwise; `abs` renders as `|·|`, `pi` as `Real.pi`, `I` as `Complex.I`. The term is rendered at `lean_type` (the
    problem's carrier by default) and ascribed to it; integer-valued operators (`factorial`, `binomial`, `gcd`,
    remainders, `floor`, `ceil`) and sub-expressions Lean computes at ℕ or ℤ render at that type and are cast. A
    function outside the tables, a symbol that is not an atom, or a fraction or ℕ subtraction rendered at ℕ or ℤ
    raises `CertificateError`.

    Args:
        expr: A symbolic expression, or anything Sage's symbolic ring converts, over `problem.symbols`.
        problem: The problem whose atoms name the variables.
        lean_type: The Lean type of the term, the problem's carrier when `None`; empty for no ascription.

    Returns:
        The term.

    Examples:
        >>> from sage.all import exp, sqrt
        >>> from veriphy.toolkit import read_problem
        >>> prob = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'z'], 'atom_types': ['ℝ', 'ℂ'],
        ...     'goal': {'kind': 'false'}})
        >>> x0, x1 = prob.symbols['x0'], prob.symbols['x1']
        >>> str(symbolic(abs(x0 - 1), prob)), str(symbolic(exp(x1), prob)), str(symbolic(sqrt(x0) / (x0**2 + 1), prob))
        ('|x - 1|', 'Complex.exp z', '(Real.sqrt x) / (x ^ 2 + 1)')
        >>> nat = read_problem({'vars': ['x0', 'x1'], 'atoms': ['x', 'n'], 'atom_types': ['ℝ', 'ℕ'], 'carrier': 'ℝ',
        ...     'goal': {'expr': "(typed(mod(x1, 7), 'ℕ') + x0) - (x0)", 'kind': 'other', 'rel': 'le', 'text': ''}})
        >>> str(symbolic(nat.goal.expr + nat.symbols['x0'], nat))
        '(x + ((n % 7 : ℕ) : ℝ) : ℝ)'
        >>> str(symbolic(nat.symbols['x1'] + 1, nat, lean_type='ℕ'))
        '(n + 1 : ℕ)'
    """
    ctx = _Context(problem, problem.carrier if lean_type is None else lean_type)
    text = _render(SR(expr), ctx)
    ascribed = _is_grouped(text) and text.endswith(f' : {ctx.lean_type})')
    return term(f'({text} : {ctx.lean_type})' if ctx.lean_type and not ascribed else text)

tactic(text)

Wrap Lean source as a tactic.

Parameters:

Name Type Description Default
text str

The Lean source of a tactic or tactic block.

required

Returns:

Type Description
Lean

The tactic.

Examples:

>>> tactic('norm_num').kind
'tactic'
Source code in src/veriphy/toolkit/_lean.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def tactic(text: str) -> Lean:
    """Wrap Lean source as a tactic.

    Args:
        text: The Lean source of a tactic or tactic block.

    Returns:
        The tactic.

    Examples:
        >>> tactic('norm_num').kind
        'tactic'
    """
    return Lean(text, 'tactic')

tensor_representation(rho, sigma)

Return the tensor product representation, x ↦ ρ(x) ⊗ 1 + 1 ⊗ σ(x).

Parameters:

Name Type Description Default
rho Rep

The first representation, one matrix per basis element of the algebra.

required
sigma Rep

The second, on the same basis.

required

Returns:

Type Description
Rep

The product's matrices.

Source code in src/veriphy/toolkit/_representation.py
79
80
81
82
83
84
85
86
87
88
89
90
def tensor_representation(rho: Rep, sigma: Rep) -> Rep:
    """Return the tensor product representation, `x ↦ ρ(x) ⊗ 1 + 1 ⊗ σ(x)`.

    Args:
        rho: The first representation, one matrix per basis element of the algebra.
        sigma: The second, on the same basis.

    Returns:
        The product's matrices.
    """
    one_r, one_s = identity_matrix(QQ, rho[0].nrows()), identity_matrix(QQ, sigma[0].nrows())
    return [a.tensor_product(one_s) + one_r.tensor_product(b) for a, b in zip(rho, sigma, strict=True)]

term(text)

Wrap Lean source as a term.

Parameters:

Name Type Description Default
text str

The Lean source of a term.

required

Returns:

Type Description
Lean

The term.

Examples:

>>> str(term('h1'))
'h1'
Source code in src/veriphy/toolkit/_lean.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def term(text: str) -> Lean:
    """Wrap Lean source as a term.

    Args:
        text: The Lean source of a term.

    Returns:
        The term.

    Examples:
        >>> str(term('h1'))
        'h1'
    """
    return Lean(text, 'term')

term_lean(e)

Return a hypergeometric term as a Lean lambda fun n k => … over ℚ, zero where a shifted binomial is negative.

Parameters:

Name Type Description Default
e Expr

The term, in the symbols n, k of this module.

required

Returns:

Type Description
str

The lambda.

Examples:

>>> print(term_lean(-sp.binomial(N, K - 1) / (2 * 2**N)))
fun (n k : ℕ) => if k < 1 then 0 else (((-1 : ℚ) / 2) * ((2 : ℚ) ^ (n))⁻¹ * ((n).choose (k - 1) : ℚ))
Source code in src/veriphy/toolkit/_summation.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def term_lean(e: sp.Expr) -> str:
    """Return a hypergeometric term as a Lean lambda `fun n k => …` over ℚ, zero where a shifted binomial is negative.

    Args:
        e: The term, in the symbols `n`, `k` of this module.

    Returns:
        The lambda.

    Examples:
        >>> print(term_lean(-sp.binomial(N, K - 1) / (2 * 2**N)))
        fun (n k : ℕ) => if k < 1 then 0 else (((-1 : ℚ) / 2) * ((2 : ℚ) ^ (n))⁻¹ * ((n).choose (k - 1) : ℚ))
    """
    guards: list[str] = []
    body = _render(e, guards)
    if guards:
        return f'fun (n k : ℕ) => if {" ∨ ".join(guards)} then 0 else {body}'
    return f'fun (n k : ℕ) => {body}'

trace_form(basis)

Return the trace form of the representation, B[i][j] = tr(eᵢ eⱼ).

Parameters:

Name Type Description Default
basis Sequence[Matrix]

The representation matrices.

required

Returns:

Type Description
Matrix

The symmetric matrix of the form.

Source code in src/veriphy/toolkit/_lie.py
107
108
109
110
111
112
113
114
115
116
117
def trace_form(basis: Sequence[Matrix]) -> Matrix:
    """Return the trace form of the representation, `B[i][j] = tr(eᵢ eⱼ)`.

    Args:
        basis: The representation matrices.

    Returns:
        The symmetric matrix of the form.
    """
    ring = _coordinates(basis).base_ring()
    return matrix(ring, len(basis), len(basis), lambda i, j: (basis[i] * basis[j]).trace())

verify_definition(problem, proofs=None)

Prove an ODE goal about a function defined explicitly by a hypothesis x = fun s => ….

The proof substitutes the definition, states each derivative of the body as a fact, rewrites with them and closes the remaining identity by ring1, field_simp or norm_num.

Parameters:

Name Type Description Default
problem Problem

The problem; its goal is an equation in an unknown function that a hypothesis defines.

required
proofs Mapping[str, str] | None

Tactics proving nonzero conditions no hypothesis states, by their Lean text.

None

Returns:

Type Description
Lean

The tactic block.

Raises:

Type Description
CertificateError

If no hypothesis defines a function of the goal.

Source code in src/veriphy/toolkit/_ode.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def verify_definition(problem: Problem, proofs: Mapping[str, str] | None = None) -> Lean:
    """Prove an ODE goal about a function defined explicitly by a hypothesis `x = fun s => …`.

    The proof substitutes the definition, states each derivative of the body as a fact, rewrites with them and closes
    the remaining identity by `ring1`, `field_simp` or `norm_num`.

    Args:
        problem: The problem; its goal is an equation in an unknown function that a hypothesis defines.
        proofs: Tactics proving nonzero conditions no hypothesis states, by their Lean text.

    Returns:
        The tactic block.

    Raises:
        CertificateError: If no hypothesis defines a function of the goal.
    """
    goal = problem.goal.expr if problem.goal is not None else None
    for definition in problem.definitions:
        f = problem.symbols[definition.function]
        if goal is None or definition.body is None or not SR(goal).find(f(SR.wild(0))):
            continue
        order = derivative_order(SR(goal), definition.function)
        facts, names = derivative_chain(
            definition.body,
            definition.var,
            order,
            problem,
            function_text=definition.text,
            proofs=proofs,
        )
        rewrite = [tactic(f'simp only [{", ".join(names)}]')] if names else []
        lines = [tactic(f'subst {definition.hyp}'), *facts, *rewrite, tactic(EQUATION_CLOSE)]
        return tactic('\n'.join(str(line) for line in lines))
    message = 'no hypothesis `x = fun s => …` defines a function of the goal'
    raise CertificateError(message)

weyl_group_order(cartan_type)

Return the order of the Weyl group, from Sage.

Parameters:

Name Type Description Default
cartan_type str

The type, as 'E8'.

required

Returns:

Type Description
int

The order.

Examples:

>>> weyl_group_order('E8')
696729600
Source code in src/veriphy/toolkit/_lie.py
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
def weyl_group_order(cartan_type: str) -> int:
    """Return the order of the Weyl group, from Sage.

    Args:
        cartan_type: The type, as `'E8'`.

    Returns:
        The order.

    Examples:
        >>> weyl_group_order('E8')
        696729600
    """
    letter, rank = _cartan_type(cartan_type)
    return int(WeylGroup([letter, rank]).cardinality())

witness(values, side)

Close an existential goal with exact rational values and a tactic for the side conditions.

Parameters:

Name Type Description Default
values Sequence[object]

One rational per binder.

required
side Lean

The tactic proving the body at those values, such as norm_num.

required

Returns:

Type Description
Lean

The tactic.

Examples:

>>> str(witness([1, QQ(-1) / 2], tactic('norm_num')))
'exact ⟨1, -(1 / 2), by norm_num⟩'
Source code in src/veriphy/toolkit/_emit.py
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
def witness(values: Sequence[object], side: Lean) -> Lean:
    """Close an existential goal with exact rational values and a tactic for the side conditions.

    Args:
        values: One rational per binder.
        side: The tactic proving the body at those values, such as `norm_num`.

    Returns:
        The tactic.

    Examples:
        >>> str(witness([1, QQ(-1) / 2], tactic('norm_num')))
        'exact ⟨1, -(1 / 2), by norm_num⟩'
    """
    rendered = ', '.join(str(rational(v)) for v in values)
    return tactic(f'exact ⟨{rendered}, by {_expect(side, "tactic", "a side-goal tactic")}⟩')

wz_mate(f)

Return the WZ mate G of a summand F(n, k) with Σₖ F(n, k) constant in n.

G satisfies F(n + 1, k) - F(n, k) = G(n, k + 1) - G(n, k); it is Gosper's antidifference in k of the left side.

Parameters:

Name Type Description Default
f Expr

The normalized summand (its sum over k is 1 for every n).

required

Returns:

Type Description
Expr

G, simplified to a hypergeometric term.

Raises:

Type Description
CertificateError

If the difference is not Gosper-summable (no WZ pair).

Examples:

>>> wz_mate(sp.binomial(N, K) / 2**N)
-binomial(n, k - 1)/(2*2**n)
Source code in src/veriphy/toolkit/_summation.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def wz_mate(f: sp.Expr) -> sp.Expr:
    """Return the WZ mate `G` of a summand `F(n, k)` with `Σₖ F(n, k)` constant in `n`.

    `G` satisfies `F(n + 1, k) - F(n, k) = G(n, k + 1) - G(n, k)`; it is Gosper's antidifference in `k` of the left
    side.

    Args:
        f: The normalized summand (its sum over `k` is 1 for every `n`).

    Returns:
        `G`, simplified to a hypergeometric term.

    Raises:
        CertificateError: If the difference is not Gosper-summable (no WZ pair).

    Examples:
        >>> wz_mate(sp.binomial(N, K) / 2**N)
        -binomial(n, k - 1)/(2*2**n)
    """
    f = _sympy(f)
    difference = sp.combsimp(f.subs(N, N + 1) - f)
    ratio = gosper_term(difference, K)
    if ratio is None:
        message = 'F(n + 1, k) - F(n, k) is not Gosper-summable in k: no WZ pair'
        raise CertificateError(message)
    return sp.combsimp(ratio * difference)

wz_proof(f, closed_form, closed_form_lean, mate=None)

Return a proof of ∑ k ∈ Finset.range (n + 1), F n k = S n (with n in scope) by a WZ pair.

The normalized summand F / S sums to 1: the summand identity telescopes, the sum is constant in n, and it is 1 at n = 0. F must vanish at k = n + 1 (natural boundaries), and S n ≠ 0 closes by positivity.

Parameters:

Name Type Description Default
f Expr

The summand F(n, k) (sympy, over n, k).

required
closed_form Expr

S(n) (sympy, over n).

required
closed_form_lean str

The Lean text of S n as the goal writes it, in n.

required
mate Expr | None

The WZ mate of F / S; found by Gosper's algorithm when None.

None

Returns:

Type Description
Lean

The tactic.

Source code in src/veriphy/toolkit/_summation.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def wz_proof(f: sp.Expr, closed_form: sp.Expr, closed_form_lean: str, mate: sp.Expr | None = None) -> Lean:
    """Return a proof of `∑ k ∈ Finset.range (n + 1), F n k = S n` (with `n` in scope) by a WZ pair.

    The normalized summand `F / S` sums to 1: the summand identity telescopes, the sum is constant in `n`, and it is 1
    at `n = 0`. `F` must vanish at `k = n + 1` (natural boundaries), and `S n ≠ 0` closes by `positivity`.

    Args:
        f: The summand `F(n, k)` (sympy, over `n`, `k`).
        closed_form: `S(n)` (sympy, over `n`).
        closed_form_lean: The Lean text of `S n` as the goal writes it, in `n`.
        mate: The WZ mate of `F / S`; found by Gosper's algorithm when `None`.

    Returns:
        The tactic.
    """
    f = _sympy(f)
    normalized = sp.combsimp(f / _sympy(closed_form))
    mate = wz_mate(normalized) if mate is None else mate
    lines = [
        f'let F : ℕ → ℕ → ℚ := fun n k => ({term_lean(f)}) n k * ({closed_form_lean})⁻¹',
        f'let G : ℕ → ℕ → ℚ := {term_lean(mate)}',
        'have hwz : ∀ n k, F (n + 1) k - F n k = G n (k + 1) - G n k := by',
        '  intro n k',
        '  rcases k with _ | k',
        '  all_goals simp [F, G, Nat.choose_succ_succ\', Nat.factorial_succ, pow_succ]',
        '  all_goals first | ring1 | (field_simp; ring1) | (field_simp; done)',
        'have hstep : ∀ n, ∑ k ∈ Finset.range (n + 2), F (n + 1) k = ∑ k ∈ Finset.range (n + 1), F n k := by',
        '  intro n',
        '  have hdiff : ∑ k ∈ Finset.range (n + 2), (F (n + 1) k - F n k) = 0 := by',
        '    rw [Finset.sum_congr rfl fun k _ => hwz n k, Finset.sum_range_sub]',
        '    simp [G]',
        '  rw [Finset.sum_sub_distrib, sub_eq_zero] at hdiff',
        '  rw [hdiff, Finset.sum_range_succ]',
        '  simp [F, Nat.choose_succ_self]',
        'have hone : ∀ n, ∑ k ∈ Finset.range (n + 1), F n k = 1 := by',
        '  intro n',
        '  induction n with',
        '  | zero => simp [F]',
        '  | succ n ih => rw [hstep, ih]',
        'have h := hone n',
        'simp only [F, ← Finset.sum_mul] at h',
        'simpa using (mul_inv_eq_one₀ (by positivity)).mp h',
    ]
    return tactic('\n'.join(lines))