COTT Calculator

architecture guide

---
1. Overview

The COTT Calculator is a Python/SymPy implementation of Traction Theory's algebra, with a tkinter calculator GUI, phase-plot visualizer, fractal engine, and Chebyshev ring decomposition engine. It lives under /solver and comprises roughly 11,000 lines across two dozen files.

The solver is built on two core principles inherited from Traction Theory: totality (all operations defined for all values) and reversibility (no information loss). These principles drive every design decision — from zero having a reciprocal, to the universal power-of-power rule that eliminates branch cuts.

1.1 File Map
Source code on Github
File Purpose
traction.py Core algebra engine: types (Zero, Omega, Null, GradedElement), simplifier, projection, logarithms
parser.py Recursive descent parser: expressions, equations, functions (solve, expand, factor, log0, logw)
calculator.py Entry point and public API re-exports for backward compatibility
evaluator.py Numeric traction evaluator: TractionValue class with zero/omega class tracking
formatting.py Display formatting: exact, approximate, complex projection, and numeric modes
decomposition.py Chebyshev ring decomposition engine: auto-detects band, reduces, evaluates
visualization.py Phase grid computation and RGB mapping (phase mode and continuity mode)
fractal.py Escape-time fractal engine with smooth HSV colouring
streamlines.py Gradient streamline computation (tangent and normal flow lines via RK2)
registry.py General-purpose function registry (category → name → object)
chebyshev_ring.py Exact arithmetic: QsPoly, Element (a + b·u in ℚ[s][u]/(u²−su+1)), TowerElement, MultiBandElement
projections/
projections/base.py Base class for projection plugins
projections/complex_lie.py Default projection: 0^zeWz
projections/geometric_algebra.py Geometric algebra: dot + wedge product, Cayley transform
gui/
gui/app.py Main calculator window: five-tab UI, number pad, phase canvas, hover gauges
gui/fullscreen.py Full-screen viewer with pan/zoom, progressive rendering, coordinate grid
gui/settings.py Settings window: colour mode, projection selection, streamline toggles
gui/constants.py Colour palette and layout constants
gui/utils.py Tick formatting, colour scaling, line clipping utilities
Tests
test_traction.py 160 pytest tests validating all traction identities
test_chebyshev_ring.py 173 tests for ring arithmetic, decomposition, and reduction
---
2. Core Algebra (traction.py)
2.1 Custom Types

Three custom SymPy Expr subclasses form the foundation. They are not Symbols — they hook into SymPy's evaluation via _eval_power, __mul__, and __truediv__.

Class Represents Key behaviour
Zero 0 Not absorbing. 0*w=1, 0^{-1}=w
Omega ω Reciprocal of zero. w*0=1, w^{-1}=0
Null Erasure element. Result of a-a=null

Two additional classes handle unevaluated logarithms: Log0 (base-0 logarithm) and LogW (base-ω logarithm). These are SymPy Function subclasses that stay symbolic until projected to ℂ.

GradedElement (Zn) represents elements at grade n in the operation hierarchy. Grade arithmetic shifts operations one level: Zn(a) + Zn(b) = Zn−1(a·b), and Zn(a) × Zn(b) = Zn+1(a+b). Fixed points: Zn(0) = Zn−1(1), Zn(ω) = Zn−1(−1). Z0 is the identity grade. The parser accepts both Z_n(expr) and Z(n, expr) syntax.

2.2 Power Rules (_eval_power)

The _eval_power hooks fire during SymPy's Pow construction, so most identities are enforced eagerly — no simplifier call needed:

Identity Generalised form
0^0 = 1
0^{-r} = w^r Negative integer or rational exponent
0^w = -1
0^{0^n} = n 0^{0^x} = x for any x
0^{w^n} = -n 0^{w^x} = -x for any x

The generalisation beyond integers enables the resolve() identity-resolution technique: wrapping any expression in 0^{0^{x}} is a guaranteed no-op.

2.3 Simplification Engine (traction_simplify)

The simplifier is a recursive, bottom-up rewriter. It handles three expression types beyond the eager power rules:

Pow: Universal power-of-power: (v^a)^b = v^{a*b} for any base v, not just 0 and ω. This is what eliminates branch cuts — (x^2)^{1/2} = x, not |x|.

