Very elegant. Very Lumpy.
Project description
Lumpy
Very elegant. Very Lumpy.
Lumpy is a tiny semantic linear algebra wrapper over NumPy.
NumPy is excellent, but its core abstraction is the n-dimensional array, not the mathematical vector or matrix. Lumpy uses NumPy underneath while enforcing a more linear-algebraic convention:
- vectors are column vectors
- matrices are built from columns
- rows are transposes or row slices
- projections are onto column spaces
- subspace tools return bases as columns
Installation
pip install lumpy-la
Basic usage
import lumpy as la
Vectors
v = la.vec(1, 2, 3)
v.shape
# (3, 1)
Vectors are always column vectors.
$$ \mathbf{v}\in \mathbb{R}^{n\times 1} $$
v.T
# array([[1., 2., 3.]])
$$ \mathbf{v}^{T}\in \mathbb{R}^{1\times n} $$
Matrices are built from columns
A = la.mat(
[1, 2, 3],
[4, 5, 6]
)
A
# array([[1., 4.],
# [2., 5.],
# [3., 6.]])
You can also pass existing column vectors:
u = la.vec(1, 2, 3)
v = la.vec(4, 5, 6)
A = la.mat(u, v)
Matrices from rows
mat() constructs matrices from columns. Sometimes, though, it is useful to type a matrix in the same visual layout used on paper.
A = la.matt(
[1, 2, 3],
[4, 5, 6]
)
A
# array([[1., 2., 3.],
# [4., 5., 6.]])
matt() preserves the usual visual layout of matrices, while mat() preserves Lumpy’s column-oriented semantics.
Standard basis vectors
Lumpy uses Python-style zero-indexing.
la.e(3, 0)
# array([[1.],
# [0.],
# [0.]])
So la.e(n, i) corresponds to the mathematical basis vector $\mathbf{e}_{i+1}\in \mathbb{R}^{n}$ .
Identity
la.eye(3)
# array([[1., 0., 0.],
# [0., 1., 0.],
# [0., 0., 1.]])
la.I is the same function — a paper-like alias for eye. It is reachable as la.I, but deliberately not exported by from lumpy import *, so the single-letter name I is never bound in your namespace unless you ask for it.
Basic operations
u = la.vec(1, 2, 3)
v = la.vec(4, 5, 6)
la.inner(u, v)
# array([[32.]])
la.dot(u, v)
# 32.0
la.norm(v)
# 8.774964387392123
inner preserves matrix structure.
$$ \langle \mathbf{u},\mathbf{v} \rangle = [\mathbf{u}\cdot \mathbf{v}]\in \mathbb{C}^{1\times 1} $$
dot returns a scalar.
$$ \mathbf{u} \cdot \mathbf{v} \in \mathbb{C} $$
For real input the result is real, since $\mathbb{R}\subset\mathbb{C}$; over $\mathbb{C}$ both use the conjugate transpose (the Hermitian inner product).
Unit vectors and normalization
v = la.vec(3, 4)
la.unit(v)
# array([[0.6],
# [0.8]])
For matrices, normalize normalizes each column:
A = la.mat(
[3, 4],
[5, 12]
)
la.normalize(A)
# array([[0.6 , 0.38461538],
# [0.8 , 0.92307692]])
Columns and rows
A = la.mat(
[1, 2, 3],
[4, 5, 6]
)
la.col(A, 0)
# array([[1.],
# [2.],
# [3.]])
la.row(A, 1)
# array([[2., 5.]])
col preserves column-vector shape.
Projection
Project onto the column space of a matrix:
A = la.mat(
[1, 0, 0],
[0, 1, 0]
)
v = la.vec(3, 4, 5)
la.proj(A, v)
# array([[3.],
# [4.],
# [0.]])
This projects $\mathbf{v}$ onto the $xy$-plane.
Vector projection is just the rank-one case:
u = la.vec(1, 1, 0)
v = la.vec(2, 3, 4)
la.proj(u, v)
Subspaces
A = la.mat(
[1, 2],
[2, 4]
)
la.rank(A)
# 1
la.independent(A)
# False
An orthonormal basis for the column space:
Q = la.orth(A)
Q.T @ Q
# approximately identity
Nullspace basis:
N = la.null(A)
A @ N
# approximately zero
Lumpy returns subspace bases as columns.
A = la.matt(
[1, 2, 3],
[2, 4, 6]
)
la.row_space(A)
The left nullspace is the nullspace of $A^{T}$:
$$ \mathrm{Null}\left(A^{T}\right) $$
la.left_null(A)
SVD
By default, Lumpy's SVD returns only rank-relevant singular directions:
A = la.mat(
[1, 0],
[2, 0]
)
U, s, Vt = la.svd(A)
For full ambient bases:
U, s, Vt = la.svd(A, full_matrices=True)
Many Lumpy subspace functions (rank, orth, null, proj, svd, …) need to decide which singular values count as nonzero. By default this uses a relative, scale-invariant tolerance — a singular value is treated as zero when it falls below max(s) * eps * max(m, n) (the same rule as numpy.linalg.matrix_rank). Because both the singular values and the cutoff scale with A, the rank decision is unaffected by rescaling:
la.rank(A) == la.rank(1e8 * A) # True
Pass an explicit number as tol for an absolute cutoff instead:
la.rank(A, tol=1e-9) # absolute threshold
Geometry
u = la.vec(1, 0)
v = la.vec(0, 1)
la.angle(u, v)
# 1.5707963267948966
la.dist(u, v)
# 1.4142135623730951
Solving systems
Prefer solve() over explicitly computing an inverse.
A = la.matt(
[2, 0],
[0, 3]
)
b = la.vec(4, 9)
la.solve(A, b)
# array([[2.],
# [3.]])
For overdetermined systems, use least squares:
A = la.matt(
[1, 1],
[1, 2],
[1, 3]
)
b = la.vec(1, 2, 2)
la.lstsq(A, b)
Complex vectors and matrices
Lumpy is correct over both ℝ and ℂ. inner, dot, outer, proj, null, row_space, and left_null use the conjugate transpose (adj), which reduces to the ordinary transpose for real input — so real code is unchanged while complex code does the right thing (e.g. dot(u, u) is real and nonnegative).
Constructors auto-upcast: la.vec(1, 2, 3) is float64, la.vec(1+2j, 3) is complex128. Pass dtype=None to defer to NumPy's inference, or an explicit dtype to force one.
Because ⟨u, v⟩ is complex, angle takes a kind:
u = la.vec(1 + 1j, 2)
v = la.vec(1j, 1)
la.angle(u, v) # "re": arccos(Re⟨u,v⟩/(‖u‖‖v‖)), range [0, π]
la.angle(u, v, kind="hermitian") # angle between the complex lines, range [0, π/2]
la.angle(u, v, kind="branch") # "re" for real input, "hermitian" for complex
"re" is the default: it equals the ordinary angle for real input and depends only on the vectors' values, never on whether they are stored as real or complex.
Running tests
python -m pytest
Design principle
Lumpy is not trying to replace NumPy.
It is a small semantic layer for doing linear algebra with conventions that feel closer to the math.
import numpy as np
import lumpy as la
Use NumPy for general arrays.
Use Lumpy when you want vectors, matrices, projections, and subspaces to behave like linear algebra objects.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lumpy_la-1.0.0.tar.gz.
File metadata
- Download URL: lumpy_la-1.0.0.tar.gz
- Upload date:
- Size: 14.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58ea240bad25db620459bd0606e4ac82e4d97ca50331f9e365a029386f1ba2c7
|
|
| MD5 |
0689057b151912129ab17616fe7b577b
|
|
| BLAKE2b-256 |
867348ebfc04559f9fb7c4860ed8e0766a8e1d32b6af4f0ad648993c352df3cc
|
File details
Details for the file lumpy_la-1.0.0-py3-none-any.whl.
File metadata
- Download URL: lumpy_la-1.0.0-py3-none-any.whl
- Upload date:
- Size: 11.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e4f098d7907d8f8883092864f3a07927596c15385398176e1800e4d337dc5d4
|
|
| MD5 |
95631019f96fb5ecfaf49b3778bf6b2b
|
|
| BLAKE2b-256 |
673e3d344495136a1d895dce2741bf0143e12f162d32a9f72ae0589d11ad525d
|