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 axle._
import axle.jblas._
import axle.syntax.linearalgebra.matrixOps
import spire.implicits.DoubleAlgebra

implicit val laJblasDouble = axle.jblas.linearAlgebraDoubleMatrix[Double]
import laJblasDouble._

Creating Matrices

string(ones(2, 3))
// res1: String =
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000

string(ones(1, 4))
// res2: String = 1.000000 1.000000 1.000000 1.000000

string(ones(4, 1))
// res3: String =
// 1.000000
// 1.000000
// 1.000000
// 1.000000

Creating matrices from arrays

string(fromColumnMajorArray(2, 2, List(1.1, 2.2, 3.3, 4.4).toArray))
// res4: String =
// 1.100000 3.300000
// 2.200000 4.400000

string(fromColumnMajorArray(2, 2, List(1.1, 2.2, 3.3, 4.4).toArray).t)
// 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]

string(m)
// 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.285241, 0.438920, 0.513144; 0.218250, 0.424770, 0.999493; 0.851683, 0.247471, 0.319942]

string(r)
// res7: String =
// 0.285241 0.438920 0.513144
// 0.218250 0.424770 0.999493
// 0.851683 0.247471 0.319942

Matrices defined by functions

string(matrix(4, 5, (r, c) => r / (c + 1d)))
// 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

string(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))
// 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]

string(x)
// 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]

string(y)
// 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

string(x.column(0))
// res20: String =
// 4.000000
// 5.100000
// 6.200000

string(x.row(1))
// 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]

string(fiveByFive)
// 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

string(fiveByFive.slice(1 to 3, 2 to 4))
// res24: String =
// 12.000000 17.000000 22.000000
// 13.000000 18.000000 23.000000
// 14.000000 19.000000 24.000000

string(fiveByFive.slice(0.until(5,2), 0.until(5,2)))
// res25: String =
// 1.000000 11.000000 21.000000
// 3.000000 13.000000 23.000000
// 5.000000 15.000000 25.000000

Negate, Transpose, Power

string(x.negate)
// res26: String =
// -4.000000
// -5.100000
// -6.200000

string(x.transpose)
// res27: String = 4.000000 5.100000 6.200000

// x.log
// x.log10

string(x.pow(2d))
// res31: String =
// 16.000000
// 26.010000
// 38.440000

Mins, Maxs, Ranges, and Sorts

r.max

r.min

// r.ceil
// r.floor

string(r.rowMaxs)

string(r.rowMins)

string(r.columnMaxs)

string(r.columnMins)

string(rowRange(r))

string(columnRange(r))

string(r.sortRows)

string(r.sortColumns)

string(r.sortRows.sortColumns)

Statistics

string(r.rowMeans)
// res32: String =
// 0.412435
// 0.547504
// 0.473032

string(r.columnMeans)
// res33: String = 0.451724 0.370387 0.610860

// median(r)

string(sumsq(r))
// res36: String = 0.854359 0.434322 1.364666

string(std(r))
// res37: String = 0.284132 0.087106 0.285900

string(cov(r))
// res38: String =
// 0.009239 -0.005334 -0.013027
// -0.005334 0.000986 -0.015923
// -0.013027 -0.015923 0.028211

string(centerRows(r))
// res39: String =
// -0.127194 0.026485 0.100709
// -0.329254 -0.122734 0.451989
// 0.378651 -0.225561 -0.153090

string(centerColumns(r))
// res40: String =
// -0.166484 0.068533 -0.097716
// -0.233475 0.054383 0.388633
// 0.399958 -0.122916 -0.290917

string(zscore(r))
// res41: String =
// -0.585937 0.786770 -0.341783
// -0.821710 0.624330 1.359331
// 1.407647 -1.411100 -1.017548

Principal Component Analysis

val (u, s) = pca(r, 0.95)
// u: org.jblas.DoubleMatrix = [-0.330396, -0.470912, 0.817973; -0.325446, -0.756653, -0.567064; 0.885959, -0.453562, 0.096739]
// s: org.jblas.DoubleMatrix = [0.038918; 0.011878; 0.011396]

string(u)
// res42: String =
// -0.330396 -0.470912 0.817973
// -0.325446 -0.756653 -0.567064
// 0.885959 -0.453562 0.096739