Mul: All Zero and Omega factors are unified into a single base-0 exponent using ωa = 0a. Exponents are summed: 0^a * 0^b = 0^{a+b}, 0^a * w^b = 0^{a-b}. After cancellation, the result is reconverted (negative rational exponent → ω-base). Signs are preserved: -0 is a valid distinct expression equal to ω in the reduced Chebyshev ring (see §6.3).

Add: If the result is SymPy's S.Zero (from cancellation), it is replaced with Null (the erasure element).

---
3. Complex Projection (project_complex)

Projection maps traction expressions to ℂ. It is intentionally the last step in the pipeline — all traction simplification happens first, so branch cuts from classical complex arithmetic are avoided.

3.1 The Lie Exponential

The core mapping is the Lie group exponential:

0^z eWz   where   W² = −iπ

The structure constant W = √(−iπ) ≈ 1.253 − 1.253i, with |W| = √π. This single formula handles all base-0 powers:

Traction Projected Verification
0^0 e0 = 1
0^w eW² = eiπ = −1 Consistent with 0^w = -1
0^{w/2} eiπ/2 = i The dyadic imaginary unit
0^n (positive n) enW, magnitude < 1 Zero-class: decaying magnitude
0^{-n} enW, magnitude > 1 Omega-class: growing magnitude
3.2 Omega in Exponent Space

A critical identity for projection: ω = W in exponent space. This is derived from the consistency equation:

eW·tω = eiπt   ⇒   Wω = −iπ = W²   ⇒   ω = W

This means: wherever ω appears inside an exponent or logarithm argument, the projector substitutes ω → W before evaluating. Without this, ω would project to ∞ (its standalone value), producing zoo contamination.

3.3 Logarithm Projection

Inverting the Lie exponential gives the logarithm projections:

log0(y) −ln(y) / W
logω(y) ln(y) / W

The same ω → W substitution is applied to arguments containing ω before the ln is evaluated.

---
4. Visualization Pipeline

The visualizer renders phase plots (domain colouring) of traction expressions over a 2D grid. The pipeline is designed to stay in the traction domain as long as possible, projecting to ℂ only at the final step.

4.1 The Native Pipeline
4.1.1 Variable System

Expressions use three variables with distinct roles:

Variable Role Substitution
p Horizontal grid coordinate Always the real horizontal axis
q Vertical grid coordinate Always the real vertical axis
x Projection's native unit Defined per projection (see below)
t Time parameter Scalar, controlled by slider in the GUI
c Fractal parameter Complex constant for escape-time iteration (the Mandelbrot c)

p and q are projection-agnostic — they always map directly to the grid axes. x is the projection's native coordinate, meaning the same expression 0^x has different geometric meaning depending on which projection is active:

Projection x = Interpretation
Complex Lie p + q·0^{w/2} Complex plane
Q-surface 0^{p + q*0^{w/2}} Complex plane lifted into zero-power domain

All three variables may appear in the same expression: p^2 + x uses both raw coordinates and the native unit.

4.1.2 Pipeline Steps

Step 1 — Parse: Recursive descent parser converts the input string to a SymPy expression with traction types (Zero, Omega, etc.).

Step 2 — Substitute: Replace pa, qb (real grid symbols), and x → the active projection's native_x(a, b). This substitution happens in the traction domain — the expressions are still traction objects, not complex numbers.

Step 3 — Traction Simplify: Apply all traction rewrite rules. The universal power-of-power rule fires here, collapsing expressions like (x^2)^{1/2}x before any complex arithmetic occurs. This is what eliminates branch cuts.

Step 4 — Project: The active projection converts the simplified traction expression to a form suitable for numeric evaluation. For Complex Lie, this applies 0^zeWz. For Q-surface, it decomposes zero-powers into phase and magnitude coordinates.

Step 5 — Evaluate: The projected expression is converted to a numpy vectorised function via SymPy's lambdify and evaluated over the grid in one pass.

For scalar display (the result line), the TractionValue evaluator (evaluator.py) provides class-tracked arithmetic: a numeric type that tracks whether a value is a plain scalar, a zero-power (0^{a+b*w}), or an omega-scaled quantity (result of division by zero). Each arithmetic operation preserves the class distinction:

