Repository navigation
Numerics
extensions/numerics/ solves the linear algebra of engineering analysis in Float64. It gives dense and sparse matrices, a conjugate-gradient solver, a skyline direct solver and a generalized eigensolver. The structural and thermal solvers of the building extension share it.

| Module | What it gives |
|---|---|
vector |
zeros, dot, norm, axpy and size and finiteness checks on List[Float64]
|
dense |
DenseMatrix, Gaussian elimination, Cholesky, triangular solves, the Jacobi eigensolver and the Thomas algorithm |
sparse |
SparseBuilder, which assembles triplets, and CsrMatrix
|
iterative |
conjugate_gradient with a Jacobi preconditioner, and SolveStatus
|
skyline |
reverse_cuthill_mckee, profile and SkylineFactor
|
eigen |
lowest_modes and count_eigenvalues_below
|
Create a SparseBuilder with the matrix size. Call add once for each contribution. Repeated positions add together. A zero is skipped. build returns a CsrMatrix with sorted, distinct columns in each row.
var builder = SparseBuilder(3)
builder.add(0, 0, 2)
builder.add(0, 1, -1)
builder.add(1, 0, -1)
builder.add(0, 0, 1) # adds to the first entry: 3
var k = builder.build()
var y = k.multiply(x)add refuses an index out of range and a value that is not finite. Every matrix is square.
Use SkylineFactor for a direct solve. Use conjugate_gradient for a large, well-conditioned system that does not fit a profile.
| Solver | Needs | Gives |
|---|---|---|
SkylineFactor(a, order) |
A symmetric matrix with no zero pivot | A factor that solves any right-hand side. It also counts negative pivots. |
conjugate_gradient(a, b, tolerance, max_iterations) |
A symmetric positive-definite matrix | One solution, the iteration count, the relative residual and a SolveStatus
|
solve(a, b) |
A small dense square matrix | One solution, by Gaussian elimination with partial pivoting |
solve_tridiagonal(lower, diagonal, upper, rhs) |
A diagonally dominant tridiagonal system | One solution, by the Thomas algorithm |
var factor = SkylineFactor(k, reverse_cuthill_mckee(k))
var u = factor.solve(f)Pass reverse_cuthill_mckee(k) as the order to make the profile small. Pass identity_order(k.size) to keep the numbering. profile(k, order) gives the number of stored entries for an order. The profile and the factor read the same upper entries after reordering. An upper-only matrix must supply those entries in the selected order.
conjugate_gradient stops at CONVERGED or at BUDGET_EXHAUSTED. An exhausted budget is not an error. The result holds the last iterate, and the caller decides what to do with it.
lowest_modes(k, m, count, tolerance, max_iterations) returns the lowest eigenvalues of K φ = λ M φ, in ascending order. Each vector is normalized so that φᵀ M φ = 1. For a structure, λ is the square of the angular frequency.
var modes = lowest_modes(stiffness, mass, 3, 1e-10, 60)
var hertz = sqrt(modes.values[0]) / (2 * pi)The method is subspace iteration. It carries min(n, max(2 count, count + 8)) trial vectors. Each iteration makes them M-orthonormal by modified Gram-Schmidt. A vector that collapses into the span of the others is replaced by a unit vector.
Replacement tries each coordinate at most once per trial vector. If no independent positive-mass direction is found, the method raises. A mass a million times the rest still converges.
After convergence, a Sturm check counts the negative eigenvalues of K - σ M. With a resolved gap after the last wanted eigenvalue, that count must equal count. A request can also end inside a repeated cluster. The check then counts on both sides of the cluster. The lower count must equal the number of lower returned modes. The upper count must cover the requested modes.
Inertia identifies the cluster even when an unwanted Ritz vector has not yet converged to it. This retains the check for missed lower modes. Cluster comparisons use the dense solver's working precision, independent of the requested iteration tolerance.
count_eigenvalues_below(k, m, shift) gives that count for any finite shift. It first tries the skyline factor and checks that its multipliers are safe. If a pivot is zero or unsafe, it scales rows and columns together. It then uses symmetric pivoting with 1 by 1 and 2 by 2 blocks. This congruence preserves inertia and the pivot tolerance.
A nonsingular indefinite shift can have a zero leading entry. The pivoted path handles this case. A shift at an eigenvalue to working precision still raises.
The pivoted fallback uses dense storage. Its storage grows with the square of the matrix size, and its work grows with the cube. The ordinary skyline path keeps its profile storage.
symmetric_eigen scales finite input by its largest entry before the Jacobi sweeps. This preserves the relative stopping test when the original entry squares would overflow or underflow. Eigenvectors stay unchanged by this scaling. The method rescales each eigenvalue at the end and raises if it cannot represent that value as finite Float64.
Every function raises Error for input it cannot use. The table gives the cases.
| Case | Functions |
|---|---|
| Sizes differ | All |
| An entry is not finite |
SparseBuilder.add, conjugate_gradient, symmetric_eigen
|
| A matrix is singular |
solve, SkylineFactor
|
| A matrix is not positive definite |
cholesky, conjugate_gradient, lowest_modes
|
| An iteration does not converge |
symmetric_eigen, lowest_modes
|
| An order is not a permutation |
SkylineFactor, profile
|
- The matrices are square and real.
-
SkylineFactorreads the upper triangle after reordering. It treats the matrix as symmetric. -
SkylineFactordoes not pivot. A symmetric indefinite matrix factors only when no pivot is zero. -
lowest_modesneeds a positive-definite stiffness. Apply the supports first. A free structure has zero eigenvalues and is refused. -
symmetric_eigenis for small matrices. Its cost grows with the cube of the size.
- Golub and Van Loan, "Matrix Computations", 4th edition, 2013.
- Hestenes and Stiefel, "Methods of conjugate gradients for solving linear systems", 1952.
- Cuthill and McKee, "Reducing the bandwidth of sparse symmetric matrices", 1969.
- George and Liu, "An implementation of a pseudoperipheral node finder", 1979.
- Bathe, "Finite Element Procedures", 2nd edition, 2014: sections 8.2 and 11.6.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Streamline
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang