Matrices
Run Symbolica in your browser
Python examples with a Run button can be edited and run here. The first run loads Python, Symbolica, hover documentation, and code completion.
The Matrix data structure allows users to do linear algebra operations in Symbolica.
Construction
Constructors validate matrix shapes: nested rows must have equal lengths, and linear data must have exactly rows * columns entries. Matrices can be created through one of the following constructors:
from symbolica import *
i = Matrix.identity(2)
e = Matrix.eye([1, 2, 3])
l = Matrix.from_linear(3, 2, [1, 2, 3, 4, 5, 6])
n = Matrix.from_nested([[1, 2, 3], [4, 5, 6]])
print("i =", i)
print("e =", e)
print("l =", l)
nuse symbolica::prelude::*;
fn main() {
let i = Matrix::identity(2, IntegerRing::new());
let e = Matrix::eye(&[1.into(), 2.into(), 3.into()], IntegerRing::new());
let l = Matrix::from_linear(
vec![
1u64.into(),
2u64.into(),
3u64.into(),
4u64.into(),
5u64.into(),
6u64.into(),
],
3,
2,
IntegerRing::new(),
)
.unwrap();
let n = Matrix::from_nested_vec(
vec![
vec![1.into(), 2.into(), 3.into()],
vec![4.into(), 5.into(), 6.into()],
],
IntegerRing::new(),
)
.unwrap();
}Output
i = {{1,0},{0,1}}
e = {{1,0,0},{0,2,0},{0,0,3}}
l = {{1,2},{3,4},{5,6}}
n = {{1,2,3},{4,5,6}}
Arithmetic
Matrices can be multiplied, added and subtracted using overloaded operators.
from symbolica import *
l = Matrix.from_linear(3, 2, [1, 2, 3, 4, 5, 6])
n = Matrix.from_nested([[1, 2, 3], [4, 5, 6]])
l * n + Matrix.identity(3)use symbolica::prelude::*;
fn main() {
let l = Matrix::from_linear(vec![1.into(), 2.into(), 3.into(), 4.into(), 5.into(), 6.into()], 3, 2, Z).unwrap();
let n = Matrix::from_nested_vec(vec![vec![1.into(), 2.into(), 3.into()], vec![4.into(), 5.into(), 6.into()]], Z).unwrap();
let c = &(&l * &n) + &Matrix::identity(3, Z);
println!("{c}");
}Output
{{10,12,15},{19,27,33},{29,40,52}}
In Rust, the type of argument of the matrix can be selected, e.g. Matrix<Q>. In Python, the default is a matrix over rational polynomials, i.e. Matrix<RationalPolynomial<Integer, u16>> in Rust.
Common operations
For matrices over fields, determinants and inverses can be computed. Inverting a singular matrix raises an error in Python and returns an error in Rust.
from symbolica import *
t = Expression.symbol('t')
a = Matrix.from_nested([[t, 2], [3, 4]])
print('a =', a)
print('a^-1 =', a.inv())
print('det(a) =', a.det())Here we specialize t = 1 and use rational coefficients.
use symbolica::prelude::*;
fn main() {
let a: Matrix<Q> = Matrix::from_nested_vec(
vec![vec![1.into(), 2.into()], vec![3.into(), 4.into()]], Q,
).unwrap();
println!("a = {}", a);
println!("a^-1 = {}", a.inv().unwrap());
println!("det(a) = {}", a.det().unwrap());
}Output
a = {{t,2},{3,4}}
a^-1 = {{2/(-3+2*t),-1/(-3+2*t)},{-3/(-6+4*t),t/(-6+4*t)}}
det(a) = -6+4*t
The matrix equation \[\mathbf{A} \cdot \vec{x} = \vec{b}\] can be solved as well:
from symbolica import *
t = Expression.symbol('t')
a = Matrix.from_nested([[t, 2], [3, 4]])
a.solve(Matrix.vec([4, 3]))Here we specialize t = 1 and use rational coefficients.
use symbolica::prelude::*;
fn main() {
let a: Matrix<Q> = Matrix::from_nested_vec(
vec![vec![1.into(), 2.into()], vec![3.into(), 4.into()]], Q,
).unwrap();
let b = Matrix::from_nested_vec(vec![vec![4.into()], vec![3.into()]], Q).unwrap();
let r = a.solve(&b).unwrap();
println!("{} . x = {} => x = {}", a, b, r);
}Output
{{5/(-3+2*t)},{(-12+3*t)/(-6+4*t)}}
Matrices can also be indexed using a (row, column) tuple, which yields the entry.