Operation Class transition
p / 0 SCALAR → OMEGA_VAL(p)
0 ^ OMEGA_VAL(p) → ZERO_POWER with ω-exponent → eiπp
ZERO_POWER × ZERO_POWER Exponents add: 0^a * 0^b = 0^{a+b}
Scalar near 0 in base Promoted to traction 0

Long-running computations (fractal iteration, full-screen progressive render) run in a background thread, keeping the GUI responsive. Starting a new computation automatically cancels any in-progress one.

Step 6 — Colour: Two modes: Phase mode (CMYT quadrant colours, brightness by log-magnitude) and Continuity mode (CMYT following magnitude via double-cover arctan, ensuring |f|→0 and |f|→∞ have the same colour).

4.2 Why Native Matters

The key architectural decision: substitution and simplification happen in the traction domain, before any projection to ℂ. An earlier design projected first, then substituted:

expr → project → subs x→a+bi → numpy (branch cuts!)

This inherited all of ℂ's branch cut baggage. Fractional powers like (x^2)^{1/2} would hit numpy's principal-value convention, causing discontinuities on the imaginary axis. The native pipeline reverses the order:

expr → subs p,q,x → traction_simplify → project → evaluate

By the time the expression reaches numeric evaluation, the traction simplifier has already collapsed the nested powers. The branch cut never forms. And the TractionValue evaluator preserves traction semantics at the numeric layer — division by zero produces ω, not NaN.

---
5. Projection Plugin System

Projections are modular. Each projection lives in its own .py file under projections/ and self-registers into the global registry on import.

5.1 Registry (registry.py)

A general-purpose {category, name} → object store. Not limited to projections — any subsystem can register functions, strategies, or configuration under its own category.

5.2 Writing a Projection

Create a file in projections/ that subclasses Projection from projections/base.py. Implement two methods:

project_expr(traction_expr, a, b) — Convert a traction expression (containing symbols a, b) to a form suitable for lambdify. Handles variable detection and substitution mode (single-variable vs two-variable).

eval_grid(projected_expr, a, b, AA, BB) — Lambdify, evaluate on the numpy meshgrid, and return a dictionary of 2D arrays: {Z, Re, Im, mag, log_mag, phase, brightness}.

Call self.register_self() at module level. The projections/__init__.py auto-imports all .py files in the directory, triggering registration.

5.3 Geometric Algebra Projection (projections/geometric_algebra.py)

A purely algebraic alternative to the Lie exponential, grounded in 2D geometric algebra. The geometric product of two vectors decomposes as:

ab = a·b + ab   (scalar + bivector)

In 2D with basis vectors e1, e2, the bivector I = e1e2 is the oriented unit area element. Its square determines the algebra's character:

Metric I² Exponential Behavior
Circular (Lie) −1 cos θ + I sin θ Rotation (phase)
Hyperbolic (GA) +1 cosh φ + I sinh φ Boost (magnitude)

Same algebraic structure, different metric signature → completely different behavior. A complex number a + bi becomes a multivector a + bI: the real part is a scalar, the imaginary part is an oriented area element.

Coloring derives from the geometric product of input p and output f(p):

Product Formula Meaning
Dot (scalar) p·f(p) = xu + yv Radial stretch / alignment
Wedge (bivector) pf(p) = xv − yu Oriented rotation / angular twist

For zero-powers, the Cayley rational transform replaces the transcendental exponential: 0^z → (1+z)/(1−z) — conformal, rational, and unit-circle preserving. No exp, log, sin, cos, or atan2.

---
6. Design Decisions
6.1 Expr Subclasses, not Symbols

Zero and Omega are Expr subclasses with _eval_power hooks. This means SymPy evaluates 0^w → −1 during construction of the Pow object — no explicit simplify call needed. Using plain Symbols would require a post-hoc rewriter for every expression.

6.2 Unified Base-0 Exponents

In multiplication, all Zero and Omega factors are converted to base-0 exponents (ωa = 0a), summed, and reconverted. This single mechanism handles: 0*w=1 (exponents cancel), 0^2*w=0 (partial cancel), 0^a * 0^b = 0^{a+b} (symbolic combine), and cross-base products like 0^5 * w^2 = 0^3.

6.3 Negative Zero and the Chebyshev Reduction

-0 is a valid, distinct expression — the additive inverse of traction zero. In the Chebyshev ring, the identity 0 + w = null (additive inverse) combined with 0 * w = 1 (multiplicative inverse) forces ω = −0: since 0² = −1, we get ω = 0−1 = 0/0² = 0/(−1) = −0.

