# Mantissa 使用教程 — Python 版与 C++ 版

Mantissa 使用教程 --- Python 版与 C++ 版

本教程面向使用者,手把手带你完成 Mantissa 的安装、第一次调用,以及全部算子在 Python 和 C++ 两侧的用法。

  • 想在脚本 / 数据分析中使用 → 直接看 [第一部分:Python 版](#第一部分:Python 版)
  • 想集成进 C++ 工程 → 直接看 [第二部分:C++ 版](#第二部分:C++ 版)
  • 两边都要用 → 顺序阅读,最后有 [Python ↔ C++ 对照表](#Python ↔ C++ 对照表)

Mantissa 是什么

Mantissa 是一个高性能 C++17 数学算法库,当前聚焦线性代数,特点:

  • 一套算法,两种用法:C++ 原生 API + nanobind Python 绑定(零拷贝,直接收发 NumPy 数组)
  • 数值安全:输入不合法时显式抛异常,绝不静默回退
  • abi3 Wheel :Python 侧一次 pip install 即可用,无需 C++ 编译器

当前所有算子支持 CPU / float64(CUDA 后端在路线图中)。算子一览:

算子 分类 功能 Python C++
eig_sym eigen 对称矩阵特征分解 A = VΛVᵀ
eig eigen 一般(可非对称)矩阵特征分解
cholesky decomposition Cholesky 分解 A = LLᵀ
svd decomposition Thin SVD(支持矩形矩阵)
qr decomposition Thin QR 分解 A = QR
matrix_sqrt matrix_func 对称半正定矩阵平方根
matrix_exp matrix_func 对称矩阵指数
matrix_log matrix_func 对称正定矩阵对数
matrix_sign matrix_func 对称矩阵符号函数
solve solve 线性方程组 A·X = B

不同版本发布的算子数量可能不同,以 print(dir(mantissa))(Python)或 include/mantissa/linalg/ 下的头文件(C++)为准。


第一部分:Python 版

1.1 安装

bash 复制代码
pip install mantissa-cpp numpy

包名说明 :PyPI 上 mantissa 这个名字已被 Twisted 项目占用,所以本项目的分发名是 mantissa-cpp ;但安装后 import 名仍是 mantissa

python 复制代码
import mantissa
print(mantissa.__version__)

Wheel 中已包含编译好的 .pyd / .so 及运行时 DLL,支持 Python 3.8+(Windows x64 / Linux x86_64 / macOS arm64)。

1.2 三条核心约定

用之前只需记住三件事:

  1. dtype 必须是 np.float64 。传 float32 / int 数组会抛 ValueError,不会静默转换。需要时先 A = A.astype(np.float64)
  2. 数组建议是 C-contiguous (NumPy 新建数组默认满足)。转置视图等非连续输入建议先 np.ascontiguousarray(A)
  3. 返回值是零拷贝的 NumPy 数组 ,通过 capsule 持有 C++ 内存的生命周期,可正常读取;如果要就地修改,先 .copy()

异常映射规则:

场景 Python 异常
shape / dtype / 对称性不合法 ValueError
数值计算失败(不收敛、奇异等) RuntimeError

1.3 快速上手

python 复制代码
import numpy as np
import mantissa

A = np.array([[4.0, 1.0],
              [1.0, 3.0]])          # 对称正定矩阵

# 特征分解
values, vectors = mantissa.eig_sym(A)
print(values)                        # [2.382, 4.618],升序

# SVD
U, S, Vt = mantissa.svd(A)

# 解线性方程组 A·x = b
b = np.array([1.0, 2.0])
x = mantissa.solve(A, b)
print(np.allclose(A @ x, b))         # True

1.4 算子逐个讲

特征分解:eig_sym / eig

eig_sym(A) --- 对称矩阵专用,更快更稳:

python 复制代码
A = np.array([[4.0, 1.0], [1.0, 3.0]])
values, vectors = mantissa.eig_sym(A)

# values 升序排列;vectors 的第 j 列对应 values[j]
assert np.allclose(A, vectors @ np.diag(values) @ vectors.T)
assert np.allclose(A @ vectors[:, 0], values[0] * vectors[:, 0])

eig(A) --- 一般方阵(可以不对称)。特征值/特征向量可能是复数,而 Mantissa 内部只支持实数 dtype,所以返回 4 元组(实部、虚部分开):

python 复制代码
A = np.array([[0.0, -1.0], [1.0, 0.0]])   # 旋转矩阵,特征值为 ±i
vr, vi, Sr, Si = mantissa.eig(A)

values  = vr + 1j * vi      # 复特征值
vectors = Sr + 1j * Si      # 复特征向量(按列)

实输入的复特征值以共轭对形式出现。对称矩阵请优先用 eig_sym

矩阵分解:cholesky / svd / qr

python 复制代码
# Cholesky:要求对称正定,返回下三角 L,满足 A = L @ L.T
L = mantissa.cholesky(A)
assert np.allclose(L @ L.T, A)

# Thin SVD:支持矩形矩阵。对 m×n 输入,k = min(m, n)
M = np.random.randn(3, 5)
U, S, Vt = mantissa.svd(M)     # U:(3,3)  S:(3,) 降序  Vt:(3,5)
assert np.allclose(M, U @ np.diag(S) @ Vt)

# Thin QR:Q 列正交,R 上三角,无列主元
Q, R = mantissa.qr(M)          # Q:(3,3)  R:(3,5)
assert np.allclose(M, Q @ R)

SVD 的常见用途------求数值秩:

python 复制代码
U, S, Vt = mantissa.svd(M)
tol = S[0] * max(M.shape) * np.finfo(np.float64).eps
rank = int((S > tol).sum())

矩阵函数:matrix_sqrt / matrix_exp / matrix_log / matrix_sign

四个函数都基于对称矩阵的特征分解实现,结果保持对称:

python 复制代码
A = np.array([[4.0, 0.0], [0.0, 9.0]])

S = mantissa.matrix_sqrt(A)      # 要求半正定;S @ S ≈ A
E = mantissa.matrix_exp(A)       # exp(A),对称
L = mantissa.matrix_log(A)       # 要求严格正定(零特征值会被拒绝)
G = mantissa.matrix_sign(A)      # sign(A);A 非奇异时 G @ G ≈ I
函数 输入要求 备注
matrix_sqrt 对称半正定 有明显负特征值时报 ValueError
matrix_exp 对称 内置溢出保护
matrix_log 对称正定 特征值必须严格 > 0
matrix_sign 对称(惯性任意) 接近零的特征值(< n·eps·max|λ|)映射为 0

解方程:solve

solve(A, B) 求解 A·X = B(内部用 FullPivLU),要求 A 方阵且非奇异:

python 复制代码
A = np.array([[4.0, 1.0], [1.0, 3.0]])

# 向量右端:B shape (n,)
b = np.array([1.0, 2.0])
x = mantissa.solve(A, b)         # ≈ [0.0909, 0.6364]

# 矩阵右端:B shape (n, k),一次解多组
B = np.eye(2)
A_inv = mantissa.solve(A, B)     # 传单位矩阵即得 A⁻¹

1.5 与 SciPy 对比验证

结果与 SciPy/LAPACK 一致(误差通常在 1e-12 量级):

python 复制代码
import scipy.linalg as sla

vals_m, _ = mantissa.eig_sym(A)
vals_s, _ = sla.eigh(A)
assert np.allclose(vals_m, vals_s)

U_m, S_m, Vt_m = mantissa.svd(M)
U_s, S_s, Vt_s = sla.svd(M, full_matrices=False)   # 注意对应 thin SVD
assert np.allclose(S_m, S_s)

1.6 Python 侧常见报错排查

报错 / 现象 原因 解决
ValueError: ... dtype 传入了 float32 / int 数组 A.astype(np.float64)
ValueError: ... square 对称类算子传入了非方阵 检查 shape;矩形矩阵用 svd / qr
ValueError: ... symmetric eig_sym 等传入的矩阵不对称 对称化 (A + A.T) / 2 或改用 eig
RuntimeError 数值计算失败(不收敛、矩阵奇异) 检查条件数;solve 前先确认 A 非奇异
pip install 报 "No matching distribution found" Python < 3.8 或平台不支持 升级 Python;确认 Windows x64 / Linux x86_64 / macOS arm64
修改返回数组后行为异常 返回数组是零拷贝只读语义 修改前 .copy()

第二部分:C++ 版

2.1 前置条件

工具 最低版本 说明
C++ 编译器 C++17 MSVC 2019+ / GCC 9+ / Clang 10+
CMake 3.24 需要 Presets v3 支持
Conan 2.x 包管理器

2.2 获取依赖

Mantissa 通过 GitLab Conan registry 分发,依赖 Eigen 5 与 glog:

bash 复制代码
# 一次性配置
conan remote add gitlab https://gitlab.com/api/v4/projects/86406866/packages/conan
conan remote login gitlab <your_user> -p <your_token>

# 在你的项目目录下安装依赖并生成 CMake toolchain
conan install . -of build --build=missing -s build_type=Release -r gitlab

2.3 集成到你的工程

CMakeLists.txt

cmake 复制代码
cmake_minimum_required(VERSION 3.24)
project(my_app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(mantissa REQUIRED)

add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE mantissa::linalg_cpu)

只需核心设施(Tensor 等)时链接 mantissa::core;用任何 linalg 算子链接 mantissa::linalg_cpu(它会自动带上 core)。

配置与构建

bash 复制代码
cmake --preset conan-default
cmake --build --preset conan-release

运行(Windows):动态库需要能找到 DLL,用 Conan 生成的运行环境脚本:

powershell 复制代码
cmd /c "build\build\generators\conanrun.bat && my_app.exe"

Linux / macOS 对应 source build/build/generators/conanrun.sh

2.4 Tensor 基础

所有算子的输入输出都是 mantissa::Tensor(float64、CPU、行优先)。头文件:<mantissa/core/tensor.hpp>

cpp 复制代码
#include <mantissa/core/tensor.hpp>
#include <iostream>
#include <vector>

// 1) 从 std::vector 构造(按行优先填充)
auto A = mantissa::Tensor::from_vector({4.0, 1.0, 1.0, 3.0}, {2, 2});

// 2) 分配空张量后手动填充
auto T = mantissa::Tensor({2, 3}, mantissa::DType::Float64);
double* p = T.data_ptr<double>();
for (int i = 0; i < T.numel(); ++i) p[i] = i * 1.0;

// 3) 读取元信息
const auto& shape = A.shape();          // shape[0]=2, shape[1]=2
std::cout << A.ndim() << A.numel();     // 2  4

// 4) 遍历元素(行优先连续存储)
const double* data = A.data_ptr<double>();
for (std::int64_t i = 0; i < shape[0]; ++i)
  for (std::int64_t j = 0; j < shape[1]; ++j)
    std::cout << data[i * shape[1] + j] << ' ';

Tensor 还提供零拷贝视图操作(共享底层 Storage,不复制数据):

cpp 复制代码
auto Tt   = T.transpose(0, 1);   // 交换维度,转置后非连续
auto row0 = T.slice(0, 0);       // 固定第 0 维取第 0 行,降一维
auto flat = T.reshape({6});      // 要求连续

注意:视图不延长内存生命周期,源 Tensor 必须比视图活得久。

2.5 调用算子

算子位于 namespace mantissa::linalg,按分类放在不同头文件下:

头文件 算子 返回类型
mantissa/linalg/eigen/eig_sym.hpp eig_sym(A) EigenResult{values, vectors}
mantissa/linalg/eigen/eig.hpp eig(A) EigResult{values_real, values_imag, vectors_real, vectors_imag}
mantissa/linalg/decomposition/cholesky.hpp cholesky(A) Tensor(下三角 L)
mantissa/linalg/decomposition/svd.hpp svd(A) SVDResult{U, S, Vt}
mantissa/linalg/decomposition/qr.hpp qr(A) QRResult{Q, R}
mantissa/linalg/matrix_func/matrix_sqrt.hpp matrix_sqrt(A) Tensor
mantissa/linalg/matrix_func/matrix_exp.hpp matrix_exp(A) Tensor
mantissa/linalg/matrix_func/matrix_log.hpp matrix_log(A) Tensor
mantissa/linalg/matrix_func/matrix_sign.hpp matrix_sign(A) Tensor
mantissa/linalg/solve/solve.hpp solve(A, B) Tensor

完整示例------SVD + 解方程:

cpp 复制代码
#include <mantissa/core/tensor.hpp>
#include <mantissa/linalg/decomposition/svd.hpp>
#include <mantissa/linalg/solve/solve.hpp>
#include <iostream>

int main() {
  // A = [[4, 1], [1, 3]]
  auto A = mantissa::Tensor::from_vector({4.0, 1.0, 1.0, 3.0}, {2, 2});

  // SVD(thin,支持矩形矩阵)
  auto [U, S, Vt] = [] {
    auto r = mantissa::linalg::svd(A);
    return std::make_tuple(std::move(r.U), std::move(r.S), std::move(r.Vt));
  }();
  // 或直接访问成员:auto result = mantissa::linalg::svd(A); result.U ...

  const double* s = S.data_ptr<double>();
  std::cout << "singular values: " << s[0] << ", " << s[1] << "\n";

  // 解 A·x = b
  auto b = mantissa::Tensor::from_vector({1.0, 2.0}, {2});
  auto x = mantissa::linalg::solve(A, b);

  const double* xp = x.data_ptr<double>();
  std::cout << "x = [" << xp[0] << ", " << xp[1] << "]\n";
  return 0;
}

对称特征分解:

cpp 复制代码
#include <mantissa/linalg/eigen/eig_sym.hpp>

auto result = mantissa::linalg::eig_sym(A);
// result.values  --- shape {n},升序
// result.vectors --- shape {n,n},第 j 列对应 values[j]
const double* w = result.values.data_ptr<double>();

一般(非对称)特征分解返回实部/虚部四个 Tensor(与 Python 侧的 4 元组一致):

cpp 复制代码
#include <mantissa/linalg/eigen/eig.hpp>

auto r = mantissa::linalg::eig(A);
// r.values_real, r.values_imag, r.vectors_real, r.vectors_imag

2.6 错误处理约定

C++ 侧与 Python 侧一一对应:

C++ 异常 触发条件
std::invalid_argument shape / dtype / 对称性不合法(对应 Python ValueError
std::runtime_error 数值计算失败,如不收敛、矩阵奇异(对应 Python RuntimeError
cpp 复制代码
try {
  auto L = mantissa::linalg::cholesky(A);   // A 非正定时抛异常
} catch (const std::invalid_argument& e) {
  // 输入不合法
} catch (const std::runtime_error& e) {
  // 数值失败
}

2.7 从源码构建(可选)

如果你需要修改库本身,或不走 Conan 分发:

bash 复制代码
git clone <repo-url> && cd Mantissa
conan install . -of build --build=missing -s build_type=Release -r gitlab
cmake --preset conan-default
cmake --build --preset conan-release

产物为 mantissa_coremantissa_linalg_cpu 两个动态库。运行测试(96 用例):

powershell 复制代码
# Windows --- 必须先注入 DLL 路径,否则报 0xc0000135
cmd /c "build\build\generators\conanrun.bat && ctest --preset conan-release --output-on-failure"

常用 CMake 选项:MANTISSA_BUILD_TESTS(默认 ON)、MANTISSA_BUILD_PYTHON(默认 OFF)、MANTISSA_ENABLE_NATIVE-march=native / /arch:AVX2,默认 OFF)。


第三部分:Python ↔ C++ 对照

同一套算法的两侧调用对照,方便在两种语言间迁移代码:

操作 Python C++
构造矩阵 np.array([[4., 1.], [1., 3.]]) Tensor::from_vector({4., 1., 1., 3.}, {2, 2})
对称特征分解 values, vectors = eig_sym(A) auto r = eig_sym(A); r.values; r.vectors
一般特征分解 vr, vi, Sr, Si = eig(A) auto r = eig(A); r.values_real; ...
Cholesky L = cholesky(A) auto L = cholesky(A);
SVD U, S, Vt = svd(A) auto r = svd(A); r.U; r.S; r.Vt
QR Q, R = qr(A) auto r = qr(A); r.Q; r.R
矩阵函数 matrix_sqrt/exp/log/sign(A) 同名函数,返回 Tensor
解方程 x = solve(A, b) auto x = solve(A, b);
输入不合法 ValueError std::invalid_argument
数值失败 RuntimeError std::runtime_error

两侧行为完全一致的部分:

  • 特征值升序(eig_sym)、奇异值降序(svd
  • 特征向量按存放
  • thin SVD / thin QR,支持矩形矩阵,k = min(m, n)
  • 仅 float64、仅 CPU、行优先存储
  • 显式异常,无静默回退

常见问题(FAQ)

Q:Python 和 C++ 结果会有差异吗?

不会。Python 绑定只是薄封装,底层调用同一份 C++ 实现;与 SciPy/LAPACK 的对照误差也在 1e-12 量级。

Q:什么时候选 Python,什么时候选 C++?

交互式分析、与 NumPy/SciPy 生态混用 → Python;对延迟敏感、已有 C++ 工程、需要精细控制内存 → C++。Python 侧的零拷贝绑定意味着即便走 Python,计算也是纯 C++ 速度。

Q:支持 float32 / GPU 吗?

当前仅 float64 / CPU。float32 与 CUDA 后端在路线图上(v0.3)。

Q:Windows 下运行报 DLL 加载失败(0xc0000135)?

动态库不在搜索路径。用 Conan 生成的环境脚本包一层:cmd /c "build\build\generators\conanrun.bat && <你的程序>"

Q:import mantissa 报 ModuleNotFoundError,但我装过包了?

确认安装的是 mantissa-cpppip show mantissa-cpp),且当前解释器与安装环境一致(虚拟环境)。


延伸资料

gitlab地址

PyPi链接

相关推荐
童园管理札记1 小时前
从政策驱动到课堂落地:2026年“人工智能+教育”全景解读与技术实践指南
人工智能·python·深度学习·职场和发展·学习方法
陈年老古董1 小时前
微博情感分析项目学习笔记
笔记·python·学习·机器学习·项目
10mAh1 小时前
【Python】从零开始做一个重复文件查找工具——找出改名副本,生成核对报告
python
用户0332126663671 小时前
使用 Python 轻松合并多个 PowerPoint 文件【代码详解】
python
Lazionr1 小时前
多态:从多种形态到运行时绑定
开发语言·c++
Xiu Yan1 小时前
Python 数据分析:Pandas DataFrame 零基础入门教程
python·jupyter·pycharm·numpy·pandas
别动我齐刘海2 小时前
ROS2 Jazzy + C++ 实战路线——ros2_control
c++·人工智能·python·opencv·机器学习·机器人·github
All for pursuit.2 小时前
【栈-4】739.每日温度
数据结构·c++·算法·leetcode
把头塞进显示器2 小时前
电商平台-测试报告
python·selenium·pytest