soma.cell

Solving 2x2 linear systems — Cramer's rule via determinants

For A·x = b with A 2x2, Cramer's rule gives x_i = det(A_i)/det(A), where A_i is A with column i replaced by b. det() is the matrix builtin. A zero determinant means the system is singular (no unique solution).

A Soma program from the math part of the verified corpus: 46 lines, cells LinearSolve, SolveTests. It passes soma check with soma 3.2.4.

How it is built

The cell LinearSolve declares 2 signals in its face: solve2(a: Float, b: Float, c: Float, d: Float, e: Float, f: Float) -> List and run() -> Map. Handlers: solve2 (line 19) and run (line 28).

The file also holds 1 test cell (SolveTests) with 4 assertions, run by soma test.

Generated from soma describe on this file.

The source

linear_solve.cell

// ============================================================================
// Solving 2x2 linear systems — Cramer's rule via determinants
// ============================================================================
//
// For A·x = b with A 2x2, Cramer's rule gives x_i = det(A_i)/det(A), where
// A_i is A with column i replaced by b. det() is the matrix builtin. A zero
// determinant means the system is singular (no unique solution).
//
//   soma run linear_solve.cell run
// ============================================================================

cell LinearSolve {
    face {
        signal solve2(a: Float, b: Float, c: Float, d: Float, e: Float, f: Float) -> List
        signal run() -> Map
    }

    // solve  [a b; c d] x = [e; f]
    on solve2(a: Float, b: Float, c: Float, d: Float, e: Float, f: Float) {
        let A = [a, b, c, d].reshape(2, 2)
        let detA = det(A)
        if abs(detA) < 0.0000000001 { return [] }   // singular
        let dx = det([e, b, f, d].reshape(2, 2))
        let dy = det([a, e, c, f].reshape(2, 2))
        return [dx / detA, dy / detA]
    }

    on run() {
        // 2x + y = 5 ; x + 3y = 10  ->  x=1, y=3
        let sol = solve2(2.0, 1.0, 1.0, 3.0, 5.0, 10.0)
        return map("x", sol[0], "y", sol[1])
    }
}

cell test SolveTests {
    rules {
        // 2x+y=5, x+3y=10 -> x=1, y=3
        assert abs(solve2(2.0, 1.0, 1.0, 3.0, 5.0, 10.0)[0] - 1.0) < 0.0000001
        assert abs(solve2(2.0, 1.0, 1.0, 3.0, 5.0, 10.0)[1] - 3.0) < 0.0000001
        // x+y=2, x-y=0 -> x=1, y=1
        assert abs(solve2(1.0, 1.0, 1.0, 0 - 1.0, 2.0, 0.0)[0] - 1.0) < 0.0000001
        // singular system returns empty
        assert len(solve2(1.0, 2.0, 2.0, 4.0, 3.0, 6.0)) == 0
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check linear_solve.cell
✓ All checks passed.
$ soma test linear_solve.cell
test SolveTests ...
  ✓ assert abs(solve2(2.0, 1.0, 1.0, 3.0, 5.0, 10.0)[0] - 1.0) < 0.0000001
  ✓ assert abs(solve2(2.0, 1.0, 1.0, 3.0, 5.0, 10.0)[1] - 3.0) < 0.0000001
  ✓ assert abs(solve2(1.0, 1.0, 1.0, 0 - 1.0, 2.0, 0.0)[0] - 1.0) < 0.0000001
  ✓ assert len(solve2(1.0, 2.0, 2.0, 4.0, 3.0, 6.0)) == 0

4 tests: 4 passed, 0 failed
$ soma verify linear_solve.cell
Verifying linear_solve.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)
$ soma run linear_solve.cell run
{"x": 1.0, "y": 3.0}

Builtins used

abs, det, len, map

Source file: /corpus/math/linear_solve.cell · examples/corpus/math/linear_solve.cell