Go module implementing matrices, vectors and linear equation system solvers.
This module is made of four packages:
- nums: utilities for working with numbers
- vec: definition of the vector primitive
- mat: definition of sparse and dense matrices
- lineq: linear equation system solvers
Includes number comparison functions:
func FloatsEqualEps(a, b, epsilon float64) bool
Compares two numbers, a
and b
, and returns true
if both numbers are closer to eachother than the third argument, epsilon
.
func FloatsEqual(a, b float64) bool
Compares two numbers, a
and b
, and returns true
if both numbers are closer to eachother than a default epsilon
of
func IsCloseToZero(a float64) bool
Returns true
whether the number a
is closer to zero than the default epsilon
of
A function to do linear interpolations:
func LinInterpol(startPos, startVal, endPos, endVal, posToInterpolate float64) float64
Computes the y-value of a linear function at x = posToInterpolate
, where the line is defined by the two points: (startPos
, startVal
) and (endPos
, endVal
).
And function to operate with open ranges:
func IsInsideOpenRange(val, start, end float64) bool
Returns true
if start < val
and val < end
.
func DoRangesOverlap(oneStart, oneEnd, twoStart, twoEnd float64) bool
Returns true
if the ranges [oneStart, oneEnd]
and [twoStart, twoEnd]
overlap.
func RangesOverlap(oneStart, oneEnd, twoStart, twoEnd float64,) (ok bool, start, end float64)
If the ranges [oneStart, oneEnd]
and [twoStart, twoEnd]
overlap, returns ok = true
and the overlap as a new range: [start, end]
.
In case there is no overlap, ok = false
.
A vector is a linear array of floating point numbers.
There is a single Vector
struct implementation, but there are two interfaces to use vectors:
Represents a vector which operations don't allow the mutation of its data. The interface is defined as follows:
type ReadOnlyVector interface {
Length() int
Norm() float64
Value(i int) float64
Opposite() ReadOnlyVector
Scaled(factor float64) ReadOnlyVector
Plus(other ReadOnlyVector) ReadOnlyVector
Minus(other ReadOnlyVector) ReadOnlyVector
Times(other ReadOnlyVector) float64
Clone() ReadOnlyVector
Equals(other ReadOnlyVector) bool
AsMutable() MutableVector
}
Use a ReadOnlyVector
as the type of those vector instances which shouldn't mutate their data.
Represents a vector which, appart from the methods present in a ReadOnlyVector
, allows modifying the vector's data.
The interface is defined as follows:
type MutableVector interface {
ReadOnlyVector
SetValue(i int, value float64)
SetZero(i int)
}
Use a MutableVector
as the type of those vector instances which need to mutate their data.
You can create vectors using any of the following functions:
func Make(size int) MutableVector
Creates a MutableVector
instance with the given size and filled with zeroes.
func MakeReadOnly(size int) ReadOnlyVector
Creates a ReadOnlyVector
instance with the given size and filled with zeroes.
func MakeWithValues(vals []float64) MutableVector
Creates a MutableVector
with the passed in values (vals
).
The length of the vector will be len(vals)
.
A matrix is a bi-dimensional array of floating point numbers. There are two matrix implementations (sparse and dense), and two interfaces to use matrices. The two interfaces, following the same approach as with the vectors are:
Represents a matrix which operations don't allow the mutation of its data. The interface is defined as follows:
type ReadOnlyMatrix interface {
Rows() int
Cols() int
NonZeroIndicesAtRow(int) []int
Value(int, int) float64
RowTimesVector(row int, v vec.ReadOnlyVector) float64
TimesVector(v vec.ReadOnlyVector) vec.ReadOnlyVector
TimesMatrix(other ReadOnlyMatrix) ReadOnlyMatrix
}
Represents a matrix that, appart from the methods in a read-only matrix, has operations to mutate its data. The interface is defined as follows:
type MutableMatrix interface {
ReadOnlyMatrix
SetValue(int, int, float64)
AddToValue(int, int, float64)
SetZeroCol(int)
SetIdentityRow(int)
}
As mentioned above, there are two different implementations for a matrix: dense and sparse. The sparse matrix only stores those values that are different from zero, thus reducing the memory footprint in case of large matrices containing lots of zeros.
Represents a sparse matrix, where only non-zero values are stored.
The SparseMat
struct implementation satisfies both the ReadOnlyMatrix
and MutableMatrix
interfaces.
Represents a dense matrix, where every value (including zeroes) are stored.
The DenseMat
struct implementation satisfies both the ReadOnlyMatrix
and MutableMatrix
interfaces.
InkMath
contains one interface defining the contract for all the linear equation solver implementations: Solver
:
type Solver interface {
CanSolve(coefficients mat.ReadOnlyMatrix, freeTerms vec.ReadOnlyVector) bool
Solve(coefficients mat.ReadOnlyMatrix, freeTerms vec.ReadOnlyVector) *Solution
}
This interface has two methods:
CanSolve
to test whether a given set of linear equations can be solved by the current methodSolve
to solve the system and produce a solution
The returned Solution
instance is defined as follows:
type Solution struct {
ReachedMaxIter bool
MinError float64
IterCount int
Solution vec.ReadOnlyVector
}
where:
ReachedMaxIter
is a flag that indicates, in the case of iterative methods, whether the maximum number of iterations was reached before a good enough solution could be foundMinError
a bound of the estimated error of the solution, which can be made as small as requiredIterCount
the number of iterations necessary to find a solutionSolution
the solution vector