Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ import { CostFunction } from '../LearningAlgorithm';
import { vec } from '../../../utilities/aliases';

describe('GradientDescent', () => {
it('defaults both alpha and maxIterations', () => {
let callCount = 0;
const costFn: CostFunction = (_theta) => {
callCount++;
if (callCount > 3) return { cost: 0, gradient: vec([0, 0]) };
return { cost: 1, gradient: vec([-1, -1]) };
};
const initial = vec([0, 0]);
const theta = gradientDescent({})(initial, costFn);
// With default alpha=0.1, after 3 iterations: 3 * 0.1 = 0.3
expect(theta.equals(vec([0.3, 0.3]))).toBe(true);
});

it('respects maxIterations', () => {
const costFn: CostFunction = (_theta) => ({
cost: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,12 @@ describe('kMeansClustering', () => {
expect(centroids).toHaveLength(15);
expect(labels.getDimension()).toBe(X.getNumberOfRows());
});

it('uses default parameters when not provided', () => {
const X = loadTestData('2-gaussian-clusters');
const { centroids, labels } = kMeansClustering(X, {});

expect(centroids).toHaveLength(5);
expect(labels.getDimension()).toBe(X.getNumberOfRows());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,9 @@ describe('SupportVectorMachineClassifier', () => {
it('cannot predict probabilities', () => {
expect(() => new SupportVectorMachineClassifier({}).predictProbabilities(mat([]))).toThrow();
});

it('throws when predicting before training', () => {
const cls = new SupportVectorMachineClassifier({});
expect(() => cls.predict(mat([[1, 2]]))).toThrow('Cannot call predict before train');
});
});
14 changes: 14 additions & 0 deletions src/applications/statistics/__tests__/LeastSquares.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ describe('LeastSquares', () => {
expect(solution).not.toBeUndefined;
});

test('handles rank-deficient overdetermined system', () => {
// A is 4x3 but rank 2, so A^T*A is 3x3 rank 2 → underdetermined normal equation
const A = mat([
[1, 0, 1],
[0, 1, 1],
[2, 0, 2],
[0, 2, 2],
]);
const b = vec([1, 1, 2, 2]);

const solution = solveOverdeterminedSystem(A, b);
expect(solution).not.toBeUndefined();
});

test('rejects a system with a dimension mismatch', () => {
const A = mat([
[1, 2],
Expand Down
12 changes: 12 additions & 0 deletions src/decompositions/__tests__/CholeskyDecomposition.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,17 @@ describe('CholeskyDecomposition', () => {
const A = mat([[-1]]);
expect(calculateCholeskyDecomposition(A)).toBeUndefined;
});

test('returns undefined for a positive-semidefinite matrix with zero diagonal', () => {
// This matrix has eigenvalues [6, 0, 0] — positive semidefinite but not positive definite
// Cholesky fails because Ljj becomes 0 and subsequent division is undefined
const A = mat([
[4, 2, 2],
[2, 1, 1],
[2, 1, 1],
]);
const result = calculateCholeskyDecomposition(A);
expect(result).toBeUndefined();
});
});
});
11 changes: 11 additions & 0 deletions src/decompositions/__tests__/LUDecomposition.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,16 @@ describe('LUDecomposition', () => {
const A = mat([[1, 2]]);
expect(() => calculateLUDecomposition(A)).toThrow();
});

test('handles a singular matrix with zero pivot', () => {
// All rows are linearly dependent → after elimination, a zero diagonal appears
// causing division by 0 in getNthLowerTriangularMatrix
const A = mat([
[1, 2, 3],
[2, 4, 6],
[3, 6, 9],
]);
expect(() => calculateLUDecomposition(A)).toThrow();
});
});
});
23 changes: 23 additions & 0 deletions src/operations/__tests__/GaussJordan.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ describe('GaussJordan', () => {
const x = (solution as UniqueSolution<number>).solution;
expect(A.apply(x).equals(b)).toBe(true);
});

test('solves a non-square (tall) system', () => {
const A = mat([
[1, 2],
[3, 4],
[5, 6],
]);
const b = vec([3, 7, 11]);
const solution = solve(A, b);
expect(solution.solutionType).toBe(SolutionType.UNIQUE);
const x = (solution as UniqueSolution<number>).solution;
expect(A.apply(x).equals(b)).toBe(true);
});

test('solves a non-square (wide) underdetermined system', () => {
const A = mat([
[1, 2, 3],
[4, 5, 6],
]);
const b = vec([1, 2]);
const solution = solve(A, b);
expect(solution.solutionType).toBe(SolutionType.UNDERDETERMINED);
});
});

describe('solveByGaussianElimination', () => {
Expand Down
42 changes: 35 additions & 7 deletions src/types/matrix/__tests__/NumberMatrix.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,27 @@ configs.forEach(({ testClassName, builder, vectorBuilder }) => {
});

describe('getSparseData', () => {
const A = builder.identity(3);
const data = A.getSparseData();
const expected = new Map();
expected.set(0, new Map().set(0, 1));
expected.set(1, new Map().set(1, 1));
expected.set(2, new Map().set(2, 1));
expect(data).toStrictEqual(expected);
test('returns sparse data for an identity matrix', () => {
const A = builder.identity(3);
const data = A.getSparseData();
const expected = new Map();
expected.set(0, new Map().set(0, 1));
expected.set(1, new Map().set(1, 1));
expected.set(2, new Map().set(2, 1));
expect(data).toStrictEqual(expected);
});

test('returns sparse data for a dense matrix with multiple non-zeros per row', () => {
const A = builder.fromArray([
[1, 2],
[3, 4],
]);
const data = A.getSparseData();
const expected = new Map();
expected.set(0, new Map().set(0, 1).set(1, 2));
expected.set(1, new Map().set(0, 3).set(1, 4));
expect(data).toStrictEqual(expected);
});
});
});

Expand Down Expand Up @@ -493,3 +507,17 @@ configs.forEach(({ testClassName, builder, vectorBuilder }) => {
});
});
});

describe('FloatMatrix', () => {
test('throws on dimension mismatch when constructing from Float64Array', () => {
const data = new Float64Array([1, 2, 3, 4, 5, 6]);
expect(() => new FloatMatrix(data, [2, 2])).toThrow('Dimension mismatch');
});

test('can be constructed from Float64Array with correct shape', () => {
const data = new Float64Array([1, 2, 3, 4]);
const M = new FloatMatrix(data, [2, 2]);
expect(M.getNumberOfRows()).toBe(2);
expect(M.getNumberOfColumns()).toBe(2);
});
});
Loading