Skip to content

Latest commit

 

History

History
298 lines (226 loc) · 7.47 KB

File metadata and controls

298 lines (226 loc) · 7.47 KB

Eigen → Zigen Migration Guide

A complete guide for migrating from Eigen C++ to Zigen Zig, designed for C++ developers.

Core advantage: API naming 95% compatible | Close performance | Type-safe | Zero dependencies


Table of Contents


API Quick Reference

Core Methods (Compatible Naming)

// These method names are directly compatible with Eigen
.zero()  .identity()  .constant()  .transpose()
.inverse()  .determinant()  .trace()  .norm()
.dot()  .cross()  .normalized()  .random()

Naming Differences

Eigen Zigen Reason
matrix(i,j) matrix.at(i,j) No operator overloading in Zig
matrix(i,j) = v matrix.set(i,j,v) Same
m1 * m2 m1.mul(cols, m2) No operator overloading
m * 2.0 m.scale(2.0) Same
m1 + m2 m1.add(m2) Same

Type Mapping

Eigen C++ Zigen
Matrix3f / Matrix3d Matrix3f / Matrix3d
Vector3f / Vector3d Vector3f / Vector3d
MatrixXf / MatrixXd MatrixXf / MatrixXd
VectorXf / VectorXd VectorXf / VectorXd
Quaternionf / Quaterniond Quaternionf / Quaterniond
SparseMatrix<double> SparseMatrix(f64)
SimplicialLDLT<> SimplicialLDLT(f64)
ConjugateGradient<> ConjugateGradient(f64)
BiCGSTAB<> BiCGSTAB(f64)

Code Comparison

1. Matrix Creation and Operations

Eigen (C++):

#include <Eigen/Dense>

Eigen::Matrix3f m1 = Eigen::Matrix3f::Identity();
Eigen::Matrix3f m2 = Eigen::Matrix3f::Zero();
float val = m1(0, 1);
m1(0, 1) = 3.14f;
Eigen::Matrix3f m3 = m1 + m2;
Eigen::Matrix3f m4 = m1 * m2;
Eigen::Matrix3f m5 = m1.transpose();

Zigen (Zig):

const Zigen = @import("zigen");

const m1 = Zigen.Matrix3f.identity();
const m2 = Zigen.Matrix3f.zero();
const val = m1.at(0, 1);
var m1_mut = m1;
m1_mut.set(0, 1, 3.14);
const m3 = m1.add(m2);
const m4 = m1.mul(3, m2);
const m5 = m1.transpose();

2. Solving Linear Systems

Eigen (C++):

Eigen::PartialPivLU<Eigen::Matrix3f> lu(A);
Eigen::Vector3f x = lu.solve(b);

Zigen (Zig):

const lu = try Zigen.LU(f32, 3).compute(A);
const x = lu.solve(b);

3. Quaternion Rotation

Eigen (C++):

Eigen::Quaternionf q(Eigen::AngleAxisf(M_PI/2, axis));
Eigen::Vector3f rotated = q * v;
Eigen::Quaternionf q2 = q1.slerp(0.5, q);

Zigen (Zig):

const q = Zigen.Quaternionf.fromAxisAngle(axis, std.math.pi / 2.0);
const rotated = q.rotate(v);
const q2 = q1.slerp(q, 0.5);

4. Dynamic Matrices

Eigen (C++):

Eigen::MatrixXd m(10, 10);        // Stack or heap, automatic
Eigen::MatrixXd inv = m.inverse();

Zigen (Zig):

var m = try Zigen.MatrixXd.init(allocator, 10, 10);
defer m.deinit();                  // Explicit lifetime
var inv = try m.inverseDynamic();
defer inv.deinit();

Sparse & Iterative Solvers

Sparse Matrix Construction

Eigen (C++):

Eigen::SparseMatrix<double> sp(n, n);
std::vector<Eigen::Triplet<double>> triplets;
triplets.push_back({0, 0, 2.0});
sp.setFromTriplets(triplets.begin(), triplets.end());

Zigen (Zig):

var sp = Zigen.SparseMatrix(f64).init(allocator, n, n);
defer sp.deinit();
try sp.insert(0, 0, 2.0);
try sp.finalize();

Sparse Solve

Eigen (C++):

Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
solver.compute(sp);
Eigen::VectorXd x = solver.solve(b);

Zigen (Zig):

var lu = try Zigen.SparseLU(f64).compute(allocator, sp);
defer lu.deinit();
var x = try lu.solve(b_data);
defer allocator.free(x);

Iterative Solvers

Eigen (C++):

Eigen::ConjugateGradient<Eigen::SparseMatrix<double>> cg;
cg.compute(A);
Eigen::VectorXd x = cg.solve(b);

Zigen (Zig):

var cg = Zigen.ConjugateGradient(f64).init(allocator, n);
defer cg.deinit();
try cg.compute(A);
var x = try cg.solve(b);
defer x.deinit();

Zero-Allocation Pattern

Zigen provides workspace-reuse APIs for performance-critical code. This is similar to Eigen's object reuse but made explicit:

Eigen (C++):

// Eigen reuses internal buffers implicitly
Eigen::PartialPivLU<MatrixXd> lu;
for (auto& A : matrices) {
    lu.compute(A);
    x = lu.solve(b);
}

Zigen (Zig):

// Zigen: explicit workspace allocation, zero allocs in loop
var lu = try Zigen.LUDynamic(f64).init(allocator, n);
defer lu.deinit();
var x_buf = try allocator.alloc(f64, n);
var pb_buf = try allocator.alloc(f64, n);
var y_buf = try allocator.alloc(f64, n);

for (matrices) |A| {
    lu.computeFrom(A);                       // Reuse workspace
    lu.solveInto(b, x_buf, pb_buf, y_buf);   // Write into buffers
}

Available Zero-Allocation APIs

Allocating Zero-Allocation
lu.solve(b) lu.solveInto(b, x, pb, y)
lu.inverse() lu.inverseInto(result, e, x, pb, y)
m.inverseDynamic() m.inverseDynamicInto(&result, work)
sp.mulVec(x) sp.mulVecInto(x, y)
m.add(other) m.addInto(other, &result)
m.mul(cols, other) m.mulInto(other, &result)
m.transpose() m.transposeInto(&result)

Key Differences

1. Operators → Method Calls

Zig has no operator overloading. All operators become explicit method calls.

2. Error Handling: Exceptions → Error Unions

Eigen: try { auto inv = matrix.inverse(); } catch(...) {}

Zigen: const inv = try matrix.inverse(); or matrix.inverse() catch |err| { ... }

3. Memory Management: RAII → Explicit Allocator

Eigen: MatrixXf m(10, 10); — automatically freed

Zigen: var m = try MatrixXf.init(allocator, 10, 10); defer m.deinit();

4. Compile-Time Dimensions

Zigen encodes matrix dimensions in the type via comptime parameters, enabling stronger compile-time checks than Eigen's template approach.


Migration Steps

Step 1: Syntax Conversion

matrix(i,j)      → matrix.at(i,j)
matrix(i,j) = v  → matrix.set(i,j,v)
m1 + m2          → m1.add(m2)
m1 * m2          → m1.mul(cols, m2)

Step 2: Add Error Handling

const inv = try matrix.inverse();
const lu = try LU(f32, 3).compute(A);

Step 3: Manage Dynamic Memory

var m = try MatrixXf.init(allocator, 10, 10);
defer m.deinit();

Step 4: Verify Correctness

zig build test
cd bench && ./run_benchmark.sh --dim 10

Related Documentation: