Eigen для C++: полное руководство с примерами
1. Что это и зачем нужна
Eigen — это высокопроизводительная, шаблонная библиотека линейной алгебры для языка C++. Она предоставляет разработчикам мощные и удобные инструменты для работы с матрицами, векторами, численными решателями и связанными алгоритмами. В отличие от многих других библиотек (например, BLAS/LAPACK), Eigen написан на чистом C++ без внешних зависимостей, что делает его идеальным выбором для проектов, где важна простота интеграции и переносимость кода.
Зачем она нужна? В современном мире программирования линейная алгебра лежит в основе огромного количества задач: от компьютерной графики и обработки изображений до машинного обучения, робототехники и научных симуляций. Eigen позволяет разработчикам не изобретать велосипед, а использовать проверенные, оптимизированные алгоритмы, которые часто работают быстрее, чем написанные вручную реализации. Библиотека активно использует шаблоны C++ (template expressions) для выполнения сложных оптимизаций на этапе компиляции, что позволяет избежать накладных расходов на создание временных объектов и объединять операции в один эффективный цикл.
Eigen поддерживает все основные типы матриц (плотные, разреженные, фиксированного и динамического размера), широкий спектр разложений (LU, QR, SVD, Cholesky, собственные значения) и геометрические преобразования (повороты, трансляции, аффинные преобразования). Благодаря своей гибкости и производительности, Eigen является стандартом де-факто для многих C++ проектов, включая такие известные фреймворки, как ROS (Robot Operating System), TensorFlow (частично) и CGAL.
2. Установка
Eigen — это библиотека только с заголовочными файлами (header-only). Это означает, что вам не нужно компилировать отдельную библиотеку или линковаться с ней. Достаточно скачать файлы и указать компилятору путь к ним.
Способ 1: Ручная установка (рекомендуемый для изучения)
# Скачиваем последнюю версию с официального сайта или GitHub
wget https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz
tar -xzf eigen-3.4.0.tar.gz
sudo mv eigen-3.4.0 /usr/local/include/eigen3
Теперь при компиляции нужно указать путь к заголовочным файлам:
g++ -std=c++11 -I /usr/local/include/eigen3 my_program.cpp -o my_program
Способ 2: Через пакетный менеджер (Linux/macOS)
# Ubuntu/Debian
sudo apt-get install libeigen3-dev
# macOS (через Homebrew)
brew install eigen
Способ 3: Через vcpkg или Conan (Windows)
# vcpkg
vcpkg install eigen3
# Conan
conan install eigen/3.4.0@
Важно: В отличие от Python (pip) или JavaScript (npm), Eigen не устанавливается через пакетный менеджер языка. Это системная библиотека на C++.
3. Быстрый старт — минимальный рабочий пример
Создайте файл main.cpp и скопируйте туда следующий код:
#include <iostream>
#include <Eigen/Dense> // Основной заголовок для плотных матриц
int main() {
// Создаем матрицу 2x2 и заполняем её
Eigen::Matrix2d mat;
mat << 1, 2,
3, 4;
// Создаем вектор 2x1
Eigen::Vector2d vec(5, 6);
// Умножаем матрицу на вектор
Eigen::Vector2d result = mat * vec;
// Выводим результат
std::cout << "Matrix:\n" << mat << std::endl;
std::cout << "Vector:\n" << vec << std::endl;
std::cout << "Result (mat * vec):\n" << result << std::endl;
return 0;
}
Компиляция и запуск:
g++ -std=c++11 -I /path/to/eigen main.cpp -o main
./main
Вывод:
Matrix:
1 2
3 4
Vector:
5
6
Result (mat * vec):
17
39
4. Основные методы/функции/классы
4.1. Eigen::MatrixXd — Матрица динамического размера
Сигнатура: class MatrixXd;
Что делает: Создает матрицу, размер которой задается во время выполнения. X означает динамический размер, d — тип double.
#include <Eigen/Dense>
#include <iostream>
int main() {
Eigen::MatrixXd m(2, 3); // 2 строки, 3 столбца
m(0, 0) = 1.0;
m(1, 2) = 5.5;
std::cout << "MatrixXd:\n" << m << std::endl;
return 0;
}
4.2. .rows() и .cols() — Получение размеров
Сигнатура: Index rows() const; и Index cols() const;
Что делает: Возвращает количество строк и столбцов матрицы соответственно.
Eigen::MatrixXd m(4, 7);
std::cout << "Rows: " << m.rows() << ", Cols: " << m.cols() << std::endl;
// Вывод: Rows: 4, Cols: 7
4.3. .transpose() — Транспонирование
Сигнатура: TransposeReturnType transpose();
Что делает: Возвращает транспонированную матрицу. Важно: a = a.transpose() вызовет ошибку (aliasing issue). Используйте .transposeInPlace().
Eigen::Matrix2i a;
a << 1, 2,
3, 4;
Eigen::Matrix2i b = a.transpose();
std::cout << "Original:\n" << a << "\nTransposed:\n" << b << std::endl;
4.4. .inverse() — Обратная матрица
Сигнатура: InverseReturnType inverse() const;
Что делает: Вычисляет обратную матрицу. Для больших матриц рекомендуется использовать разложения (например, .partialPivLu().solve()).
Eigen::Matrix2d a;
a << 4, 7,
2, 6;
Eigen::Matrix2d inv_a = a.inverse();
std::cout << "Inverse:\n" << inv_a << std::endl;
// Проверка: a * inv_a должно быть единичной матрицей
std::cout << "Check (should be identity):\n" << a * inv_a << std::endl;
4.5. .determinant() — Детерминант
Сигнатура: Scalar determinant() const;
Что делает: Вычисляет определитель квадратной матрицы.
Eigen::Matrix3d a;
a << 1, 2, 3,
4, 5, 6,
7, 8, 10; // Матрица с детерминантом -3
std::cout << "Determinant: " << a.determinant() << std::endl;
// Вывод: Determinant: -3
4.6. .eigenvalues() — Собственные значения
Сигнатура: EigenSolver<MatrixType>::EigenvalueType eigenvalues() const;
Что делает: Вычисляет собственные значения матрицы. Возвращает комплексный вектор.
Eigen::Matrix2d a;
a << 2, 1,
1, 2;
Eigen::EigenSolver<Eigen::Matrix2d> solver(a);
std::cout << "Eigenvalues:\n" << solver.eigenvalues() << std::endl;
// Вывод: (3, 0) и (1, 0)
4.7. .block() — Извлечение подматрицы (блока)
Сигнатура: Block<Derived> block(Index startRow, Index startCol, Index blockRows, Index blockCols);
Что делает: Позволяет получить доступ к подматрице (блоку) исходной матрицы. Изменения в блоке влияют на исходную матрицу.
Eigen::Matrix4d m = Eigen::Matrix4d::Random();
std::cout << "Top-left 2x2 block:\n" << m.block<2,2>(0,0) << std::endl;
// Изменяем блок
m.block<2,2>(2,2) = Eigen::Matrix2d::Zero();
std::cout << "Matrix after zeroing bottom-right block:\n" << m << std::endl;
4.8. .col() и .row() — Доступ к строкам и столбцам
Сигнатура: ColXpr col(Index i); и RowXpr row(Index i);
Что делает: Возвращает ссылку на i-й столбец или i-ю строку матрицы.
Eigen::Matrix3d m = Eigen::Matrix3d::Identity();
m.col(1) = Eigen::Vector3d(10, 20, 30); // Заменяем второй столбец
std::cout << "Modified matrix:\n" << m << std::endl;
4.9. .norm() — Норма вектора
Сигнатура: Scalar norm() const;
Что делает: Вычисляет L2 норму (длину) вектора. Для матриц возвращает норму Фробениуса.
Eigen::Vector3d v(3.0, 4.0, 0.0);
std::cout << "Norm of (3,4,0): " << v.norm() << std::endl;
// Вывод: 5
4.10. .solve() — Решение системы линейных уравнений
Сигнатура: Solve<Decomposition> solve(const MatrixBase<OtherDerived> &b) const;
Что делает: Решает систему Ax = b. Обычно используется с разложением (например, ldlt, partialPivLu).
Eigen::Matrix2d A;
A << 2, 1,
1, 3;
Eigen::Vector2d b(4, 5);
Eigen::Vector2d x = A.ldlt().solve(b);
std::cout << "Solution x:\n" << x << std::endl;
// Проверка: A * x должно быть равно b
std::cout << "Check (A*x):\n" << A * x << std::endl;
5. Реальный практический пример: Аппроксимация кривой методом наименьших квадратов
В этом примере мы решим задачу подгонки полинома второй степени под набор точек данных. Это классическая задача линейной регрессии, решаемая с помощью псевдообратной матрицы (SVD).
#include <iostream>
#include <vector>
#include <Eigen/D