Linear Algebra
A LinearAlgebra
typeclass.
The axle-jblas
spoke provides witnesses for JBLAS matrices.
The default jblas matrix toString
isn't very readable,
so this tutorial wraps most results in the Axle string
function,
invoking the cats.Show
witness for those matrices.
Imports and implicits
Import JBLAS and Axle's LinearAlgebra
witness for it.
import cats.implicits._
import spire.algebra.Field
import spire.algebra.NRoot
import axle._
import axle.jblas._
import axle.syntax.linearalgebra.matrixOps
implicit val fieldDouble: Field[Double] = spire.implicits.DoubleAlgebra
implicit val nrootDouble: NRoot[Double] = spire.implicits.DoubleAlgebra
implicit val laJblasDouble = axle.jblas.linearAlgebraDoubleMatrix[Double]
import laJblasDouble._
Creating Matrices
ones(2, 3).show
// res1: String = """1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000"""
ones(1, 4).show
// res2: String = "1.000000 1.000000 1.000000 1.000000"
ones(4, 1).show
// res3: String = """1.000000
// 1.000000
// 1.000000
// 1.000000"""
Creating matrices from arrays
fromColumnMajorArray(2, 2, List(1.1, 2.2, 3.3, 4.4).toArray).show
// res4: String = """1.100000 3.300000
// 2.200000 4.400000"""
fromColumnMajorArray(2, 2, List(1.1, 2.2, 3.3, 4.4).toArray).t.show
// res5: String = """1.100000 2.200000
// 3.300000 4.400000"""
val m = fromColumnMajorArray(4, 5, (1 to 20).map(_.toDouble).toArray)
// m: org.jblas.DoubleMatrix = [1.000000, 5.000000, 9.000000, 13.000000, 17.000000; 2.000000, 6.000000, 10.000000, 14.000000, 18.000000; 3.000000, 7.000000, 11.000000, 15.000000, 19.000000; 4.000000, 8.000000, 12.000000, 16.000000, 20.000000]
m.show
// res6: String = """1.000000 5.000000 9.000000 13.000000 17.000000
// 2.000000 6.000000 10.000000 14.000000 18.000000
// 3.000000 7.000000 11.000000 15.000000 19.000000
// 4.000000 8.000000 12.000000 16.000000 20.000000"""
Random matrices
val r = rand(3, 3)
// r: org.jblas.DoubleMatrix = [0.097284, 0.543659, 0.426756; 0.449587, 0.661096, 0.363255; 0.767837, 0.382935, 0.563905]
r.show
// res7: String = """0.097284 0.543659 0.426756
// 0.449587 0.661096 0.363255
// 0.767837 0.382935 0.563905"""
Matrices defined by functions
matrix(4, 5, (r, c) => r / (c + 1d)).show
// res8: String = """0.000000 0.000000 0.000000 0.000000 0.000000
// 1.000000 0.500000 0.333333 0.250000 0.200000
// 2.000000 1.000000 0.666667 0.500000 0.400000
// 3.000000 1.500000 1.000000 0.750000 0.600000"""
matrix(4, 5, 1d,
(r: Int) => r + 0.5,
(c: Int) => c + 0.6,
(r: Int, c: Int, diag: Double, left: Double, right: Double) => diag).show
// res9: String = """1.000000 1.600000 2.600000 3.600000 4.600000
// 1.500000 1.000000 1.600000 2.600000 3.600000
// 2.500000 1.500000 1.000000 1.600000 2.600000
// 3.500000 2.500000 1.500000 1.000000 1.600000"""
Metadata
val x = fromColumnMajorArray(3, 1, Vector(4.0, 5.1, 6.2).toArray)
// x: org.jblas.DoubleMatrix = [4.000000; 5.100000; 6.200000]
x.show
// res10: String = """4.000000
// 5.100000
// 6.200000"""
val y = fromColumnMajorArray(3, 1, Vector(7.3, 8.4, 9.5).toArray)
// y: org.jblas.DoubleMatrix = [7.300000; 8.400000; 9.500000]
y.show
// res11: String = """7.300000
// 8.400000
// 9.500000"""
x.isEmpty
// res12: Boolean = false
x.isRowVector
// res13: Boolean = false
x.isColumnVector
// res14: Boolean = true
x.isSquare
// res15: Boolean = false
x.isScalar
// res16: Boolean = false
x.rows
// res17: Int = 3
x.columns
// res18: Int = 1
x.length
// res19: Int = 3
Accessing columns, rows, and elements
x.column(0).show
// res20: String = """4.000000
// 5.100000
// 6.200000"""
x.row(1).show
// res21: String = "5.100000"
x.get(2, 0)
// res22: Double = 6.2
val fiveByFive = fromColumnMajorArray(5, 5, (1 to 25).map(_.toDouble).toArray)
// fiveByFive: org.jblas.DoubleMatrix = [1.000000, 6.000000, 11.000000, 16.000000, 21.000000; 2.000000, 7.000000, 12.000000, 17.000000, 22.000000; 3.000000, 8.000000, 13.000000, 18.000000, 23.000000; 4.000000, 9.000000, 14.000000, 19.000000, 24.000000; 5.000000, 10.000000, 15.000000, 20.000000, 25.000000]
fiveByFive.show
// res23: String = """1.000000 6.000000 11.000000 16.000000 21.000000
// 2.000000 7.000000 12.000000 17.000000 22.000000
// 3.000000 8.000000 13.000000 18.000000 23.000000
// 4.000000 9.000000 14.000000 19.000000 24.000000
// 5.000000 10.000000 15.000000 20.000000 25.000000"""
fiveByFive.slice(1 to 3, 2 to 4).show
// res24: String = """12.000000 17.000000 22.000000
// 13.000000 18.000000 23.000000
// 14.000000 19.000000 24.000000"""
fiveByFive.slice(0.until(5,2), 0.until(5,2)).show
// res25: String = """1.000000 11.000000 21.000000
// 3.000000 13.000000 23.000000
// 5.000000 15.000000 25.000000"""
Negate, Transpose, Power
x.negate.show
// res26: String = """-4.000000
// -5.100000
// -6.200000"""
x.transpose.show
// res27: String = "4.000000 5.100000 6.200000"
// x.log
// x.log10
x.pow(2d).show
// res28: String = """16.000000
// 26.010000
// 38.440000"""
Mins, Maxs, Ranges, and Sorts
r.max
// res29: Double = 0.7678372997210756
r.min
// res30: Double = 0.0972843504537868
// r.ceil
// r.floor
r.rowMaxs.show
// res31: String = """0.543659
// 0.661096
// 0.767837"""
r.rowMins.show
// res32: String = """0.097284
// 0.363255
// 0.382935"""
r.columnMaxs.show
// res33: String = "0.767837 0.661096 0.563905"
r.columnMins.show
// res34: String = "0.097284 0.382935 0.363255"
rowRange(r).show
// res35: String = """0.446375
// 0.297841
// 0.384902"""
columnRange(r).show
// res36: String = "0.670553 0.278161 0.200650"
r.sortRows.show
// res37: String = """0.097284 0.426756 0.543659
// 0.363255 0.449587 0.661096
// 0.382935 0.563905 0.767837"""
r.sortColumns.show
// res38: String = """0.097284 0.382935 0.363255
// 0.449587 0.543659 0.426756
// 0.767837 0.661096 0.563905"""
r.sortRows.sortColumns.show
// res39: String = """0.097284 0.426756 0.543659
// 0.363255 0.449587 0.661096
// 0.382935 0.563905 0.767837"""
Statistics
r.rowMeans.show
// res40: String = """0.355900
// 0.491313
// 0.571559"""
r.columnMeans.show
// res41: String = "0.438236 0.529230 0.451305"
// median(r)
sumsq(r).show
// res42: String = "0.801166 0.879253 0.632064"
std(r).show
// res43: String = "0.273870 0.114016 0.083734"
cov(r).show
// res44: String = """0.038749 0.000055 -0.002697
// 0.000055 0.005796 0.004294
// -0.002697 0.004294 0.004226"""
centerRows(r).show
// res45: String = """-0.258616 0.187760 0.070856
// -0.041726 0.169783 -0.128057
// 0.196278 -0.188624 -0.007654"""
centerColumns(r).show
// res46: String = """-0.340952 0.014429 -0.024549
// 0.011350 0.131866 -0.088050
// 0.329601 -0.146295 0.112600"""
zscore(r).show
// res47: String = """-1.244941 0.126554 -0.293183
// 0.041445 1.156554 -1.051546
// 1.203496 -1.283108 1.344729"""
Principal Component Analysis
val (u, s) = pca(r)
// u: org.jblas.DoubleMatrix = [-0.996881, -0.056116, 0.055494; 0.008517, -0.775546, -0.631234; 0.078461, -0.628792, 0.773605]
// s: org.jblas.DoubleMatrix = [0.038961; 0.009281; 0.000529]
u.show
// res48: String = """-0.996881 -0.056116 0.055494
// 0.008517 -0.775546 -0.631234
// 0.078461 -0.628792 0.773605"""
s.show
// res49: String = """0.038961
// 0.009281
// 0.000529"""
Horizontal and vertical concatenation
(x aside y).show
// res50: String = """4.000000 7.300000
// 5.100000 8.400000
// 6.200000 9.500000"""
(x atop y).show
// res51: String = """4.000000
// 5.100000
// 6.200000
// 7.300000
// 8.400000
// 9.500000"""
Addition and subtraction
val z = ones(2, 3)
// z: org.jblas.DoubleMatrix = [1.000000, 1.000000, 1.000000; 1.000000, 1.000000, 1.000000]
z.show
// res52: String = """1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000"""
Matrix addition
import spire.implicits.additiveSemigroupOps
(z + z).show
// res53: String = """2.000000 2.000000 2.000000
// 2.000000 2.000000 2.000000"""
Scalar addition (JBLAS method)
z.addScalar(1.1).show
// res54: String = """2.100000 2.100000 2.100000
// 2.100000 2.100000 2.100000"""
Matrix subtraction
import spire.implicits.additiveGroupOps
(z - z).show
// res55: String = """0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000"""
Scalar subtraction (JBLAS method)
z.subtractScalar(0.2).show
// res56: String = """0.800000 0.800000 0.800000
// 0.800000 0.800000 0.800000"""
Multiplication and Division
Scalar multiplication
z.multiplyScalar(3d).show
// res57: String = """3.000000 3.000000 3.000000
// 3.000000 3.000000 3.000000"""
Matrix multiplication
import spire.implicits.multiplicativeSemigroupOps
(z * z.transpose).show
// res58: String = """3.000000 3.000000
// 3.000000 3.000000"""
Scalar division (JBLAS method)
z.divideScalar(100d).show
// res59: String = """0.010000 0.010000 0.010000
// 0.010000 0.010000 0.010000"""
Map element values
implicit val endo = axle.jblas.endoFunctorDoubleMatrix[Double]
// endo: algebra.Endofunctor[org.jblas.DoubleMatrix, Double] = axle.jblas.package$$anon$1@239fdf0e
import axle.syntax.endofunctor.endofunctorOps
val half = ones(3, 3).map(_ / 2d)
// half: org.jblas.DoubleMatrix = [0.500000, 0.500000, 0.500000; 0.500000, 0.500000, 0.500000; 0.500000, 0.500000, 0.500000]
half.show
// res60: String = """0.500000 0.500000 0.500000
// 0.500000 0.500000 0.500000
// 0.500000 0.500000 0.500000"""
Boolean operators
(r lt half).show
// res61: String = """1.000000 0.000000 1.000000
// 1.000000 0.000000 1.000000
// 0.000000 1.000000 0.000000"""
(r le half).show
// res62: String = """1.000000 0.000000 1.000000
// 1.000000 0.000000 1.000000
// 0.000000 1.000000 0.000000"""
(r gt half).show
// res63: String = """0.000000 1.000000 0.000000
// 0.000000 1.000000 0.000000
// 1.000000 0.000000 1.000000"""
(r ge half).show
// res64: String = """0.000000 1.000000 0.000000
// 0.000000 1.000000 0.000000
// 1.000000 0.000000 1.000000"""
(r eq half).show
// res65: String = """0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000"""
(r ne half).show
// res66: String = """1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000"""
((r lt half) or (r gt half)).show
// res67: String = """1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000"""
((r lt half) and (r gt half)).show
// res68: String = """0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000"""
((r lt half) xor (r gt half)).show
// res69: String = """1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000"""
((r lt half) not).show
// res70: String = """0.000000 1.000000 0.000000
// 0.000000 1.000000 0.000000
// 1.000000 0.000000 1.000000"""
Higher order methods
(m.map(_ + 1)).show
// res71: String = """2.000000 6.000000 10.000000 14.000000 18.000000
// 3.000000 7.000000 11.000000 15.000000 19.000000
// 4.000000 8.000000 12.000000 16.000000 20.000000
// 5.000000 9.000000 13.000000 17.000000 21.000000"""
(m.map(_ * 10)).show
// res72: String = """10.000000 50.000000 90.000000 130.000000 170.000000
// 20.000000 60.000000 100.000000 140.000000 180.000000
// 30.000000 70.000000 110.000000 150.000000 190.000000
// 40.000000 80.000000 120.000000 160.000000 200.000000"""
// m.foldLeft(zeros(4, 1))(_ + _)
(m.foldLeft(ones(4, 1))(_ mulPointwise _)).show
// res73: String = """9945.000000
// 30240.000000
// 65835.000000
// 122880.000000"""
// m.foldTop(zeros(1, 5))(_ + _)
(m.foldTop(ones(1, 5))(_ mulPointwise _)).show
// res74: String = "24.000000 1680.000000 11880.000000 43680.000000 116280.000000"