This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.

Sym

import std/sym

std/sym — symbolic scalar expressions with a canonical normal form.

sym.kz · 44 tors

std/sym — symbolic scalar expressions with a canonical normal form. · 38 more lines
std/sym — symbolic scalar expressions with a canonical normal form. A `sym` is a CANONICAL expression string over the decimal kernel: atoms π, e, φ (named constants — derive to zero) variable x (the free variable — sym:deriv w.r.t. x) radical √( <expr> ) inverse inv( <expr> ) (a/b normalizes to a*inv(b)) product ( <e1> * <e2> * ... ) coefficient first, then factors sorted sum ( <e1> + <e2> + ... ) constant term first, then terms sorted decimal a std/num canonical literal — 0e0 | [-]D[.DDD]e[-]E The normal form is the contract: value-equivalence is string equality, same law as std/num. NF rules applied at build time: - nested add/mul flatten - sum children group by structural base: π + π -> (2e0*π), (2e0*π)+(3e0*π) -> (5e0*π); decimals fold into one constant term - product decimals fold into one coefficient; coeff 0 -> 0e0, coeff 1 drops; f and inv(f) cancel pairwise - inv(inv(x)) -> x; inv distributes over product children; inv(dec) folds to a decimal iff the mantissa's prime factors are all in {2,5} (1/4 -> 2.5e-1 exact; inv(3) stays symbolic — no silent inexact) - √(dec>=0) folds iff a perfect square (√(0.25) -> 5e-1); a negative decimal radicand refuses on |neg-root - equal √ factors pair to their radicand (√2*√2 -> 2) when the radicand is provably nonneg (dec >= 0 or a positive atom) - coeff*inv(dec) reduces the rational to lowest terms, so the canon for a value is the reduced fraction: 4*inv(6e1) -> inv(1.5e1), which is what a fresh "1/15" produces - inv(-d) -> -1*inv(d): the sign lives on the coefficient What NF does NOT do: no distributive factoring, no trig/ident tables, no power node (π*π stays a product — a pow canon is a later rung). Those are the deriv/discovery rungs, not canon mechanics. Marks: sym-owned strings ride <sexpr!>, issued by lit/parse and every op, consumed linearly by ops, discharged by free, swapped to <allocated!> by render — the same discipline as std/num:exact.

Phantom lifecycles

Derived from the phantom labels in the declarations below — state! issues an obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.

string 6 states sexpr!sexpr-external!allocated!

ingest

<sexpr-external!> koru_std/sym.kz:1324

parse

<!sexpr-external><sexpr!><std.io:allocated!> koru_std/sym.kz:1330

add

<!sexpr><sexpr!> koru_std/sym.kz:1340

sub

<!sexpr><sexpr!> koru_std/sym.kz:1348

mul

<!sexpr><sexpr!> koru_std/sym.kz:1356

div

<!sexpr><sexpr!> koru_std/sym.kz:1364

neg

<!sexpr><sexpr!> koru_std/sym.kz:1378

sqrt

<!sexpr><sexpr!> koru_std/sym.kz:1385

equiv

<sexpr> koru_std/sym.kz:1399

free

<!sexpr> koru_std/sym.kz:1408

render

<!sexpr><std.io:allocated!> koru_std/sym.kz:1414

of-dec

<std.num:!exact><sexpr!> koru_std/sym.kz:1435

pow

<!sexpr><sexpr!> koru_std/sym.kz:1444

deriv

<!sexpr><sexpr!> koru_std/sym.kz:1459

~part supgen koru_std/sym.supgen.kz

