Skip to content

Core solver API: inverse and general solve

Keywords: solver · singular · solve_lsq · least-norm · G(3,1)

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_03.py

Code

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

"""
solver_basics_03.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, singular, solve_lsq, least-norm, G(3,1)
"""

from __future__ import annotations

import numpy as np
from pytanga import Algebra
from pytanga.solver.solve import solve, solve_lsq


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")

# 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}")

# ---------------------------------------------------------------------------
# Part C — Singular system: solve vs solve_lsq
# ---------------------------------------------------------------------------
hr("Part C — Singular A: solve raises, solve_lsq gives least-norm solution")

alg = Algebra(3, (1,), "float64")  # G(3,1) has a null vector (grade-1 blade)

# a null vector is singular: A_sing * A_sing = 0, so the system A_sing * X = 1 has no solution.
A_sing = alg("e1 + e2")

print(f"A_singular = {A_sing}")
print(f"A_sing * A_sing = {A_sing * A_sing}  (should be 0)")

try:
    X_fail = solve(A_sing, 1.0)
    print(f"solve result:  X = {X_fail}")
    print(f"A_singular * X_fail = {A_sing * X_fail}")
    print("solve succeeded (unexpected for this example)")
except np.linalg.LinAlgError as e:
    print(f"solve raised LinAlgError: {e}")
except ValueError as e:
    print(f"solve raised ValueError: {e}")
except Exception as e:
    print(f"solve raised unexpected exception: {type(e).__name__}: {e}")


X_lsq = solve_lsq(A_sing, 1.0)
check_lsq = A_sing * X_lsq
check_lsq.prune()
print(f"solve_lsq result:  X = {X_lsq}")
print(f"A_singular * X_lsq = {check_lsq}  (least-squares residual)")