@@ -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
255279end
256280
257281@testset " SuperDiagonal" begin
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.9 t)' ; (2 t)' ]
456+ Y = [(2 t)' ; - (0.01 t)' ]
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.9 t)' ; (2 t)' ]
463+ X = [(2 t)' ; - (0.01 t)' ]
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
417469end
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) = 3 x^ 3 - 5 x^ 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) = (18 x- 10 )/ (1 + (1 - 10 x + 9 x^ 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