Implementation:Haifengl Smile DenseMatrix Factory
Overview
The DenseMatrix Factory provides static factory methods on the DenseMatrix abstract class for constructing dense matrices in the Smile tensor module. These methods create matrices from raw 2D arrays, generate special matrices (zeros, identity, diagonal, random), and handle off-heap memory allocation via java.lang.foreign.MemorySegment. The factory automatically selects the appropriate concrete implementation (DenseMatrix64 for Float64, DenseMatrix32 for Float32) based on the input type or the specified ScalarType.
Type
API Doc
Source Location
base/src/main/java/smile/tensor/DenseMatrix.java:L1339-1651
Import Statements
import smile.tensor.DenseMatrix;
import smile.tensor.ScalarType;
import smile.stat.distribution.Distribution;
import smile.stat.distribution.GaussianDistribution;
External Dependencies
| Dependency | Purpose | Integration |
|---|---|---|
| BLAS (cblas_h) | Scalar operations during initialization | Via FFM (java.lang.foreign) |
| LAPACK (clapack_h) | Used by downstream decomposition methods | Via FFM (java.lang.foreign) |
| java.lang.foreign.MemorySegment | Off-heap matrix storage | Java Foreign Function & Memory API |
API Signatures
public abstract class DenseMatrix implements Matrix, Serializable {
// Construct from raw 2D arrays
public static DenseMatrix of(double[][] A)
public static DenseMatrix of(float[][] A)
// Zero matrices
public static DenseMatrix zeros(ScalarType scalarType, int m, int n)
public DenseMatrix zeros(int m, int n) // instance method, inherits ScalarType
// Identity matrices
public static DenseMatrix eye(ScalarType scalarType, int n)
public static DenseMatrix eye(ScalarType scalarType, int m, int n)
public DenseMatrix eye(int n) // instance method, inherits ScalarType
public DenseMatrix eye(int m, int n) // instance method, inherits ScalarType
// Diagonal matrices
public static DenseMatrix diagflat(double[] diag)
public static DenseMatrix diagflat(float[] diag)
// Random matrices
public static DenseMatrix rand(ScalarType scalarType, int m, int n)
public static DenseMatrix rand(ScalarType scalarType, int m, int n, double lo, double hi)
public static DenseMatrix rand(ScalarType scalarType, int m, int n, Distribution distribution)
public static DenseMatrix randn(ScalarType scalarType, int m, int n)
// Toeplitz matrices
public static DenseMatrix toeplitz(double[] a)
public static DenseMatrix toeplitz(float[] a)
public static DenseMatrix toeplitz(double[] kl, double[] ku)
public static DenseMatrix toeplitz(float[] kl, float[] ku)
}
Inputs and Outputs
| Method | Inputs | Output | Notes |
|---|---|---|---|
of(double[][]) |
2D double array | DenseMatrix64 |
Copies data to off-heap; infers Float64 |
of(float[][]) |
2D float array | DenseMatrix32 |
Copies data to off-heap; infers Float32 |
zeros(ScalarType, m, n) |
Scalar type + dimensions | DenseMatrix64 or DenseMatrix32 |
All elements initialized to zero |
eye(ScalarType, n) |
Scalar type + size | Square identity matrix | where |
eye(ScalarType, m, n) |
Scalar type + dimensions | Rectangular identity | for |
diagflat(double[]) |
1D diagonal values | Square diagonal matrix | Float64 scalar type |
rand(ScalarType, m, n) |
Scalar type + dimensions | Random uniform in [0,1) | Uses MathEx.random()
|
randn(ScalarType, m, n) |
Scalar type + dimensions | Random standard normal | Delegates to GaussianDistribution
|
toeplitz(double[]) |
1D array | Symmetric Toeplitz matrix | Sets UPLO=LOWER for symmetry |
Implementation Details
Leading Dimension Optimization
The zeros() factory computes an optimized leading dimension before allocating:
static int ld(int n) {
int elementSize = 4;
if (n <= 256 / elementSize) return n;
return (((n * elementSize + 511) / 512) * 512 + 64) / elementSize;
}
This ensures the leading dimension avoids powers of 2 that cause cache set conflicts on modern CPUs. For small matrices (), no padding is applied.
Scalar Type Dispatch
The zeros() method uses a switch expression to dispatch to the correct concrete type:
public static DenseMatrix zeros(ScalarType scalarType, int m, int n) {
int ld = ld(m);
return switch (scalarType) {
case Float64 -> {
double[] array = new double[ld * n];
yield new DenseMatrix64(array, m, n, ld, null, null);
}
case Float32 -> {
float[] array = new float[ld * n];
yield new DenseMatrix32(array, m, n, ld, null, null);
}
default -> throw new UnsupportedOperationException("Unsupported ScalarType: " + scalarType);
};
}
Memory Layout
Matrices are stored in column-major order with element at offset . The underlying storage is a MemorySegment backed by a Java array, which the FFM API can pass directly to native BLAS/LAPACK functions.
Usage Examples
Creating a Matrix from Data
import smile.tensor.DenseMatrix;
import smile.tensor.ScalarType;
// From a 2D double array (yields Float64 DenseMatrix)
double[][] data = {
{1.0, 2.0, 3.0},
{4.0, 5.0, 6.0},
{7.0, 8.0, 9.0}
};
DenseMatrix A = DenseMatrix.of(data);
System.out.println(A.nrow() + " x " + A.ncol()); // 3 x 3
System.out.println(A.scalarType()); // Float64
// From a 2D float array (yields Float32 DenseMatrix)
float[][] floatData = {
{1.0f, 2.0f},
{3.0f, 4.0f}
};
DenseMatrix B = DenseMatrix.of(floatData);
System.out.println(B.scalarType()); // Float32
Generating Special Matrices
import smile.tensor.DenseMatrix;
import smile.tensor.ScalarType;
// 4x4 zero matrix in double precision
DenseMatrix Z = DenseMatrix.zeros(ScalarType.Float64, 4, 4);
// 3x3 identity matrix in single precision
DenseMatrix I = DenseMatrix.eye(ScalarType.Float32, 3);
System.out.println(I.get(0, 0)); // 1.0
System.out.println(I.get(0, 1)); // 0.0
// 5x5 identity matrix (rectangular variant)
DenseMatrix I_rect = DenseMatrix.eye(ScalarType.Float64, 5, 3);
Random Matrix Generation
import smile.tensor.DenseMatrix;
import smile.tensor.ScalarType;
// 100x50 random matrix with uniform distribution in [0, 1)
DenseMatrix R = DenseMatrix.rand(ScalarType.Float64, 100, 50);
// 100x50 random matrix with standard normal distribution
DenseMatrix G = DenseMatrix.randn(ScalarType.Float64, 100, 50);
// Uniform random in a specific range [-1, 1)
DenseMatrix U = DenseMatrix.rand(ScalarType.Float64, 100, 50, -1.0, 1.0);
Diagonal and Toeplitz Matrices
import smile.tensor.DenseMatrix;
// Diagonal matrix from eigenvalues
double[] eigenvalues = {5.0, 3.0, 1.0};
DenseMatrix D = DenseMatrix.diagflat(eigenvalues);
// D = diag(5, 3, 1)
// Symmetric Toeplitz matrix (constant diagonals)
double[] toepCoeffs = {4.0, 1.0, 0.5};
DenseMatrix T = DenseMatrix.toeplitz(toepCoeffs);
// T is symmetric with UPLO = LOWER
System.out.println(T.isSymmetric()); // true
Setting Matrix Properties
import smile.tensor.DenseMatrix;
import smile.tensor.ScalarType;
import smile.linalg.UPLO;
// Create a symmetric matrix and mark it
DenseMatrix S = DenseMatrix.zeros(ScalarType.Float64, 3, 3);
S.set(0, 0, 4.0); S.set(0, 1, 2.0); S.set(0, 2, 1.0);
S.set(1, 0, 2.0); S.set(1, 1, 5.0); S.set(1, 2, 3.0);
S.set(2, 0, 1.0); S.set(2, 1, 3.0); S.set(2, 2, 6.0);
S.withUplo(UPLO.LOWER); // Mark as symmetric (lower triangular storage)
System.out.println(S.isSymmetric()); // true
// Now Cholesky decomposition and symmetric BLAS routines are available
Error Handling
| Condition | Exception | Message |
|---|---|---|
| or | IllegalArgumentException |
"Invalid matrix size: m x n" |
ld < m for COL_MAJOR |
IllegalArgumentException |
"Invalid leading dimension for COL_MAJOR: ld < m" |
| Unsupported ScalarType | UnsupportedOperationException |
"Unsupported ScalarType: ..." |
withUplo() on non-square matrix |
IllegalArgumentException |
"The matrix is not square: m x n" |
Performance Considerations
- The optimized leading dimension avoids cache line conflicts, which can improve GEMM performance by 10--30% on large matrices.
- Off-heap memory via
MemorySegmentavoids the 2GB limit of Java arrays and enables zero-copy transfer to native routines. - For small matrices (n <= 64), no leading dimension padding is applied to avoid wasting memory.