Koru Standard Library: the `supgen` part of sym.kz — SupGen: certificate-producing structural synthesis over the Dec/sym tower. Joined via `~part supgen` in sym.kz; merged into the module at load. Power-series arithmetic over exact rationals (Rat = n/d over big ints) and bounded ODE discovery: enumerate (order, degree) candidates, build the series residual, solve for the relation kernel exactly, verify the residual on terms NOT used in the solve, emit a certificate string — or refuse `| not-found` (never claims nonexistence; bounds are named). Series of any expression in the sym grammar (dec/x/+/*/inv/sqrt) has RATIONAL coefficients — every construct here is D-finite. Constant atoms pi/e/phi are not rational, so series over them refuse | non-rational; poles and zero-constant sqrt/inverses refuse | non-analytic. Certificate algebra (second half of the SupGen artifact, mirroring isar-proofs HolonomicCertificate): cert-seed registers a declared law body p0=[..]|p1=[..] for an abstract/opaque generator; cert-shift is integralShift — if g satisfies cert c and deriv h = g, h satisfies shift(c): order+1, p0 := [0], pk := c.p(k-1).

series

<!sexpr><std.io:allocated!> koru_std/sym.supgen.kz:653

derive-ode

<!sexpr><std.io:allocated!> koru_std/sym.supgen.kz:679

cert-shift

<std.io:!allocated><std.io:allocated!> koru_std/sym.supgen.kz:880

cert-seed

<std.io:allocated!> koru_std/sym.supgen.kz:899

~part certcomp koru_std/sym.certcomp.kz

Koru Standard Library: the `certcomp` part of sym.kz — certificate closure under sum and product via companion-basis elimination over the rational-function field Q(x). Mirrors isar_holonomic_closure_algebra.py (_tensor_basis_certificate) and is proven in isar-proofs HolonomicClosure.lean. General composition is refused BY NAME (exp_exp_not_holonomic is a theorem, not a gap). Joined via `~part certcomp` in sym.kz; merged into the module at load. A cert ode(k=m,d=D)|p0..pm with nonzero leading p_m yields the companion rewrite g^(m) = sum_{k<m} (-p_k/p_m) g^k over Q(x). The derivative stays inside a finite span: sum: f^(i), g^(j) dim = m + n product: f^(i) * g^(j) dim = m * n so h, h', ..., h^dim are linearly dependent over Q(x); the first dependency found by augmented Gauss-Jordan gives h's ODE. Output is a theorem application (via=sum / via=prod), not a finite witness. Polynomials over Rat — coefficient lists, low to high; trailing zeros are permitted internally, polyIsZero/polyDegIdx scan.

cert-sum

<std.io:!allocated><std.io:allocated!> koru_std/sym.certcomp.kz:930

cert-prod

<std.io:!allocated><std.io:allocated!> koru_std/sym.certcomp.kz:953

cert-compose

<std.io:!allocated><std.io:allocated!> koru_std/sym.certcomp.kz:978

cert-bridge

<std.io:!allocated><std.io:allocated!> koru_std/sym.certcomp.kz:1000

~part certreg koru_std/sym.certreg.kz

Koru Standard Library: the `certreg` part of sym.kz — the certificate registry, mirroring isar_holonomic_closure_algebra.py's HolonomicRegistry. Koru has no mutable globals: the registry is an allocated TEXT BLOB threaded through flows — every op consumes the old blob and hands back a new one (persistent-value semantics). Joined via `~part certreg` in sym.kz; merged into the module at load. Format: reg:v1;G1=<cert>;G2=<cert>;... Keys are deterministic (G1, G2, ...). Provenance is embedded in each cert's `via` field: seed / verified=N / shift(Gk) / sum(Ga,Gb) / prod(Ga,Gb) — the Python registry's provenance strings, compressed into the cert text itself. Split `reg:v1;G1=cert;...` into cert-text slices borrowing `text`. Validates the header and each `G<n>=` record prefix.

cert-reg-new

<std.io:allocated!> koru_std/sym.certreg.kz:76

cert-reg-put

<std.io:!allocated><std.io:allocated!> koru_std/sym.certreg.kz:84

cert-reg-seed

<std.io:!allocated><std.io:allocated!> koru_std/sym.certreg.kz:102

cert-reg-shift

<std.io:!allocated><std.io:allocated!> koru_std/sym.certreg.kz:134

cert-reg-sum

<std.io:!allocated><std.io:allocated!> koru_std/sym.certreg.kz:158

cert-reg-prod

<std.io:!allocated><std.io:allocated!> koru_std/sym.certreg.kz:187

cert-reg-derive

<std.io:!allocated><!sexpr><std.io:allocated!> koru_std/sym.certreg.kz:217

cert-reg-compose

<std.io:!allocated><std.io:allocated!> koru_std/sym.certreg.kz:246

cert-reg-get

<*><std.io:allocated!> koru_std/sym.certreg.kz:265

cert-reg-last

<*><std.io:allocated!> koru_std/sym.certreg.kz:280

~part certgen koru_std/sym.certgen.kz

Koru Standard Library: the `certgen` part of sym.kz — certified series. Joined via `~part certgen` in sym.kz; merged into the module at load. Depends on: sym.kz (parseTree/norm/SNode), supgen (Rat/serFromNode/ certWrite/OdeCert), certcomp (Poly/Frac closure algebra, certBridge, recWrite, certClosure). cert-of derives the ODE STRUCTURALLY — theorem-level, no series witness (HolonomicClosure.lean semantics): dec -> f' = 0 x -> x f' - f = 0 a + b -> certClosure sum a * b -> certClosure prod inv(a), a order-1 -> q1 g' - q0 g = 0 (g = 1/f) sq(p), p a poly -> 2p g' - p' g = 0 (g = sqrt(p)) anything else is a named refusal, never a silent default. cert-series emits `cser(terms=N,det=1)|a=[...]|ode=...|rec=...`: cert-of -> certBridge -> det (Lean h0: q_{K,0} != 0) -> seeds are the expression's ACTUAL first R coefficients (exact), tail is generated by the recurrence. Every generated coefficient is forced by the ODE residual — theorem-correct, not a finite witness. det=0 refuses as under-determined carrying the (unpackageable) rec for diagnostics.

cert-of

<!sexpr><std.io:allocated!> koru_std/sym.certgen.kz:367

cert-series

<!sexpr><std.io:allocated!> koru_std/sym.certgen.kz:395

decide-integral

<!sexpr><std.io:allocated!> koru_std/sym.certgen.kz:593

decide-ode

<!sexpr><std.io:allocated!> koru_std/sym.certgen.kz:666

decide-field

<*><std.io:allocated!> koru_std/sym.certgen.kz:707

~part rel koru_std/sym.rel.kz

std/sym part: rel — declared-generator relationships. A generator is a named function of x declared in a `rel:v1` registry: rel:v1;exp=;sin=g(cos);cos=(-1e0*g(sin)) Entry i internes atom 4+i; `g(name)` in an expression resolves against the registry's names. A law is a canonical sexpr (over x, decimals, √/inv and other generators) giving D(g_i) — attached after declaration so cyclic law pairs (sin <-> cos) and self-laws (exp' = exp) both work. Grammar extension lives in sym.kz: `g(name)` parses only under a registry (ParseCtx.greg); the closed `lit` grammar refuses g-atoms. Expressions containing g-atoms form a distinct universe from <sexpr!> — they cannot flow through sym:add/mul/... (those parse with no registry) — so rel surfaces take and return plain strings. Tors: rel-new -> rel:v1 (allocated!) rel-seed reg, name -> exact reg' | dup | full | bad declare a name rel-law reg, name, law -> exact reg' | unknown-generator | bad rel-derive reg, expr -> exact text | unknown-generator | lawless | bad rel-norm reg, expr -> exact text | unknown-generator | bad rel-derive applies the declared laws mechanically (chain rule) and returns the normalized canonical text — theorem-grade, since each step is the derivative rule or a declared law substitution. rel-norm gives the canonical form: two expressions provably-equal under the laws iff their canonical texts match (the equality-check primitive is caller-side text compare).

rel-new

<std.io:allocated!> koru_std/sym.rel.kz:98

rel-seed

<std.io:!allocated><std.io:allocated!> koru_std/sym.rel.kz:105

rel-law

<std.io:!allocated><std.io:allocated!> koru_std/sym.rel.kz:130

rel-derive

<*><std.io:allocated!> koru_std/sym.rel.kz:155

rel-norm

<*><std.io:allocated!> koru_std/sym.rel.kz:182