This single identity recovers the 8th roots of unity purely algebraically. The Explain tab's Reduced Form section computes this by reducing the ring polynomial modulo Tn(s/2) = 0 (the Chebyshev polynomial relation from 0 + ω = ∅) and substituting back:

Expression Reduced Form Complex value (θ = π/2)
0^0 1 1
0 0 i
0^2 −1 −1
0^3 −0 i
0^4 1 1

The Traction Plane plot displays these values with cardinal points 1 (right), −1 (left), 0 (top), −0 (bottom), confirming that traction zero acts as the imaginary unit i.

6.4 Avoiding sp_simplify

SymPy's simplify() is deliberately avoided in the projection pipeline. It aggressively rewrites expressions in ways that break numeric evaluation — for instance, merging i·√r into √(−r), which produces NaN for positive r. Projected expressions are left in their natural factored form.

---
7. Parser & Functions (parser.py)

The parser is a hand-written recursive descent parser supporting full operator precedence (parentheses, exponentiation, multiplication/division, addition/subtraction) with right-associative ^.

7.1 Built-in Functions
Syntax Meaning
log0(expr) Base-0 logarithm (symbolic until projected)
logw(expr) Base-ω logarithm
solve(expr) or solve(expr, var) Solve expr = 0 for var. Returns a SolutionSet.
expand(expr) Symbolic expansion (distributes products over sums)
factor(expr) Symbolic factoring
7.2 Equation Syntax

The parser supports equation syntax: lhs = rhs is automatically converted to solve(lhs − rhs) and solved. The result is a single solution or a SolutionSet.

7.3 Graded Element Syntax

Graded elements can be entered as Z_n(expr) or Z(n, expr), where n is the grade level. These are first-class values: they can be combined with arithmetic operators, and the grade algebra rules (see §2) apply automatically.

---
8. GUI (gui/)

The calculator GUI (gui/app.py) is a tkinter window with an input display, a result display (with an ≈ toggle for approximation mode), and a five-tab workspace:

Tab Contents
Plot Calculator number pad (left) + phase-plot canvas with hover gauges, zoom controls, and settings (right)
Explain Chebyshev ring decomposition text + orbit plot (matplotlib)
Phase Map Interactive phase-amplitude decomposition at varying θ, with orbit, amplitude, and table subplots
Orient 3D 3D tower visualisation with two rotation sliders (θw and θu)
Help Scrollable, clickable examples organised by section
8.1 Approximation Mode

The result display has an = / toggle button. In exact mode, results display as symbolic traction expressions (fractions, Zn, etc.). In approximation mode, rationals and complex values are displayed as decimals.

8.2 Full-Screen Viewer (gui/fullscreen.py)

A dedicated full-screen window with pan and zoom. Uses dual-layer image buffers: a low-resolution persistent layer for instant feedback during interaction, and a high-resolution progressive layer rendered after a 200 ms debounce. Includes a coordinate grid overlay and legend.

8.3 Fractal Mode (fractal.py)

Expressions containing the variable c trigger fractal (escape-time iteration) mode. The iterator starts at traction zero (x0 = 0, which projects to eiπ/2 = i under θ = π/2) and iterates the expression at each grid point. Colouring uses smooth HSV with anti-banding based on escape count and final magnitude.

8.4 Settings (gui/settings.py)

The settings window provides controls for: colour mode (phase or continuity), active projection (Complex Lie or Q-Surface), and gradient overlays (tangent lines following |f| gradient, normal lines following constant-|f| contours).

---
9. Known Limitations

SymPy eager evaluation: 0^1 is eagerly simplified to Zero() by _eval_power, losing the exponent information. This means Pow(Zero(), 1) and bare Zero() are indistinguishable to the projector — both project via eW.

Non-analytic two-variable mode: When p and q are used as independent real variables, the function is generally not analytic in the complex sense. Phase and magnitude gradients are independent, so the tangent/normal lines (which follow magnitude) may not align with the phase colouring. This is correct behaviour, not a bug.

Projection completeness: The Lie exponential projection is one of potentially many valid mappings from traction to ℂ. The plugin system exists specifically to allow alternative projections (e.g. rational-magnitude formulas) to be developed and compared.

---