Files
IT3708/src/linreg_test.odin
2026-02-06 09:17:59 +01:00

130 lines
2.8 KiB
Odin

package main
import "core:fmt"
import "core:math"
import "core:testing"
@(test)
test_solve_simple_2x2 :: proc(t: ^testing.T) {
// Properly allocate 2D array
A := [][]f64{{2.0, 1.0}, {1.0, 3.0}}
b := []f64{5.0, 6.0}
x := solve_linear_system(A, b)
defer delete(x)
testing.expect(t, math.abs(x[0] - 1.8) < 1e-10, "x[0] should be 1.8")
testing.expect(t, math.abs(x[1] - 1.4) < 1e-10, "x[1] should be 1.4")
}
@(test)
test_solve_identity :: proc(t: ^testing.T) {
A := [][]f64{{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}
b := []f64{3.0, 7.0, -2.0}
x := solve_linear_system(A, b)
defer delete(x)
for i in 0 ..< 3 {
testing.expect(t, math.abs(x[i] - b[i]) < 1e-10)
}
}
@(test)
test_solve_needs_pivoting :: proc(t: ^testing.T) {
// System that requires pivoting for numerical stability
A := [][]f64{{0.0001, 1.0}, {1.0, 1.0}}
b := []f64{1.0, 2.0}
x := solve_linear_system(A, b)
defer delete(x)
// Verify Ax = b
result := make([]f64, 2)
defer delete(result)
for i in 0 ..< 2 {
sum := 0.0
for j in 0 ..< 2 {
sum += A[i][j] * x[j]
}
result[i] = sum
}
testing.expect(t, math.abs(result[0] - b[0]) < 1e-6)
testing.expect(t, math.abs(result[1] - b[1]) < 1e-6)
}
@(test)
test_solve_singular_matrix :: proc(t: ^testing.T) {
// Singular matrix (rows are linearly dependent)
A := [][]f64{{1.0, 2.0}, {2.0, 4.0}}
b := []f64{3.0, 6.0}
x := solve_linear_system(A, b)
defer delete(x)
// Should return zero vector for singular matrix
testing.expect_value(t, len(x), 2)
testing.expect(t, math.abs(x[0]) < 1e-10)
testing.expect(t, math.abs(x[1]) < 1e-10)
}
@(test)
test_solve_larger_system :: proc(t: ^testing.T) {
A := [][]f64 {
{4.0, 1.0, 2.0, 1.0},
{1.0, 5.0, 1.0, 2.0},
{2.0, 1.0, 6.0, 1.0},
{1.0, 2.0, 1.0, 7.0},
}
b := []f64{10.0, 12.0, 14.0, 16.0}
x := solve_linear_system(A, b)
defer delete(x)
// Verify Ax ≈ b
for i in 0 ..< 4 {
sum := 0.0
for j in 0 ..< 4 {
sum += A[i][j] * x[j]
}
testing.expect(t, math.abs(sum - b[i]) < 1e-8)
}
}
@(test)
test_predict :: proc(t: ^testing.T) {
X := [][]f64{{1.0, 1.0}, {1.0, 2.0}, {1.0, 3.0}}
beta := []f64{3.0, 2.0} // y = 2x + 3
predictions := predict(X, beta)
defer delete(predictions)
expected := []f64{5.0, 7.0, 9.0}
for i in 0 ..< len(predictions) {
testing.expect(t, math.abs(predictions[i] - expected[i]) < 1e-10)
}
}
@(test)
test_rmse :: proc(t: ^testing.T) {
predictions := []f64{5.0, 7.0, 9.0}
actual := []f64{5.1, 6.9, 9.2}
error := rmse(predictions, actual)
// RMSE = sqrt((0.1² + 0.1² + 0.2²) / 3) = sqrt(0.06/3) ≈ 0.141
testing.expect(t, math.abs(error - 0.141) < 0.01, "RMSE should be ~0.141")
}
@(test)
test_rmse_perfect_fit :: proc(t: ^testing.T) {
predictions := []f64{1.0, 2.0, 3.0}
actual := []f64{1.0, 2.0, 3.0}
error := rmse(predictions, actual)
testing.expect(t, error < 1e-10, "RMSE should be 0 for perfect fit")
}