Skip to content

14_concepts_and_property_invariants.cpp

Demonstrates C++20 linear algebra concepts, compile-time invariant tagging (assume_spd), and loud warnings on untagged storage.

#include <iomanip>
#include <iostream>
#include <numerics.hpp>
#include <string_view>

int main(int argc, char **argv) {
    const bool plot = argc > 1 && std::string_view(argv[1]) == "--plot";
    using namespace num;

    std::cout << "========================================================================\n";
    std::cout << "  14: C++20 Linear Algebra Invariants & Loud Diagnostics Demonstration  \n";
    std::cout << "========================================================================\n\n";

    // 1. Construct a 4x4 Symmetric Positive-Definite (SPD) 1D Laplace mat
    mat<real> A(4, 4, 0.0);
    A(0, 0) = 2.0; A(0, 1) = -1.0;
    A(1, 0) = -1.0; A(1, 1) = 2.0; A(1, 2) = -1.0;
    A(2, 1) = -1.0; A(2, 2) = 2.0; A(2, 3) = -1.0;
    A(3, 2) = -1.0; A(3, 3) = 2.0;

    vec<real> b{1.0, 2.0, 2.0, 1.0};

    // 2. Untagged input does not compile; the escape hatch is explicit
    std::cout << "--- [Case 1: Deliberate opt-out via num::unsafe::cholesky(A)] ---\n";
    std::cout << "cholesky(assume_spd(A)) on a raw mat is a compile error: a raw matrix carries no SPD\n"
                 "invariant, and Cholesky is undefined without one. Uncommenting the line below\n"
                 "fails to build, rather than warning and continuing:\n\n"
                 "    // auto bad = cholesky(assume_spd(A));  // error: static assertion failed\n\n"
                 "To take the precondition on faith anyway, say so at the call site:\n";

    auto chol_untagged = unsafe::cholesky(A); // opt-out, greppable, no verification
    vec<real> x_untagged(4, 0.0);
    solve(chol_untagged, b, x_untagged);
    std::cout << "unsafe:: Solution x = [" << x_untagged[0] << ", " << x_untagged[1] << ", "
              << x_untagged[2] << ", " << x_untagged[3] << "]\n\n";

    // 3. Demonstration of Tagged Input via assume_spd(A) -> 100% Warning-Free
    std::cout << "--- [Case 2: Tagged Invariant Input cholesky(assume_spd(A))] ---\n";
    std::cout << "Wrapping with assume_spd(A) makes it an spd_operator and runs 100% warning-free:\n";

    auto spd_matrix = assume_spd(A);

    // Static concept verification at compile time:
    static_assert(spd_operator<decltype(spd_matrix)>, "assume_spd attaches law::spd");
    static_assert(self_adjoint_operator<decltype(spd_matrix)>, "spd implies self-adjoint");

    auto chol_tagged = cholesky(spd_matrix);
    vec<real> x_tagged(4, 0.0);
    solve(chol_tagged, b, x_tagged);
    std::cout << "Tagged Solution x   = [" << x_tagged[0] << ", " << x_tagged[1] << ", "
              << x_tagged[2] << ", " << x_tagged[3] << "]\n\n";

    // 4. Demonstration of Dynamic Invariant Validation via make_spd(A)
    std::cout << "--- [Case 3: Dynamic Validation via make_spd(A)] ---\n";
    try {
        auto validated = make_spd(A);
        std::cout << "make_spd(A) successfully validated positive definiteness.\n";
    } catch (const std::exception &e) {
        std::cout << "Validation error: " << e.what() << "\n";
    }

    mat<real> Indefinite(2, 2, 0.0);
    Indefinite(0, 0) = 1.0; Indefinite(0, 1) = 3.0;
    Indefinite(1, 0) = 3.0; Indefinite(1, 1) = 1.0; // det = -8 < 0
    std::cout << "Testing make_spd on Indefinite mat [[1, 3], [3, 1]]...\n";
    try {
        auto invalid = make_spd(Indefinite);
        (void)invalid;
    } catch (const std::exception &e) {
        std::cout << "[Caught Expected Violation] " << e.what() << "\n\n";
    }

    // 5. In-Terminal Solution Visualization
    std::vector<double> grid{0.0, 1.0, 2.0, 3.0};
    std::vector<double> sol{x_tagged[0], x_tagged[1], x_tagged[2], x_tagged[3]};

    if (plot) {
        plt::plot(grid, sol, "x_solution", "linespoints");
        plt::title("14 Concepts & Property Invariants: 1D Poisson Solution");
        plt::xlabel("Grid Node i");
        plt::ylabel("Solution x_i");
        plt::show();
    }

    std::cout << "\n[SUCCESS] Example 14 completed.\n";
    return 0;
}

Possible output:

========================================================================
  14: C++20 Linear Algebra Invariants & Loud Diagnostics Demonstration
========================================================================

--- [Case 1: Deliberate opt-out via num::unsafe::cholesky(A)] ---
cholesky(assume_spd(A)) on a raw mat is a compile error: a raw matrix carries no SPD
invariant, and Cholesky is undefined without one. Uncommenting the line below
fails to build, rather than warning and continuing:

    // auto bad = cholesky(assume_spd(A));  // error: static assertion failed

To take the precondition on faith anyway, say so at the call site:
unsafe:: Solution x = [3, 5, 5, 3]

--- [Case 2: Tagged Invariant Input cholesky(assume_spd(A))] ---
Wrapping with assume_spd(A) makes it an spd_operator and runs 100% warning-free:
Tagged Solution x   = [3, 5, 5, 3]

--- [Case 3: Dynamic Validation via make_spd(A)] ---
make_spd(A) successfully validated positive definiteness.
Testing make_spd on Indefinite mat [[1, 3], [3, 1]]...
[Caught Expected Violation] make_spd: matrix is not symmetric positive definite


[SUCCESS] Example 14 completed.