Linear Algebra

SMILE’s numerical core lives in smile.tensor: Vector, DenseMatrix, SparseMatrix, BandMatrix, and SymmMatrix, plus factorizations (LU, QR, Cholesky, SVD, eigen). Heavy ops call native BLAS/LAPACK via Java FFM when available and automatically fall back to pure-Java implementations (JBlas and JLapack) otherwise. You can inspect availability with NativeLibrary.isBlasAvailable() / isLapackAvailable() or select the backend via -Dsmile.linalg.backend=auto|native|java (or the SMILE_LINALG_BACKEND environment variable).

This page walks from scalar helpers through vectors and matrices to decompositions. Java uses the tensor API directly; Scala adds a lazy expression DSL (+, *, %*%, \) on top of the same types.

Math Functions

Besides java.lang.Math, smile.math.MathEx covers combinatorics, norms, distances, correlations, and related helpers used throughout the library.

import static smile.math.MathEx.*

choose(10, 3)

120.0
      

Special Functions

smile.math.special provides beta, erf / erfc, gamma / lgamma, and digamma. These show up in probability, statistics, and differential equations — for example erfc(x) = 1 - erf(x) is a special case of the incomplete gamma function.

import smile.math.special.*

Erf.erf(1.0)
Gamma.digamma(1.0)

0.8427007929497149
-0.5772156649015328
      

Vectors

Most algorithms take double[] (or float[]) and operate through MathEx. Scala lifts arrays into lazy vector expressions so arithmetic looks like math; evaluation happens when a concrete vector (or print) is needed.

import static smile.math.MathEx.*

double[] x = {1.0, 2.0, 3.0, 4.0}
double[] y = {4.0, 3.0, 2.0, 1.0}

// Java has no vector operator overloading; use MathEx or smile.tensor.Vector.
norm(x)
dot(x, y)

5.477225575051661
20.0
      

Norms include norm / norm2 (L2), norm1, normInf, and normFro. standardize centers and scales to unit variance; unitize / unitize1 scale to unit L2 / L1 norm (in place).

import static smile.math.MathEx.*

double[] y = {4.0, 3.0, 2.0, 1.0}
unitize(y)
y

[0.7302967433402214, 0.5477225575051661, 0.3651483716701107, 0.18257418583505536]
      

Pairwise helpers include dot, distance, kld / jsd (divergences), cov, cor, spearman, and kendall.

import static smile.math.MathEx.*

double[] x = {1.0, 2.0, 3.0, 4.0}
double[] y = {4.0, 3.0, 2.0, 1.0}
unitize(y)

dot(x, y)
cov(x, y)

3.651483716701107
-0.30429030972509225
      

Matrices

Build dense matrices with DenseMatrix.of, zeros, eye, randn, and rand. Prefer DenseMatrix over the old smile.math.matrix.Matrix type (removed).

import smile.tensor.*
import static smile.tensor.ScalarType.Float64

double[][] A = {
    { 0.7220180,  0.07121225, 0.6881997},
    {-0.2648886, -0.89044952, 0.3700456},
    {-0.6391588,  0.44947578, 0.6240573}
}
double[][] B = {
    {0.6881997, -0.07121225,  0.7220180},
    {0.3700456,  0.89044952, -0.2648886},
    {0.6240573, -0.44947578, -0.6391588}
}
double[][] C = {
    { 0.9527204, -0.2973347,  0.06257778},
    {-0.2808735, -0.9403636, -0.19190231},
    { 0.1159052,  0.1652528, -0.97941688}
}

var a = DenseMatrix.of(A)
var b = DenseMatrix.of(B)
var c = DenseMatrix.of(C)
a
DenseMatrix.eye(Float64, 3)

3 x 3
  0.7220    0.0712    0.6882
 -0.2649   -0.8904    0.3700
 -0.6392    0.4495    0.6241

3 x 3
  1.0000    0.0000    0.0000
  0.0000    1.0000    0.0000
  0.0000    0.0000    1.0000
      

Matrix–vector products use mv (and tv for Aᵀx). In Scala, A * x is the same product inside the expression DSL.

import smile.tensor.*
import static smile.linalg.Transpose.NO_TRANSPOSE

