|
| 1 | +@testitem "Buffer creation and validation" begin |
| 2 | + using DynamicExpressions |
| 3 | + using Test |
| 4 | + |
| 5 | + # Test data setup |
| 6 | + X = rand(2, 10) # 2 features, 10 samples |
| 7 | + operators = OperatorEnum(; binary_operators=[+, *], unary_operators=[sin]) |
| 8 | + tree = Node(; |
| 9 | + op=2, l=Node(; op=1, l=Node(Float64; feature=1)), r=Node(Float64; val=2.0) |
| 10 | + ) |
| 11 | + |
| 12 | + # Basic buffer creation - buffer shape should match (num_leafs, num_samples) |
| 13 | + buffer = zeros(5, size(X, 2)) # 5 leaves should be enough for our test tree |
| 14 | + buffer_ref = Ref(0) |
| 15 | + eval_options = EvalOptions(; buffer=buffer, buffer_ref=buffer_ref) |
| 16 | + @test eval_options.buffer.array === buffer |
| 17 | + @test eval_options.buffer.index === buffer_ref |
| 18 | + |
| 19 | + # Test buffer is not allowed with turbo/bumper |
| 20 | + @test_throws AssertionError EvalOptions(; |
| 21 | + turbo=true, buffer=buffer, buffer_ref=buffer_ref |
| 22 | + ) |
| 23 | + @test_throws AssertionError EvalOptions(; |
| 24 | + bumper=true, buffer=buffer, buffer_ref=buffer_ref |
| 25 | + ) |
| 26 | + |
| 27 | + # Basic evaluation should work |
| 28 | + result = eval_tree_array(tree, X, operators; eval_options) |
| 29 | + @test length(result) == 2 # Returns (output, ok) |
| 30 | + @test size(result[1]) == (size(X, 2),) # Output should match number of samples |
| 31 | +end |
| 32 | + |
| 33 | +@testitem "Buffer correctness" begin |
| 34 | + using DynamicExpressions |
| 35 | + using Test |
| 36 | + |
| 37 | + X = rand(2, 10) |
| 38 | + operators = OperatorEnum(; binary_operators=[+, *], unary_operators=[sin]) |
| 39 | + |
| 40 | + # Test different tree structures |
| 41 | + @testset "Tree type: \$description" for (description, tree) in [ |
| 42 | + ("Single feature", Node(Float64; feature=1)), |
| 43 | + ("Constant", Node(Float64; val=1.5)), |
| 44 | + ("Binary op", Node(; op=1, l=Node(Float64; feature=1), r=Node(Float64; val=2.0))), |
| 45 | + ("Unary op", Node(; op=1, l=Node(Float64; feature=1))), |
| 46 | + ] |
| 47 | + # Regular evaluation |
| 48 | + result1, ok1 = eval_tree_array(tree, X, operators) |
| 49 | + |
| 50 | + # Evaluation with buffer |
| 51 | + buffer = zeros(5, size(X, 2)) |
| 52 | + buffer_ref = Ref(0) |
| 53 | + eval_options = EvalOptions(; buffer=buffer, buffer_ref=buffer_ref) |
| 54 | + result2, ok2 = eval_tree_array(tree, X, operators; eval_options) |
| 55 | + |
| 56 | + # Results should be identical |
| 57 | + @test result1 ≈ result2 |
| 58 | + @test ok1 == ok2 |
| 59 | + end |
| 60 | +end |
| 61 | + |
| 62 | +@testitem "Buffer index management" begin |
| 63 | + using DynamicExpressions |
| 64 | + using Test |
| 65 | + |
| 66 | + X = rand(2, 10) |
| 67 | + operators = OperatorEnum(; binary_operators=[+, *], unary_operators=[sin]) |
| 68 | + |
| 69 | + # Create a complex tree that will use multiple buffer slots |
| 70 | + tree = Node(; |
| 71 | + op=1, # + |
| 72 | + l=Node(; op=1, l=Node(Float64; feature=1)), # sin(x1) |
| 73 | + r=Node(; op=1, l=Node(Float64; feature=2), r=Node(Float64; val=2.0)), # x2 + 2 |
| 74 | + ) |
| 75 | + |
| 76 | + # This tree needs more buffer space due to intermediate computations |
| 77 | + buffer = zeros(10, size(X, 2)) |
| 78 | + buffer_ref = Ref(0) |
| 79 | + eval_options = EvalOptions(; buffer=buffer, buffer_ref=buffer_ref) |
| 80 | + |
| 81 | + # Index should start at 1 |
| 82 | + @test buffer_ref[] == 0 |
| 83 | + |
| 84 | + # Evaluate |
| 85 | + result, ok = eval_tree_array(tree, X, operators; eval_options) |
| 86 | + |
| 87 | + # Index should have advanced |
| 88 | + @test buffer_ref[] == 2 |
| 89 | + |
| 90 | + # Reset and evaluate again |
| 91 | + result2, ok2 = eval_tree_array(tree, X, operators; eval_options) |
| 92 | + # (We expect the index to automatically reset) |
| 93 | + |
| 94 | + # Results should be identical |
| 95 | + @test result ≈ result2 |
| 96 | + @test ok == ok2 |
| 97 | + @test buffer_ref[] == 2 |
| 98 | +end |
| 99 | + |
| 100 | +@testitem "Buffer error handling" begin |
| 101 | + using DynamicExpressions |
| 102 | + using Test |
| 103 | + |
| 104 | + X = rand(2, 10) |
| 105 | + operators = OperatorEnum(; binary_operators=[+, /, *], unary_operators=[sin]) |
| 106 | + |
| 107 | + # Create a tree that might produce NaN/Inf |
| 108 | + tree = Node(; |
| 109 | + op=2, # / |
| 110 | + l=Node(Float64; val=1.0), |
| 111 | + r=Node(Float64; val=0.0), # Division by zero |
| 112 | + ) |
| 113 | + |
| 114 | + buffer = zeros(5, size(X, 2)) |
| 115 | + buffer_ref = Ref(0) |
| 116 | + eval_options = EvalOptions(; buffer=buffer, buffer_ref=buffer_ref) |
| 117 | + |
| 118 | + # Test with early_exit=true |
| 119 | + result1, ok1 = eval_tree_array(tree, X, operators; eval_options) |
| 120 | + @test !ok1 |
| 121 | +end |
0 commit comments