lieplusplus_py
Swarm Systems Lab Python lieplusplus library
⚠️ Warning: This project is a work in progress.
Installation
pip install lieplusplus_py
Python Lie++ API Documentation
SO(3) - 3D Rotations
import numpy as np
from lieplusplus import SO3
# --- Constructors ---
R = SO3() # Identity rotation
R = SO3([1, 0, 0, 0]) # Quaternion [w, x, y, z]
R = SO3(np.eye(3)) # Rotation matrix (3×3)
R = SO3([0.1, 0.2, 0.3]) # Rotation vector (exp map)
R = SO3([1, 0, 0], [0, 1, 0]) # Rotation sending u -> v
R = SO3.random() # Random rotation
# --- Group operations ---
R3 = R1 * R2 # Compose rotations
v_rot = R * v # Rotate a 3-vector
M_rot = R * M # Rotate a 3×3 matrix
# --- Lie-theoretic operations (static) ---
u = SO3.log(R) # R^3 log-coordinate
R2 = SO3.exp(u) # Exponential map
U = SO3.wedge(u) # 3×3 skew-symmetric matrix
u2 = SO3.vee(U) # Vector from skew matrix
u0 = SO3.tangent_zero() # Zero element of so(3)
# --- Jacobians ---
J = SO3.leftJacobian(u)
Jinv = SO3.invLeftJacobian(u)
# --- Accessors ---
R.asMatrix() # 3×3 rotation matrix
R.Adjoint() # 3×3 Adjoint matrix
R.inv() # Inverse rotation
R.q() # Quaternion [w, x, y, z]
SE(3) - Poses
import numpy as np
from lieplusplus import SE3, SO3
# --- Constructors ---
T = SE3() # Identity pose
T = SE3(R, t) # SO(3) + translation vector
T = SE3(np.eye(4)) # Full 4×4 pose matrix
T = SE3([0.1, 0.2, 0.3], [1, 2, 3]) # Rotation vector + translation
T = SE3.random() # Random pose
# --- Group operations ---
T3 = T1 * T2 # Compose poses
p_world = T * p # Transform a 3D point
# --- Lie-theoretic operations (static) ---
u = SE3.log(T) # R^6 tangent vector
T2 = SE3.exp(u)
U = SE3.wedge(u)
u2 = SE3.vee(U)
u0 = SE3.tangent_zero()
# --- Accessors ---
T.asMatrix() # 4×4 transformation matrix
T.Adjoint() # 6×6 Adjoint matrix
T.inv() # Inverse pose
T.R() # Rotation (SO3)
T.q() # Quaternion
T.translation() # 3D translation vector
SE_2(3) - Extended Poses
import numpy as np
from lieplusplus import SE3_2, SO3
# --- Constructors ---
X = SE3_2() # Identity extended pose
X = SE3_2(R, [v, p]) # Rotation + [velocity, position]
X = SE3_2(R, [np.zeros(3), np.ones(3)]) # Example
X = SE3_2.random() # Random extended pose
# --- Group operations ---
X3 = X1 * X2
# --- Lie-theoretic operations (static) ---
u = SE3_2.log(X) # R^9 tangent vector
X2 = SE3_2.exp(u)
U = SE3_2.wedge(u)
u2 = SE3_2.vee(U)
u0 = SE3_2.tangent_zero()
# --- Accessors ---
X.asMatrix() # 5×5 extended matrix
X.Adjoint() # 9×9 Adjoint matrix
X.inv() # Inverse extended pose
X.R() # Rotation (SO3)
X.v() # Velocity vector
X.p() # Position vector
Vectorized operators (batch)
The group classes above are single-element by design: the algebra of one robot rarely needs
vectorization. Calling them from Python in a loop is dominated not by the arithmetic (tens of
nanoseconds) but by the pybind boundary crossing (~1-1.5 us per call), which a loop over N
entities pays N times.
lieplusplus._core.batch runs the same kernels, crossing once per array:
import numpy as np
from lieplusplus._core import batch as B
xi = np.random.normal(size=(1000, 3)) * 0.01
R = B.so3_exp(xi) # (1000, 3, 3)
R = B.so3_retract(R, xi) # R * exp(xi), the integration primitive
Measured on SO(3) retraction: ~3 us/entity for a Python loop vs ~0.04 us/entity batched (~68x at N=1000), and faster than an equivalent vectorized NumPy implementation, since the C++ loop materializes no temporaries.
What the batch API gives you
B.so3_exp(v) # (3,) -> (3, 3) shape polymorphic:
B.so3_exp(v_stack) # (N, 3) -> (N, 3, 3) one element or a stack
B.so3_retract(one_R, tangents) # (3,3) x (N,3) -> (N,3,3) leading axis broadcasts N vs 1
B.so3_retract(R, tangents, out=R) # writes in place, allocating nothing
B.so3_exp(fortran_or_float32_or_list) # any layout/dtype converts automatically
Shape errors name the operator and the argument:
so3_exp: argument 'x' must have shape (3) or (N, 3), got (4, 5)
so3_retract: leading dimensions do not broadcast, got 4 and 3
Available operators
| SO(3) | shapes |
|---|---|
so3_exp / so3_log |
(N,3) -> (N,3,3) / (N,3,3) -> (N,3) |
so3_inv |
(N,3,3) -> (N,3,3) |
so3_compose |
(N,3,3) x (N,3,3) -> (N,3,3) |
so3_retract |
(N,3,3) x (N,3) -> (N,3,3) |
so3_rotate |
(N,3,3) x (N,3) -> (N,3) |
so3_right_jacobian / so3_inv_right_jacobian |
(N,3) -> (N,3,3) |
| SE(3) | shapes |
|---|---|
se3_exp / se3_log |
(N,6) -> (N,4,4) / (N,4,4) -> (N,6) |
se3_inv |
(N,4,4) -> (N,4,4) |
se3_compose |
(N,4,4) x (N,4,4) -> (N,4,4) |
se3_retract |
(N,4,4) x (N,6) -> (N,4,4) |
se3_transform |
(N,4,4) x (N,3) -> (N,3) |
Vectorizing a new operator
The machinery lives in src/pybind_batch.hpp and is library-agnostic - it only assumes
fixed-size Eigen types in and out, so it can be dropped into any pybind11 project. Adding a
vectorized operator is one line; shape checking, broadcasting, out=, layout conversion and error
messages come for free:
namespace pb = pybind_batch;
pb::def_unary<Vec3d, Matrix3d>(m, "so3_exp",
[](const Vec3d& u) { return SO3d::exp(u).asMatrix(); }, "Exponential map.");
pb::def_binary<Matrix3d, Vec3d, Matrix3d>(m, "so3_retract",
[](const Matrix3d& R, const Vec3d& u) { return (SO3d(R) * SO3d::exp(u)).asMatrix(); },
"Right retraction R*exp(u).");
The element types determine the array shapes: Vec3d means (N, 3), Matrix4d means (N, 4, 4).
Note.
batchis a separate namespace from the scalar API on purpose.SO3.expalready accepts(3,)and(3,1)column vectors, so merging the two would make(3,1)ambiguous with a batch of 3. Keeping them apart letsbatchdispatch unambiguously onndim.
License
MIT
Metadata
Release files for lieplusplus-py 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lieplusplus_py-0.6.0.tar.gz | 87.0 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lieplusplus_py-0.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.14 | CPython 3.14 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| lieplusplus_py-0.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| lieplusplus_py-0.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
Total release size: 6.3 MB
Release files / lieplusplus_py-0.6.0.tar.gz
| Download URL | lieplusplus_py-0.6.0.tar.gz |
|---|---|
| Size | 87.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b28574ad202e06bbfef297a817ab6fb6887adf83ee9ba246bc6fb556b171163c
|
|
BLAKE2b-256 checksum How to use checksums |
015e21e2ef7292b7f1595981ce85d92ff014f982cbcf5377a9960dc439fd81fd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|
Release files / lieplusplus_py-0.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | lieplusplus_py-0.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.14 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
1612ee40f8dfe5df50c18e5710d08c71c304d0daad06e91304983a56fc40f634
|
|
BLAKE2b-256 checksum How to use checksums |
16e2a55f905dcce261d2ef3c66e2e2d272b8bbc7d4e375493b9d9a62d5646092
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|
Release files / lieplusplus_py-0.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | lieplusplus_py-0.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.13 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
296f1a946f1f4a6440347efec629f77a122d58650fd27eb5c9d506945d46c843
|
|
BLAKE2b-256 checksum How to use checksums |
17e3cc884ff7991dd515a48a042f0f96ff54f37f960343d104c4fbe71548d142
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|
Release files / lieplusplus_py-0.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | lieplusplus_py-0.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
ebcd06786f1c757a9d527b405f0f6cec28b850ebd04bc78f55199d9894bf854a
|
|
BLAKE2b-256 checksum How to use checksums |
4333360d601038705cde01730e9f2b17b283f73d0d3b0a449e94bec200b37a41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|