Skip to content

Core solver API: inverse and general solve

Keywords: solver · inverse · general solve · residual

The `solver_basic_xx.py" demonstrate the equation-solving pipeline in G(3,0) float64.

Topics covered:

1 — Multivector inverse via solve() 2 — General solve: A * X = B 3 — Detecting a singular system (solve vs solve_lsq)

Source

ga/numerics/solver_basics_02.py

Code

# SPDX-License-Identifier: Apache-2.0
# Copyright 2021 Christian Perwass

"""
solver_basics_02.py — Core solver API: inverse and general solve.

The `solver_basic_xx.py" demonstrate the equation-solving pipeline in G(3,0) float64.

Topics covered:

  1 — Multivector inverse via solve()
  2 — General solve: A * X = B
  3 — Detecting a singular system (solve vs solve_lsq)

Keywords: solver, inverse, general solve, residual
"""

from __future__ import annotations

from pytanga import Algebra
from pytanga.solver.solve import solve


def hr(title: str) -> None:
    """Print a title with a horizontal rule."""
    print(f"\n{'─' * 60}\n{title}\n{'─' * 60}")


# ---------------------------------------------------------------------------
# Setup
# ---------------------------------------------------------------------------

# If you run this script for the first time, creating the algebra will trigger
# a one-time codegen step that may take a few seconds.
alg = Algebra(3, 0, "float64")

# ---------------------------------------------------------------------------
# General solve: A * X = B
# ---------------------------------------------------------------------------
hr("General solve: A * X = B")

# Use a general (mixed-grade) multivector so the sub-algebra has full depth.
A = alg("0.5 + e1 - 2.0 e2")
print(f"A = {A}")

B = alg("1 + 3 e1")
print(f"A = {A}")
print(f"B = {B}")

X = solve(A, B)
print(f"X = {X}")

check = A * X
check.prune()
print(f"A*X = {check}")

# Residual norm: max absolute coefficient error
residual = {k: abs((A * X).to_dict().get(k, 0.0) - v) for k, v in B.to_dict().items()}
print(f"Max residual: {max(residual.values()):.2e}  (should be ~1e-15)")