Linear Algebra
Giac.jl provides comprehensive symbolic linear algebra capabilities including matrix operations, determinants, eigenvalues, and linear system solving.
Setup
using Giac, LinearAlgebra
using Giac.Commands: eigenvalues, linsolve
@giac_var a b c d x yMatrix Creation
Numeric Matrices
Create matrices from Julia arrays:
A = GiacMatrix([1 2; 3 4])
# 2×2 GiacMatrix:
# 1 2
# 3 4Symbolic Matrices
Create matrices with symbolic entries:
@giac_var a b c d
B = GiacMatrix([[a, b],
[c, d]])
# 2×2 GiacMatrix:
# a b
# c dSymbol Constructor
Create large symbolic matrices using the symbol constructor:
@giac_several_vars m 2 2
M = GiacMatrix([[m11, m12],
[m21, m22]])
det(M) # m11*m22-m12*m21
# Or use the compact constructor:
M = GiacMatrix(:m, 3, 3)
# 3×3 GiacMatrix with entries m11, m12, ...For very large matrices:
M = GiacMatrix(:m, 100, 100)
# 100×100 GiacMatrix:
# m_1_1 m_1_2 m_1_3 ... m_1_100
# m_2_1 m_2_2 m_2_3 ... m_2_100
# ⋮ ⋮ ⋮ ⋱ ⋮
# m_100_1 m_100_2 m_100_3 ... m_100_100Custom Index Ranges
Create matrices with non-1-based indexing using UnitRange or StepRange arguments:
# 0-based indexing (useful for physics, quantum mechanics)
M = GiacMatrix(:ψ, 0:2, 0:2)
# 3×3 GiacMatrix with entries ψ00, ψ01, ψ02, ψ10, ψ11, ...
# Negative indices (useful for angular momentum states)
J = GiacMatrix(:J, -1:1)
# 3×1 column vector with entries J_m1, J_0, J_1 (m = minus)
# Custom ranges
A = GiacMatrix(:A, 5:7, 1:3)
# 3×3 GiacMatrix with entries A51, A52, A53, A61, ...
# StepRange for even indices
E = GiacMatrix(:E, 0:2:6)
# 4×1 column vector with entries E0, E2, E4, E6
# Mixed integer and range arguments
M = GiacMatrix(:M, 3, 0:2)
# 3×3 matrix: rows use 1:3, columns use 0:2
# Entries: M10, M11, M12, M20, M21, M22, M30, M31, M32The same range syntax works with @giac_several_vars:
@giac_several_vars ψ 0:2
# Creates: ψ0, ψ1, ψ2
@giac_several_vars T 0:1 0:2
# Creates: T00, T01, T02, T10, T11, T12
@giac_several_vars c -1:1
# Creates: c_m1, c_0, c_1 (m = minus for negative indices)Naming Convention:
- Indices 0-9: concatenated directly (e.g.,
m12,ψ00) - Indices > 9: underscore separators (e.g.,
m_1_10) - Negative indices:
mprefix for minus (e.g.,-1→m1, soc_m1for index -1)
Iteration and Linear Indexing
GiacMatrix supports the standard Julia iteration and linear-indexing protocols. Iteration order is column-major, matching Julia's Matrix.
length and basic iteration
@giac_var x y
M = GiacMatrix([x y x+y;
2*x 2*y x*y]) # 2×3
length(M) # 6 (= rows * cols)
for e in M
println(e) # x, 2*x, y, 2*y, x+y, x*y (column-major)
end
collect(M) # 6-element VectorLinear indexing M[i] and Cartesian-index access
@giac_var x y
M = GiacMatrix([x y x+y;
2*x 2*y x*y])
M[1] # x (= M[1, 1])
M[3] # y (= M[1, 2])
M[5] # x + y (= M[1, 3])
M[CartesianIndex(2, 3)] # x*y (same as M[2, 3])LinearIndices(M) and CartesianIndices(M)
LI = LinearIndices(M)
LI[1, 1], LI[2, 1], LI[1, 2] # 1, 2, 3 (column-major)
CI = CartesianIndices(M)
CI[1], CI[3], CI[5]
# CartesianIndex(1,1), CartesianIndex(1,2), CartesianIndex(1,3)These are convenient for routines that take a GiacMatrix and want to walk its entries with a linear counter, or convert between the two index forms:
LI = LinearIndices(M)
for i in 1:M.rows, j in 1:M.cols
@assert M[LI[i, j]] === M[i, j] || string(M[LI[i, j]]) == string(M[i, j])
endDeterminant
Compute the determinant using det:
A = GiacMatrix([1 2; 3 4])
det(A)
# Output: -2
# Symbolic determinant
@giac_var a b c d
B = GiacMatrix([[a, b], [c, d]])
det(B)
# Output: a*d-b*cInverse
Compute the matrix inverse using inv:
A = GiacMatrix([1 2; 3 4])
inv(A)
# Returns the inverse matrixFor singular matrices, GIAC will return an error.
Trace
Compute the trace (sum of diagonal elements) using tr:
A = GiacMatrix([1 2; 3 4])
tr(A)
# Output: 5Transpose
Compute the transpose using transpose:
A = GiacMatrix([1 2; 3 4])
transpose(A)
# 2×2 GiacMatrix:
# 1 3
# 2 4Eigenvalues and Eigenvectors
Eigenvalues
Compute eigenvalues using the eigenvalues command from Giac.Commands:
using Giac.Commands: eigenvalues
A = GiacMatrix([2 1; 1 2])
# Convert GiacMatrix to GiacExpr via ptr for eigenvalues command
eigenvalues(GiacExpr(A.ptr))
# Output: 3,1Characteristic Polynomial
The characteristic polynomial can be computed for eigenvalue analysis.
Linear System Solving
Using linsolve
Solve systems of linear equations using linsolve:
using Giac.Commands: linsolve
@giac_var x y
# System: x + y = 3, x - y = 1
linsolve([x + y ~ 3, x - y ~ 1], [x, y])
# Output: [2, 1]Using solve
The general solve command also works for linear systems:
using Giac.Commands: solve
solve([x + y ~ 3, x - y ~ 1], [x, y])
# Returns the solutionGiac.Commands.solve is the same generic function as CommonSolve.solve, so it shares the "solve" verb with DifferentialEquations.jl, NLsolve.jl, Symbolics.jl, etc. Dispatch is by argument type, so there is no name conflict when both packages are loaded — solve(eq::GiacExpr, var) routes to Giac, solve(prob::ODEProblem, …) routes to DifferentialEquations, and so on.
using Giac.Commands: solve # this is the same function as CommonSolve.solve
solve(x^2 - 1 ~ 0, x) # → list[-1, 1]
# The `~` operator builds a symbolic equation; Giac dispatches the
# CommonSolve.solve verb to its CAS solver.Either side of the equation works:
solve(x^2 ~ 1, x) # → list[-1, 1]
solve(x^2 - 1, x) # → list[-1, 1] (Giac infers `= 0`)CommonSolve defines three verbs — init, solve, and solve! — to support the iterative-solver protocol used by DifferentialEquations.jl and friends (init(prob, …) builds an integrator, solve! advances it, solve is a one-shot wrapper that does both). Giac is a one-shot symbolic solver — there is no integrator state to advance — so only solve has a natural meaning here. Giac therefore extends CommonSolve.solve and leaves init and solve! untouched, so packages that do implement the iterative protocol can extend them without any conflict.
Giac also has its own ODE solver, Giac.Commands.desolve (symbolic) and Giac.Commands.odesolve (numerical) — see Differential Equations. Those are intentionally separate verbs (not aliased to solve) because their signatures don't fit the algebraic solve(eq, var) shape.
Matrix Rank
Compute the rank of a matrix using invoke_cmd(:rank, ...):
A = GiacMatrix([1 2 3; 4 5 6; 7 8 9])
# Use invoke_cmd because rank conflicts with LinearAlgebra.rank
# Convert GiacMatrix to GiacExpr via ptr
invoke_cmd(:rank, GiacExpr(A.ptr))
# Output: 2 (linearly dependent rows)Matrix Operations
Addition and Subtraction
A = GiacMatrix([1 2; 3 4])
B = GiacMatrix([5 6; 7 8])
A + B
# 2×2 GiacMatrix:
# 6 8
# 10 12
A - B
# 2×2 GiacMatrix:
# -4 -4
# -4 -4Matrix Multiplication
A = GiacMatrix([1 2; 3 4])
B = GiacMatrix([5 6; 7 8])
A * B
# 2×2 GiacMatrix:
# 19 22
# 43 50Scalar Multiplication
A = GiacMatrix([1 2; 3 4])
2 * A
# 2×2 GiacMatrix:
# 2 4
# 6 8Matrix Powers
A = GiacMatrix([1 2; 3 4])
A^2
# Computes A * ATable of Functions
| Function | Description |
|---|---|
GiacMatrix(array) | Create symbolic matrix from array |
GiacMatrix(:sym, m, n) | Create m×n symbolic matrix (1-based) |
GiacMatrix(:sym, a:b, c:d) | Create matrix with custom index ranges |
GiacMatrix(:sym, m, a:b) | Mixed integer and range arguments |
det(M) | Determinant |
inv(M) | Inverse |
tr(M) | Trace |
transpose(M) | Transpose |
eigenvalues(GiacExpr(M.ptr)) | Eigenvalues |
linsolve(eqs, vars) | Solve linear system |
invoke_cmd(:rank, GiacExpr(M.ptr)) | Matrix rank |
Notes
- All matrix operations work symbolically
- For numerical evaluation, use
to_juliato convert results - Large symbolic matrices can be created efficiently using the symbol constructor
- The
~operator creates equations for use withlinsolveandsolve