A computer algebra system for engineers, in C#
Symbolic differentiation, integration, expansion and root-finding as a .NET library — and a typed rule language that compiles new algebra into it.
Engineers reach for a computer algebra system for one reason: the formula is the specification, and the numbers are what you get after. A control law, a load model, a consistency invariant — you want to differentiate it, simplify it, solve it, and only then evaluate it. Most of the time that means leaving your stack for Mathematica, Maple or SymPy, and carrying the result back by hand.
MLambda.Genesis.Algebra brings the algebra into .NET, as a library you can call from the same code that uses the result. And the CAS rule language lets you extend it in a notation that is closer to the mathematics than to C#.
Seven constructors
Everything stands on one algebraic data type, Expr, with seven constructors:
Num, Sym, Add, Mul, Pow,
Neg and FuncCall. That is deliberately minimal, in the tradition of
Davenport, Siret and Tournier. Subtraction is not primitive — a − b is
Add(a, Neg(b)) — and division is Mul(a, Pow(b, −1)), so the single
rule x^a · x^b = x^(a+b) handles division without a special case. Fewer
constructors means every transformation downstream has fewer cases to get right.
Around the type: a lexer, a recursive-descent parser, a pretty-printer and a numeric evaluator, so you can go from text to tree and back.
What the library does
Each algorithm is a pure function from Expr to Expr. Nothing is
mutated; nothing is shared between calls.
- Simplify — canonical forms, constant folding, power combination.
- Differentiate —
D(expr, x), with the chain rule driven by a function registry that knows the derivatives and antiderivatives of the built-ins (sin,cos,exp,ln,sqrt) and accepts user-registered functions. - Integrate — symbolic antiderivatives, with polynomial factorisation used for partial fractions.
- Expand — distribute products over sums.
- Solve — real roots of a polynomial equation: coefficient extraction, then dispatch on degree — linear, quadratic in closed form, higher degrees by factoring.
- Polynomial arithmetic — GCD by the Euclidean algorithm and factorisation over the rationals, after Knuth and after Geddes, Czapor and Labahn.
- Taylor series to a given order about a point, and an AC
matcher that matches modulo associativity and commutativity, so
a + bandb + aare the same pattern.
var expr = MathParser.Parse("x^2 + 2*x + 1");
var deriv = Differentiator.D(expr, new Sym("x")); // 2 * x + 2
var roots = Solver.Solve(expr, new Sym("x")); // [-1]
Open by construction
The algebra does not own its rules. The extension point is one interface:
public interface IRewriteSystem
{
IEnumerable<RewriteRule> Rules { get; }
}
public sealed record RewriteRule(string Name, Func<Expr, Expr> Apply);
Anything that yields Expr → Expr transformations — hand-written or generated —
contributes rules, and a host composes systems by concatenating them and applying a strategy:
innermost-first, outermost-first, or in parallel. That is what makes the next part possible.
A language for the rules
Writing rewrite rules as C# lambdas over a tree works, but it does not look like the
mathematics, and the type of "an expression that matches Pow(f, Num(n))" is not
something C# will check for you. So Genesis has a second language, CAS, for exactly this:
module Calculus {
let isConst (e : Expr) : Bool =
match e with
| Num(_) -> true
| _ -> false
rule D : Expr -> Expr {
| Num(_) -> Num(0)
| Sym("x") -> Num(1)
| Sym(_) -> Num(0)
| Add(f, g) -> Add(D(f), D(g))
| Mul(f, g) -> Add(Mul(D(f), g), Mul(f, D(g)))
| Pow(f, Num(n)) -> Mul(Mul(Num(n), Pow(f, Num(n-1))), D(f))
| _ -> Num(0)
}
}
A module holds helpers — pure functions used in guards — and rules: pattern arms with optional
when guards, constructor deconstruction, wildcards, literals and bindings, plus a
small functional calculus for the right-hand sides (lambdas, let,
if, match). The type system is a simplified Hindley–Milner —
Damas and Milner's Algorithm W — extended with the distinguished type Expr and
type variables, so an arm that returns the wrong kind of thing is a type error in the rule
file, not a runtime surprise in the algebra.
The pipeline is the same shape as every Genesis language: lexer, parser, type checker,
emitter, pretty-printer — and then Roslyn, which compiles the emitted C# to an assembly
implementing IRewriteSystem. The rules become code without anyone writing the
code. Load the assembly, concatenate its rules with the built-ins, choose a strategy, and the
algebra now knows your domain's identities.
Why this is in an architecture platform
Because Turing's method has a mathematical stage. Gilb's Planguage gives every quality requirement a scale, a meter and a target — quantities, not adjectives — and the method book's chapter on "the mathematical definition" is where those quantities become expressions the platform can reason about: derived, simplified, compared against measurement. An architecture platform that treats software as mathematics needs algebra it can run, and it needs it in the language the rest of the platform is written in.
Stated limits
The polynomial machinery works over double coefficients with a
10⁻¹⁵ threshold — floating point, not exact rationals — and the solver returns real
roots. The CAS type checker's unifier does not yet perform the occurs check. These are the
honest edges of a v0.x library that is used in earnest but is not claiming to replace Maple.
What it claims is narrower and true: symbolic differentiation, integration, expansion,
root-finding and user-defined rewrite systems, as pure functions, in a NuGet package, with a
typed rule language that compiles into it.
dotnet add package MLambda.Genesis.Algebra — MIT, part of
Genesis. Documentation for the algebra,
the math expression language and the CAS rule language at
genesis.mlambda.net.