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
- API Quick Reference
- Code Comparison
- Key Differences
- Sparse & Iterative Solvers
- Zero-Allocation Pattern
- Migration Steps
// These method names are directly compatible with Eigen
.zero() .identity() .constant() .transpose()
.inverse() .determinant() .trace() .norm()
.dot() .cross() .normalized() .random()| 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 |
| 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) |
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();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);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);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();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();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);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();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
}| 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) |
Zig has no operator overloading. All operators become explicit method calls.
Eigen: try { auto inv = matrix.inverse(); } catch(...) {}
Zigen: const inv = try matrix.inverse(); or matrix.inverse() catch |err| { ... }
Eigen: MatrixXf m(10, 10); — automatically freed
Zigen: var m = try MatrixXf.init(allocator, 10, 10); defer m.deinit();
Zigen encodes matrix dimensions in the type via comptime parameters, enabling stronger compile-time checks than Eigen's template approach.
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)
const inv = try matrix.inverse();
const lu = try LU(f32, 3).compute(A);var m = try MatrixXf.init(allocator, 10, 10);
defer m.deinit();zig build test
cd bench && ./run_benchmark.sh --dim 10Related Documentation:
- API Reference — Complete API listing
- Module Docs — Per-module detailed reference
- Project Homepage — Zigen project introduction
- Benchmark Report — Performance comparison with Eigen