string(s)
// res43: String =
// 0.038918
// 0.011878
// 0.011396

Horizontal and vertical concatenation

string(x aside y)
// res44: String =
// 4.000000 7.300000
// 5.100000 8.400000
// 6.200000 9.500000

string(x atop y)
// res45: String =
// 4.000000
// 5.100000
// 6.200000
// 7.300000
// 8.400000
// 9.500000

Addition and subtraction

val x = ones(2, 3)
// x: org.jblas.DoubleMatrix = [1.000000, 1.000000, 1.000000; 1.000000, 1.000000, 1.000000]

string(x)
// res46: String =
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000

Matrix addition

import spire.implicits.additiveSemigroupOps
// import spire.implicits.additiveSemigroupOps

string(x + x)
// res47: String =
// 2.000000 2.000000 2.000000
// 2.000000 2.000000 2.000000

Scalar addition (JBLAS method)

string(x.addScalar(1.1))
// res48: String =
// 2.100000 2.100000 2.100000
// 2.100000 2.100000 2.100000

Matrix subtraction

import spire.implicits.additiveGroupOps
// import spire.implicits.additiveGroupOps

string(x - x)
// res49: String =
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000

Scalar subtraction (JBLAS method)

string(x.subtractScalar(0.2))
// res50: String =
// 0.800000 0.800000 0.800000
// 0.800000 0.800000 0.800000

Multiplication and Division

Scalar multiplication

string(x.multiplyScalar(3d))
// res51: String =
// 3.000000 3.000000 3.000000
// 3.000000 3.000000 3.000000

Matrix multiplication

import spire.implicits.multiplicativeSemigroupOps
// import spire.implicits.multiplicativeSemigroupOps

string(x * x.transpose)
// res52: String =
// 3.000000 3.000000
// 3.000000 3.000000

Scalar division (JBLAS method)

string(x.divideScalar(100d))
// res53: String =
// 0.010000 0.010000 0.010000
// 0.010000 0.010000 0.010000

Map element values

implicit val endo = axle.jblas.endoFunctorDoubleMatrix[Double]
// endo: axle.algebra.Endofunctor[org.jblas.DoubleMatrix,Double] = axle.jblas.package$$anon$6@6473ee76

import axle.syntax.endofunctor.endofunctorOps
// 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]

string(half)
// res54: String =
// 0.500000 0.500000 0.500000
// 0.500000 0.500000 0.500000
// 0.500000 0.500000 0.500000

Boolean operators

string(r lt half)
// res55: String =
// 1.000000 1.000000 0.000000
// 1.000000 1.000000 0.000000
// 0.000000 1.000000 1.000000

string(r le half)
// res56: String =
// 1.000000 1.000000 0.000000
// 1.000000 1.000000 0.000000
// 0.000000 1.000000 1.000000

string(r gt half)
// res57: String =
// 0.000000 0.000000 1.000000
// 0.000000 0.000000 1.000000
// 1.000000 0.000000 0.000000

string(r ge half)
// res58: String =
// 0.000000 0.000000 1.000000
// 0.000000 0.000000 1.000000
// 1.000000 0.000000 0.000000

string(r eq half)
// res59: String =
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000

string(r ne half)
// res60: String =
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000

string((r lt half) or (r gt half))
// res61: String =
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000

string((r lt half) and (r gt half))
// res62: String =
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000
// 0.000000 0.000000 0.000000

string((r lt half) xor (r gt half))
// res63: String =
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000
// 1.000000 1.000000 1.000000

string((r lt half) not)
// res64: String =
// 0.000000 0.000000 1.000000
// 0.000000 0.000000 1.000000
// 1.000000 0.000000 0.000000

Higher order methods

string(m.map(_ + 1))
// res65: 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

string(m.map(_ * 10))
// res66: 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))(_ + _)

string(m.foldLeft(ones(4, 1))(_ mulPointwise _))
// res69: String =
// 9945.000000
// 30240.000000
// 65835.000000
// 122880.000000

// m.foldTop(zeros(1, 5))(_ + _)

string(m.foldTop(ones(1, 5))(_ mulPointwise _))
// res72: String = 24.000000 1680.000000 11880.000000 43680.000000 116280.000000