130 lines
2.8 KiB
Odin
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")
|
|
}
|