Skip to content

Commit a00468f

Browse files
committed
more constraint, factor matching, and curvature tests
1 parent 61260b9 commit a00468f

File tree

2 files changed

+73
-10
lines changed

2 files changed

+73
-10
lines changed

src/Core/constraint.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,10 @@ nonnegative! = Entrywise(ReLU, isnonnegative)
418418
IntervalConstraint(a, b) = Entrywise(x -> clamp(x, a, b), x -> a x b)
419419

420420
function binaryproject(x)
421-
if x > 0.5
422-
return one(x)
423-
else
421+
if x < 0.5
424422
return zero(x)
423+
else
424+
return one(x)
425425
end
426426
end
427427

test/runtests.jl

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,30 @@ const VERBOSE = true
252252
@test ProjectedNormalization(l1scale!) == l1normalize!
253253
@test ScaledNormalization(l1normalize!) == l1scale!
254254
end
255+
256+
@testset "IntervalConstraint" begin
257+
c! = IntervalConstraint(-1, 2)
258+
v = [4, -1, 0, 0.5, 2, -2]
259+
c!(v)
260+
@test v == [2, -1, 0, 0.5, 2, -1]
261+
262+
v = [4, -1, 0, 0.5, 2, -2]
263+
binary!(v)
264+
@test v == [1, 0, 0, 1, 1, 0]
265+
end
266+
267+
@testset "LinearConstraint" begin
268+
A = randn(10, 3)
269+
x = rand(3)
270+
b = A*x
271+
x_input = x + 0.01*rand(3)
272+
x_proj = x_input - A' * ( (A*A') \ (A*x_input .- b) )
273+
274+
c! = LinearConstraint(A, b)
275+
c!(x_input)
276+
@test x_input x_proj
277+
@test A*x_input b
278+
end
255279
end
256280

257281
@testset "SuperDiagonal" begin
@@ -413,6 +437,34 @@ end
413437
T_noisy = Tucker1((B_noisy,A_noisy))
414438
order_found = match_factors!(T_noisy, T)
415439
@test order_found == invperm(order) # Should find the inverse permutation
440+
441+
T = CPDecomposition((5,4,6), 3)
442+
A, B, C = factors(T)
443+
A_noisy = A + 0.01*randn(size(A))
444+
B_noisy = B + 0.01*randn(size(B))
445+
C_noisy = C + 0.01*randn(size(C))
446+
order = [2, 3, 1]
447+
A_noisy .= @view A_noisy[:, order]
448+
B_noisy .= @view B_noisy[:, order]
449+
C_noisy .= @view C_noisy[:, order]
450+
T_noisy = CPDecomposition((A_noisy,B_noisy, C_noisy))
451+
order_found = match_factors!(T_noisy, T)
452+
@test order_found == invperm(order) # Should find the inverse permutation
453+
454+
t = range(0, 1; length=10)
455+
X = [(1.9t)'; (2t)']
456+
Y = [(2t)'; -(0.01t)']
457+
order = [2, 1]
458+
# should be stable since we look through Y and find its best match in X
459+
@test_nowarn order_found = match_slices!(X, Y; dims=1)
460+
@test order_found == invperm(order) # Should find the inverse permutation
461+
462+
Y = [(1.9t)'; (2t)']
463+
X = [(2t)'; -(0.01t)']
464+
order = [2, 1]
465+
# Should warn since both rows of Y are closest to the first row of X
466+
@test_warn "`match_slices!` may not have found the best ordering; using O(n²) greedy approach." (order2_found = match_slices!(X, Y; dims=1))
467+
@test order2_found == invperm(order) # Should still find the correct inverse permutation
416468
end
417469
end
418470

@@ -845,19 +897,30 @@ end
845897

846898
# any smooth function s.t.
847899
# f(0) = 1, f(1) = 0, f''(1) = 0
848-
f(x) = 3x^3 - 5x^2 + x + 1
900+
f(x) = (x-1)^4 # 3x^3 - 5x^2 + x + 1
849901
# k(x) = f''(x) / (1 + (f'(x))^2)^(3/2)
850-
k(x) = (18x-10)/(1 + (1 - 10x + 9x^2)^2)^(1.5) # closed form true curvature
851-
x = range(0, 1, length=20)[2:end] # exclude x=0 point (necessary for :splines)
902+
k(x) = 12*(x-1)^2 / (1 + (4*(x-1)^3)^2)^(1.5) #(18x-10)/(1 + (1 - 10x + 9x^2)^2)^(1.5)
903+
# closed form true curvature
904+
x = range(0, 1, length=20)[2:end] # exclude left end point (we assume these values)
852905
y = f.(x)
853906
k_true = k.(x)
854907
methods = (:finite_differences, :splines, :circles)
855908
k_finite_differences, k_splines, k_circles = (standard_curvature(y; method=method) for method in methods)
856909
# Mean Absolute Percentage Error
857-
MAPE(test_vals, true_vals) = mean(@. abs((test_vals - true_vals)/true_vals))
858-
@test MAPE(k_splines, k_true) < 0.07 # 7%
859-
@test MAPE(k_circles, k_true) < 0.08 # 8%
860-
@test MAPE(k_finite_differences, k_true) < 0.09 # 9%
910+
function MAPE(test_vals, true_vals)
911+
out = @. abs((test_vals - true_vals)/(true_vals))
912+
913+
# Use the next largest value
914+
problem_indexes = abs.(true_vals) .< (eps())
915+
if any(problem_indexes)
916+
next_smallest_value = minimum(abs.(true_vals[abs.(true_vals) .>= (eps())]))
917+
out[problem_indexes] .= abs.((test_vals[problem_indexes]) ./ next_smallest_value)
918+
end
919+
return mean(out)
920+
end
921+
@test MAPE(k_splines, k_true) < 0.02 # 2%
922+
@test MAPE(k_circles, k_true) < 0.03 # 3%
923+
@test MAPE(k_finite_differences, k_true) < 0.03 # 3%
861924

862925
T = Tucker1((10, 10, 10), 3)
863926
Y = array(T)

0 commit comments

Comments
 (0)