var a = DenseMatrix.of(new double[][] {
    { 0.7220180,  0.07121225, 0.6881997},
    {-0.2648886, -0.89044952, 0.3700456},
    {-0.6391588,  0.44947578, 0.6240573}
})
var x = Vector.column(new double[]{1.0, 2.0, 3.0})
var y = Vector.column(new double[]{3.0, 2.0, 1.0})

a.mv(x)                                // A x
a.mv(NO_TRANSPOSE, 1.0, x, 1.5, y)    // y := A x + 1.5 y
y

[2.9290414582113184, -0.9356509345036078, 2.131964578605774]

[7.4290416, 2.06434916, 3.63196466]
      

In Java, add / sub mutate the receiver (A += B). Copy first when you need to keep the original. Scala’s + / - return lazy expressions.

import smile.tensor.*

var a = DenseMatrix.of(new double[][] {
    { 0.7220180,  0.07121225, 0.6881997},
    {-0.2648886, -0.89044952, 0.3700456},
    {-0.6391588,  0.44947578, 0.6240573}
})
var b = DenseMatrix.of(new double[][] {
    {0.6881997, -0.07121225,  0.7220180},
    {0.3700456,  0.89044952, -0.2648886},
    {0.6240573, -0.44947578, -0.6391588}
})

a.copy().add(b)

3 x 3
  1.4102    0.0000    1.4102
  0.1052    0.0000    0.1052
 -0.0151    0.0000   -0.0151
      

Scala * on matrices is element-wise. True matrix multiplication is %*% (R-style) or Java mm / mt / tm (A B, A Bᵀ, Aᵀ B).

import smile.tensor.*

var a = DenseMatrix.of(new double[][] {
    { 0.7220180,  0.07121225, 0.6881997},
    {-0.2648886, -0.89044952, 0.3700456},
    {-0.6391588,  0.44947578, 0.6240573}
})
var b = DenseMatrix.of(new double[][] {
    {0.6881997, -0.07121225,  0.7220180},
    {0.3700456,  0.89044952, -0.2648886},
    {0.6240573, -0.44947578, -0.6391588}
})
var c = DenseMatrix.of(new double[][] {
    { 0.9527204, -0.2973347,  0.06257778},
    {-0.2808735, -0.9403636, -0.19190231},
    { 0.1159052,  0.1652528, -0.97941688}
})

a.mm(b).mm(c)
a.mt(b).mm(c)   // A Bᵀ C

3 x 3
  0.9984    0.0067    0.0554
 -0.0257    0.9361    0.3508
 -0.0495   -0.3517    0.9348

3 x 3
  0.8978   -0.4369    0.0543
  0.4189    0.8856    0.2006
 -0.1357   -0.1574    0.9782
      

transpose() materializes immediately. On a Scala MatrixExpression, prefer lazy .t so a product chain can fuse transpose into mt / tm. Scala also picks an optimal parenthesization for %*% chains (matrix-chain ordering).

import smile.tensor.*
import static smile.tensor.ScalarType.Float64

var a = DenseMatrix.randn(Float64, 300, 900)
var b = DenseMatrix.randn(Float64, 900, 150)
var c = DenseMatrix.randn(Float64, 150, 1800)
var d = DenseMatrix.randn(Float64, 1800, 30)

var r = a.mm(b).mm(c).mm(d)
r.nrow() + " x " + r.ncol()

300 x 30
      

Matrix Decomposition

DenseMatrix exposes lu, qr, cholesky, svd, and eigen. From those you get determinants, ranks, inverses, and linear solves. Scala also wraps common cases as lu, qr, inv, det, rank, and the solve operator \.

import smile.tensor.*

var a = DenseMatrix.of(new double[][] {
    { 0.7220180,  0.07121225, 0.6881997},
    {-0.2648886, -0.89044952, 0.3700456},
    {-0.6391588,  0.44947578, 0.6240573}
})

var inv = a.inverse()
inv.mm(a)                              // ≈ I

a.lu().solve(new double[]{1.0, 2.0, 3.0})  // solve A x = b

3 x 3
  1.0000   -0.0000    0.0000
 -0.0000    1.0000    0.0000
  0.0000    0.0000    1.0000

[-1.7252356779053744, -0.3612592362819077, 3.3004624918302046]
      

Both lu().solve(b) and Scala’s \ solve Ax = b (equivalently x = A⁻¹b). That is different from the matrix–vector product a.mv(b) / a * b.

←   SMILE Serve Statistics  →