From d1598f4f6790628fc45747b309e75ceeb48b0376 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Tue, 25 Nov 2025 18:04:54 +0100 Subject: [PATCH 1/8] Enforce info field to be a NamedTuple --- src/Argmax2D/Argmax2D.jl | 16 ++-- src/DecisionFocusedLearningBenchmarks.jl | 2 +- src/DynamicAssortment/DynamicAssortment.jl | 2 +- .../DynamicVehicleScheduling.jl | 2 +- .../anticipative_solver.jl | 2 +- .../StochasticVehicleScheduling.jl | 18 ++-- src/Utils/Utils.jl | 1 - src/Utils/data_sample.jl | 86 ++++++++++++++++--- src/Utils/interface.jl | 28 +----- src/Utils/policy.jl | 2 +- test/argmax.jl | 2 +- test/argmax_2d.jl | 2 +- test/dynamic_vsp.jl | 2 +- test/dynamic_vsp_plots.jl | 2 +- test/fixed_size_shortest_path.jl | 2 +- test/portfolio_optimization.jl | 2 +- test/ranking.jl | 2 +- test/subset_selection.jl | 2 +- test/utils.jl | 12 ++- test/vsp.jl | 2 +- test/warcraft.jl | 2 +- 21 files changed, 121 insertions(+), 70 deletions(-) diff --git a/src/Argmax2D/Argmax2D.jl b/src/Argmax2D/Argmax2D.jl index 5f67c8e..512d60b 100644 --- a/src/Argmax2D/Argmax2D.jl +++ b/src/Argmax2D/Argmax2D.jl @@ -62,7 +62,7 @@ function Utils.generate_sample(bench::Argmax2DBenchmark, rng::AbstractRNG) θ_true ./= 2 * norm(θ_true) instance = build_polytope(rand(rng, polytope_vertex_range); shift=rand(rng)) y_true = maximizer(θ_true; instance) - return DataSample(; x=x, θ=θ_true, y=y_true, info=instance) + return DataSample(; x=x, θ=θ_true, y=y_true, instance=instance) end """ @@ -88,11 +88,11 @@ function Utils.generate_statistical_model( return model end -function Utils.plot_data(::Argmax2DBenchmark; info, θ, kwargs...) +function Utils.plot_data(::Argmax2DBenchmark; instance, θ, kwargs...) pl = init_plot() - plot_polytope!(pl, info) + plot_polytope!(pl, instance) plot_objective!(pl, θ) - return plot_maximizer!(pl, θ, info, maximizer) + return plot_maximizer!(pl, θ, instance, maximizer) end """ @@ -101,9 +101,13 @@ $TYPEDSIGNATURES Plot the data sample for the [`Argmax2DBenchmark`](@ref). """ function Utils.plot_data( - bench::Argmax2DBenchmark, sample::DataSample; info=sample.info, θ=sample.θ, kwargs... + bench::Argmax2DBenchmark, + sample::DataSample; + instance=sample.instance, + θ=sample.θ, + kwargs..., ) - return Utils.plot_data(bench; info, θ, kwargs...) + return Utils.plot_data(bench; instance, θ, kwargs...) end export Argmax2DBenchmark diff --git a/src/DecisionFocusedLearningBenchmarks.jl b/src/DecisionFocusedLearningBenchmarks.jl index be2c500..4515ec1 100644 --- a/src/DecisionFocusedLearningBenchmarks.jl +++ b/src/DecisionFocusedLearningBenchmarks.jl @@ -70,7 +70,7 @@ export generate_sample, generate_dataset, generate_environments, generate_enviro export generate_scenario export generate_policies export generate_statistical_model -export generate_maximizer, maximizer_kwargs +export generate_maximizer export generate_anticipative_solution export is_exogenous, is_endogenous diff --git a/src/DynamicAssortment/DynamicAssortment.jl b/src/DynamicAssortment/DynamicAssortment.jl index 14ef63c..c943dba 100644 --- a/src/DynamicAssortment/DynamicAssortment.jl +++ b/src/DynamicAssortment/DynamicAssortment.jl @@ -83,7 +83,7 @@ Outputs a data sample containing an [`Instance`](@ref). function Utils.generate_sample( b::DynamicAssortmentBenchmark, rng::AbstractRNG=MersenneTwister(0) ) - return DataSample(; info=Instance(b, rng)) + return DataSample(; instance=Instance(b, rng)) end """ diff --git a/src/DynamicVehicleScheduling/DynamicVehicleScheduling.jl b/src/DynamicVehicleScheduling/DynamicVehicleScheduling.jl index 06fd54c..fb0ea7e 100644 --- a/src/DynamicVehicleScheduling/DynamicVehicleScheduling.jl +++ b/src/DynamicVehicleScheduling/DynamicVehicleScheduling.jl @@ -70,7 +70,7 @@ function Utils.generate_dataset(b::DynamicVehicleSchedulingBenchmark, dataset_si dataset_size = min(dataset_size, length(files)) return [ DataSample(; - info=Instance( + instance=Instance( read_vsp_instance(files[i]); max_requests_per_epoch, Δ_dispatch, diff --git a/src/DynamicVehicleScheduling/anticipative_solver.jl b/src/DynamicVehicleScheduling/anticipative_solver.jl index c2eae89..9a29b5b 100644 --- a/src/DynamicVehicleScheduling/anticipative_solver.jl +++ b/src/DynamicVehicleScheduling/anticipative_solver.jl @@ -222,7 +222,7 @@ function anticipative_solver( compute_features(state, env.instance) end - return DataSample(; info=(; state, reward), y=y_true, x) + return DataSample(; y=y_true, x, state, reward) end return obj, dataset diff --git a/src/StochasticVehicleScheduling/StochasticVehicleScheduling.jl b/src/StochasticVehicleScheduling/StochasticVehicleScheduling.jl index a2b2374..b9f2099 100644 --- a/src/StochasticVehicleScheduling/StochasticVehicleScheduling.jl +++ b/src/StochasticVehicleScheduling/StochasticVehicleScheduling.jl @@ -67,7 +67,7 @@ end function Utils.objective_value( ::StochasticVehicleSchedulingBenchmark, sample::DataSample, y::BitVector ) - return evaluate_solution(y, sample.info) + return evaluate_solution(y, sample.instance) end """ @@ -98,7 +98,7 @@ function Utils.generate_sample( else nothing end - return DataSample(; x, info=instance, y=y_true) + return DataSample(; x, instance, y=y_true) end """ @@ -145,11 +145,12 @@ end $TYPEDSIGNATURES """ function plot_instance( - ::StochasticVehicleSchedulingBenchmark, sample::DataSample{<:Instance{City}}; kwargs... + ::StochasticVehicleSchedulingBenchmark, sample::DataSample; kwargs... ) - (; tasks, district_width, width) = sample.info.city + @assert hasproperty(sample.instance, :city) "Sample does not contain city information." + (; tasks, district_width, width) = sample.instance.city ticks = 0:district_width:width - max_time = maximum(t.end_time for t in sample.info.city.tasks[1:(end - 1)]) + max_time = maximum(t.end_time for t in sample.instance.city.tasks[1:(end - 1)]) fig = plot(; xlabel="x", ylabel="y", @@ -204,11 +205,12 @@ end $TYPEDSIGNATURES """ function plot_solution( - ::StochasticVehicleSchedulingBenchmark, sample::DataSample{<:Instance{City}}; kwargs... + ::StochasticVehicleSchedulingBenchmark, sample::DataSample; kwargs... ) - (; tasks, district_width, width) = sample.info.city + @assert hasproperty(sample.instance, :city) "Sample does not contain city information." + (; tasks, district_width, width) = sample.instance.city ticks = 0:district_width:width - solution = Solution(sample.y, sample.info) + solution = Solution(sample.y, sample.instance) path_list = compute_path_list(solution) fig = plot(; xlabel="x", diff --git a/src/Utils/Utils.jl b/src/Utils/Utils.jl index 1b4767f..0989c85 100644 --- a/src/Utils/Utils.jl +++ b/src/Utils/Utils.jl @@ -35,7 +35,6 @@ export generate_policies export generate_anticipative_solution export plot_data, compute_gap -export maximizer_kwargs export grid_graph, get_path, path_to_matrix export neg_tensor, squeeze_last_dims, average_tensor export scip_model, highs_model diff --git a/src/Utils/data_sample.jl b/src/Utils/data_sample.jl index f445219..b043b48 100644 --- a/src/Utils/data_sample.jl +++ b/src/Utils/data_sample.jl @@ -2,39 +2,99 @@ $TYPEDEF Data sample data structure. +Its main purpose is to store datasets generated by the benchmarks. +It has 3 main fields: features `x`, cost parameters `θ` and solution `y`. +Additionally, it has an `info` field to store any additional information as a `NamedTuple`, usually the instance, but can be used for anything else. # Fields $TYPEDFIELDS """ -@kwdef struct DataSample{ - I, +struct DataSample{ + I<:NamedTuple, F<:Union{AbstractArray,Nothing}, S<:Union{AbstractArray,Nothing}, C<:Union{AbstractArray,Nothing}, } "input features (optional)" - x::F = nothing + x::F "intermediate cost parameters (optional)" - θ::C = nothing + θ::C "output solution (optional)" - y::S = nothing + y::S "additional information, usually the instance (optional)" - info::I = nothing + info::I end +""" +$TYPEDSIGNATURES + +Constructor for `DataSample` with keyword arguments. + +Additional keyword arguments beyond `x`, `θ`, and `y` are stored in the `info` field +and can be accessed directly (e.g., `data.instance` instead of `data.info.instance`). + +# Examples +```julia +d = DataSample(x=[1,2,3], θ=[4,5,6], y=[7,8,9], instance="my_instance") +d.instance # "my_instance" +``` +""" +function DataSample(; x=nothing, θ=nothing, y=nothing, kwargs...) + info = (; kwargs...) + return DataSample(x, θ, y, info) +end + +""" +$TYPEDSIGNATURES + +Extended property access for `DataSample`. + +Allows accessing `info` fields directly as properties (e.g., `d.instance` instead of `d.info.instance`). +""" +function Base.getproperty(d::DataSample, name::Symbol) + if name in (:x, :θ, :y, :info) + return getfield(d, name) + else + return getproperty(getfield(d, :info), name) + end +end + +""" +$TYPEDSIGNATURES + +Return all property names of a `DataSample`, including both struct fields and `info` fields. + +This enables tab completion for all available properties, including those stored in `info`. +""" +function Base.propertynames(d::DataSample, private::Bool=false) + return (fieldnames(DataSample)..., propertynames(getfield(d, :info), private)...) +end + +""" +$TYPEDSIGNATURES + +Display a `DataSample` with truncated array representations for readability. + +Large arrays are automatically truncated with ellipsis (`...`), similar to standard Julia array printing. +""" function Base.show(io::IO, d::DataSample) fields = String[] + io_limited = IOContext(io, :limit => true, :compact => true) if !isnothing(d.x) - push!(fields, "x=$(d.x)") + x_str = sprint(show, d.x; context=io_limited) + push!(fields, "x=$x_str") end if !isnothing(d.θ) - push!(fields, "θ_true=$(d.θ)") + θ_str = sprint(show, d.θ; context=io_limited) + push!(fields, "θ_true=$θ_str") end if !isnothing(d.y) - push!(fields, "y_true=$(d.y)") + y_str = sprint(show, d.y; context=io_limited) + push!(fields, "y_true=$y_str") end - if !isnothing(d.info) - push!(fields, "instance=$(d.info)") + for (key, value) in pairs(d.info) + value_str = sprint(show, value; context=io_limited) + push!(fields, "$key=$value_str") end return print(io, "DataSample(", join(fields, ", "), ")") end @@ -57,7 +117,7 @@ Transform the features in the dataset. function StatsBase.transform(t, dataset::AbstractVector{<:DataSample}) return map(dataset) do d (; info, x, θ, y) = d - DataSample(; info, x=StatsBase.transform(t, x), θ, y) + DataSample(StatsBase.transform(t, x), θ, y, info) end end @@ -80,7 +140,7 @@ Reconstruct the features in the dataset. function StatsBase.reconstruct(t, dataset::AbstractVector{<:DataSample}) return map(dataset) do d (; info, x, θ, y) = d - DataSample(; info, x=StatsBase.reconstruct(t, x), θ, y) + DataSample(StatsBase.reconstruct(t, x), θ, y, info) end end diff --git a/src/Utils/interface.jl b/src/Utils/interface.jl index 19689e5..fbe61a9 100644 --- a/src/Utils/interface.jl +++ b/src/Utils/interface.jl @@ -99,26 +99,6 @@ function compute_gap end """ $TYPEDSIGNATURES -For simple benchmarks where there is no instance object, maximizer does not need any keyword arguments. -""" -function maximizer_kwargs( - ::AbstractBenchmark, sample::DataSample{Nothing,F,S,C} -) where {F,S,C} - return NamedTuple() -end - -""" -$TYPEDSIGNATURES - -For benchmarks where there is an instance object, maximizer needs the instance object as a keyword argument. -""" -function maximizer_kwargs(::AbstractBenchmark, sample::DataSample) - return (; instance=sample.info) -end - -""" -$TYPEDSIGNATURES - Default behaviour of `objective_value`. """ function objective_value(::AbstractBenchmark, θ::AbstractArray, y::AbstractArray) @@ -175,7 +155,7 @@ function compute_gap( target_obj = objective_value(bench, sample) x = sample.x θ = statistical_model(x) - y = maximizer(θ; maximizer_kwargs(bench, sample)...) + y = maximizer(θ; sample.info...) obj = objective_value(bench, sample, y) Δ = check ? obj - target_obj : target_obj - obj return Δ / abs(target_obj) @@ -234,7 +214,7 @@ Uses the info field of the sample as the instance. function generate_environment( bench::AbstractDynamicBenchmark, sample::DataSample, rng::AbstractRNG; kwargs... ) - return generate_environment(bench, sample.info, rng; kwargs...) + return generate_environment(bench, sample.instance, rng; kwargs...) end """ @@ -250,7 +230,7 @@ function generate_environments( kwargs..., ) Random.seed!(rng, seed) - return map(dataset) do instance - generate_environment(bench, instance, rng; kwargs...) + return map(dataset) do sample + generate_environment(bench, sample, rng; kwargs...) end end diff --git a/src/Utils/policy.jl b/src/Utils/policy.jl index 537335f..8d9fd7b 100644 --- a/src/Utils/policy.jl +++ b/src/Utils/policy.jl @@ -44,7 +44,7 @@ function evaluate_policy!( features, state = observe(env) state_copy = deepcopy(state) # To avoid mutation issues reward = step!(env, y) - sample = DataSample(; x=features, y=y, info=(; state=state_copy, reward)) + sample = DataSample(; x=features, y=y, state=state_copy, reward=reward) if @isdefined labeled_dataset push!(labeled_dataset, sample) else diff --git a/test/argmax.jl b/test/argmax.jl index cce896c..3ceab6d 100644 --- a/test/argmax.jl +++ b/test/argmax.jl @@ -24,7 +24,7 @@ @test size(x) == (nb_features, instance_dim) @test length(θ_true) == instance_dim @test length(y_true) == instance_dim - @test isnothing(sample.info) + @test length(sample.info) == 0 @test all(y_true .== maximizer(θ_true)) θ = model(x) diff --git a/test/argmax_2d.jl b/test/argmax_2d.jl index 75351b8..70e4a0a 100644 --- a/test/argmax_2d.jl +++ b/test/argmax_2d.jl @@ -24,7 +24,7 @@ x = sample.x θ_true = sample.θ y_true = sample.y - instance = sample.info + instance = sample.instance @test length(x) == nb_features @test length(θ_true) == 2 @test length(y_true) == 2 diff --git a/test/dynamic_vsp.jl b/test/dynamic_vsp.jl index ff01218..40b4307 100644 --- a/test/dynamic_vsp.jl +++ b/test/dynamic_vsp.jl @@ -26,7 +26,7 @@ @test mean(r_lazy) <= mean(r_greedy) env = environments[1] - instance = dataset[1].info + instance = dataset[1].instance scenario = generate_scenario(b, instance) v, y = generate_anticipative_solution(b, env, scenario; nb_epochs=2, reset_env=true) diff --git a/test/dynamic_vsp_plots.jl b/test/dynamic_vsp_plots.jl index 5bba734..30e2adc 100644 --- a/test/dynamic_vsp_plots.jl +++ b/test/dynamic_vsp_plots.jl @@ -13,7 +13,7 @@ fig1 = DVSP.plot_instance(env) @test fig1 isa Plots.Plot - instance = dataset[1].info + instance = dataset[1].instance scenario = generate_scenario(b, instance; seed=0) v, y = generate_anticipative_solution(b, env, scenario; nb_epochs=3, reset_env=true) diff --git a/test/fixed_size_shortest_path.jl b/test/fixed_size_shortest_path.jl index c8f659a..ccf85d5 100644 --- a/test/fixed_size_shortest_path.jl +++ b/test/fixed_size_shortest_path.jl @@ -25,7 +25,7 @@ @test size(x) == (p,) @test length(θ_true) == A @test length(y_true) == A - @test isnothing(sample.info) + @test length(sample.info) == 0 @test all(y_true .== maximizer(θ_true)) θ = model(x) @test length(θ) == length(θ_true) diff --git a/test/portfolio_optimization.jl b/test/portfolio_optimization.jl index 6d0f0a2..28980ff 100644 --- a/test/portfolio_optimization.jl +++ b/test/portfolio_optimization.jl @@ -16,7 +16,7 @@ @test size(x) == (p,) @test length(θ_true) == d @test length(y_true) == d - @test isnothing(sample.info) + @test length(sample.info) == 0 @test all(y_true .== maximizer(θ_true)) θ = model(x) diff --git a/test/ranking.jl b/test/ranking.jl index 8c8cf10..0a74bf3 100644 --- a/test/ranking.jl +++ b/test/ranking.jl @@ -21,7 +21,7 @@ @test size(x) == (nb_features, instance_dim) @test length(θ_true) == instance_dim @test length(y_true) == instance_dim - @test isnothing(sample.info) + @test length(sample.info) == 0 @test all(y_true .== maximizer(θ_true)) θ = model(x) diff --git a/test/subset_selection.jl b/test/subset_selection.jl index 6cb6ea9..6bbbc09 100644 --- a/test/subset_selection.jl +++ b/test/subset_selection.jl @@ -23,7 +23,7 @@ @test size(x) == (n,) @test length(θ_true) == n @test length(y_true) == n - @test isnothing(sample.info) + @test length(sample.info) == 0 @test all(y_true .== maximizer(θ_true)) # Features and true weights should be equal diff --git a/test/utils.jl b/test/utils.jl index 21d0820..3848b53 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -32,7 +32,10 @@ end function random_sample() return DataSample(; - x=randn(rng, 10, 5), θ=rand(rng, 5), y=rand(rng, 10), info="this is an instance" + x=randn(rng, 10, 5), + θ=rand(rng, 5), + y=rand(rng, 10), + instance="this is an instance", ) end @@ -41,8 +44,11 @@ end io = IOBuffer() show(io, sample) - @test String(take!(io)) == - "DataSample(x=$(sample.x), θ_true=$(sample.θ), y_true=$(sample.y), instance=$(sample.info))" + s = String(take!(io)) + @test occursin("DataSample(", s) + @test occursin("θ_true", s) + @test occursin("y_true", s) + @test occursin("instance=\"this is an instance\"", s) # Test StatsBase methods using StatsBase: diff --git a/test/vsp.jl b/test/vsp.jl index eb054da..1c3b5fd 100644 --- a/test/vsp.jl +++ b/test/vsp.jl @@ -42,7 +42,7 @@ for sample in dataset x = sample.x - instance = sample.info + instance = sample.instance E = ne(instance.graph) @test size(x) == (20, E) θ = model(x) diff --git a/test/warcraft.jl b/test/warcraft.jl index 7653609..e5a7de0 100644 --- a/test/warcraft.jl +++ b/test/warcraft.jl @@ -24,7 +24,7 @@ y_true = sample.y @test size(x) == (96, 96, 3, 1) @test all(θ_true .<= 0) - @test isnothing(sample.info) + @test length(sample.info) == 0 θ = model(x) @test size(θ) == size(θ_true) From 5fe39407cd818bced2c9e01450d538b7b6fc5c2c Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 11:54:51 +0100 Subject: [PATCH 2/8] update CI --- .github/workflows/Test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index c750c68..05872b8 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - julia-version: ['1'] + julia-version: ['1.10', '1'] steps: - uses: actions/checkout@v5 From a66f67eca71a2f44b169e82793fed08a75cf84e8 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 11:58:42 +0100 Subject: [PATCH 3/8] fix compat --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 9af78f7..788c061 100644 --- a/Project.toml +++ b/Project.toml @@ -62,9 +62,9 @@ Requires = "1.3.0" SCIP = "0.12" SimpleWeightedGraphs = "1.4" SparseArrays = "1" -Statistics = "1.11.1" +Statistics = "1" StatsBase = "0.34.4" -julia = "1.6" +julia = "1.10" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" From d9c3fd91585209d72e9c035b6518477003dee118 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 14:33:53 +0100 Subject: [PATCH 4/8] try to fix tests in CI --- Project.toml | 22 ++------------ .../anticipative_solver.jl | 26 ++++++++-------- src/DynamicVehicleScheduling/maximizer.jl | 9 ++++-- .../static_vsp/parsing.jl | 12 ++++---- .../instance/instance.jl | 4 +-- src/StochasticVehicleScheduling/maximizer.jl | 2 +- .../solution/algorithms/local_search.jl | 6 ++-- src/Utils/policy.jl | 8 ++--- test/Project.toml | 25 ++++++++++++++++ test/argmax.jl | 2 +- test/argmax_2d.jl | 2 +- test/code.jl | 13 ++++---- test/dynamic_assortment.jl | 28 ++++++++--------- test/dynamic_vsp.jl | 2 +- test/dynamic_vsp_plots.jl | 5 ++-- test/fixed_size_shortest_path.jl | 2 +- test/portfolio_optimization.jl | 2 +- test/ranking.jl | 2 +- test/runtests.jl | 30 +++++++++++++++---- test/subset_selection.jl | 2 +- test/utils.jl | 24 +++++++-------- test/vsp.jl | 2 +- test/warcraft.jl | 2 +- 23 files changed, 131 insertions(+), 101 deletions(-) create mode 100644 test/Project.toml diff --git a/Project.toml b/Project.toml index 788c061..5322192 100644 --- a/Project.toml +++ b/Project.toml @@ -3,6 +3,9 @@ uuid = "2fbe496a-299b-4c81-bab5-c44dfc55cf20" authors = ["Members of JuliaDecisionFocusedLearning"] version = "0.3.0" +[workspace] +projects = ["docs", "test"] + [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" @@ -65,22 +68,3 @@ SparseArrays = "1" Statistics = "1" StatsBase = "0.34.4" julia = "1.10" - -[extras] -Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" -Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" -Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" -JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" -JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" -ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" -Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" -UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" -Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" - -[targets] -test = ["Aqua", "Documenter", "Flux", "Graphs", "JET", "JuliaFormatter", "Random", "ProgressMeter", "StableRNGs", "Statistics", "Test", "TestItemRunner", "UnicodePlots", "Zygote"] diff --git a/src/DynamicVehicleScheduling/anticipative_solver.jl b/src/DynamicVehicleScheduling/anticipative_solver.jl index 9a29b5b..42293c5 100644 --- a/src/DynamicVehicleScheduling/anticipative_solver.jl +++ b/src/DynamicVehicleScheduling/anticipative_solver.jl @@ -21,13 +21,14 @@ function retrieve_routes_anticipative( current_task = task while current_task != 1 # < nb_tasks push!(route, current_task) - local next_task + next_task = -1 for i in 1:nb_tasks if isapprox(y[current_task, i, t], 1; atol=0.1) next_task = i break end end + @assert next_task != -1 "No next task found from task $current_task at epoch $t" current_task = next_task end push!(routes[i], route) @@ -92,14 +93,14 @@ function anticipative_solver( job_indices = 2:nb_nodes epoch_indices = T - @variable(model, y[i = 1:nb_nodes, j = 1:nb_nodes, t = epoch_indices]; binary=true) + @variable(model, y[i=1:nb_nodes, j=1:nb_nodes, t=epoch_indices]; binary=true) @objective( model, Max, sum( - -duration[i, j] * y[i, j, t] for - i in 1:nb_nodes, j in 1:nb_nodes, t in epoch_indices + -duration[i, j] * y[i, j, t] for i in 1:nb_nodes, j in 1:nb_nodes, + t in epoch_indices ) ) @@ -171,12 +172,14 @@ function anticipative_solver( routes = epoch_routes[i] epoch_customers = epoch_indices[i] - y_true = VSPSolution( - Vector{Int}[ - map(idx -> findfirst(==(idx), epoch_customers), route) for route in routes - ]; - max_index=length(epoch_customers), - ).edge_matrix + y_true = + VSPSolution( + Vector{Int}[ + map(idx -> findfirst(==(idx), epoch_customers), route) for + route in routes + ]; + max_index=length(epoch_customers), + ).edge_matrix location_indices = customer_index[epoch_customers] new_coordinates = env.instance.static_instance.coordinate[location_indices] @@ -200,8 +203,7 @@ function anticipative_solver( is_must_dispatch[2:end] .= true else is_must_dispatch[2:end] .= - planning_start_time .+ epoch_duration .+ @view(new_duration[1, 2:end]) .> - new_start_time[2:end] + planning_start_time .+ epoch_duration .+ @view(new_duration[1, 2:end]) .> new_start_time[2:end] end is_postponable[2:end] .= .!is_must_dispatch[2:end] # TODO: avoid code duplication with add_new_customers! diff --git a/src/DynamicVehicleScheduling/maximizer.jl b/src/DynamicVehicleScheduling/maximizer.jl index 65e8b60..8c7e272 100644 --- a/src/DynamicVehicleScheduling/maximizer.jl +++ b/src/DynamicVehicleScheduling/maximizer.jl @@ -62,13 +62,14 @@ function retrieve_routes(y::AbstractArray, graph::AbstractGraph) current_task = task while current_task != 1 # < nb_tasks push!(route, current_task) - local next_task + next_task = -1 for i in outneighbors(graph, current_task) if isapprox(y[current_task, i], 1; atol=0.1) next_task = i break end end + @assert next_task != -1 "No next task found from task $current_task" current_task = next_task end push!(routes, route) @@ -93,7 +94,7 @@ function prize_collecting_vsp( nb_nodes = nv(graph) job_indices = 2:(nb_nodes) - @variable(model, y[i = 1:nb_nodes, j = 1:nb_nodes; has_edge(graph, i, j)] >= 0) + @variable(model, y[i=1:nb_nodes, j=1:nb_nodes; has_edge(graph, i, j)] >= 0) θ_ext = fill(0.0, location_count(instance)) # no prize for must dispatch requests, only hard constraints θ_ext[instance.is_postponable] .= θ @@ -129,7 +130,9 @@ end function oracle(θ; instance::DVSPState, kwargs...) routes = prize_collecting_vsp(θ; instance=instance, kwargs...) - return VSPSolution(routes; max_index=location_count(instance.state_instance)).edge_matrix + return VSPSolution( + routes; max_index=location_count(instance.state_instance) + ).edge_matrix end function g(y; instance, kwargs...) diff --git a/src/DynamicVehicleScheduling/static_vsp/parsing.jl b/src/DynamicVehicleScheduling/static_vsp/parsing.jl index 8cc67be..671e207 100644 --- a/src/DynamicVehicleScheduling/static_vsp/parsing.jl +++ b/src/DynamicVehicleScheduling/static_vsp/parsing.jl @@ -10,14 +10,14 @@ Normalize all time values by the `normalization` parameter. function read_vsp_instance(filepath::String; normalization=3600.0, digits=2) type = Float64 #rounded ? Int : Float64 mode = "" - local edge_weight_type - local edge_weight_format + edge_weight_type = "" + edge_weight_format = "" duration_matrix = Vector{type}[] nb_locations = 0 - local demand - local service_time - local coordinates - local start_time + demand = type[] + service_time = type[] + coordinates = Matrix{type}(undef, 0, 2) + start_time = type[] file = open(filepath, "r") for line in eachline(file) diff --git a/src/StochasticVehicleScheduling/instance/instance.jl b/src/StochasticVehicleScheduling/instance/instance.jl index 0c09e6f..090c69a 100644 --- a/src/StochasticVehicleScheduling/instance/instance.jl +++ b/src/StochasticVehicleScheduling/instance/instance.jl @@ -39,8 +39,8 @@ function create_VSP_graph(city::City) job_tasks = 2:(city.nb_tasks + 1) travel_times = [ - distance(task1.end_point, task2.start_point) for - task1 in city.tasks, task2 in city.tasks + distance(task1.end_point, task2.start_point) for task1 in city.tasks, + task2 in city.tasks ] # Create existing edges diff --git a/src/StochasticVehicleScheduling/maximizer.jl b/src/StochasticVehicleScheduling/maximizer.jl index 515d989..66e9eb3 100644 --- a/src/StochasticVehicleScheduling/maximizer.jl +++ b/src/StochasticVehicleScheduling/maximizer.jl @@ -14,7 +14,7 @@ function vsp_maximizer( nb_nodes = nv(graph) job_indices = 2:(nb_nodes - 1) - @variable(model, y[i = 1:nb_nodes, j = 1:nb_nodes; has_edge(graph, i, j)], Bin) + @variable(model, y[i=1:nb_nodes, j=1:nb_nodes; has_edge(graph, i, j)], Bin) @objective( model, diff --git a/src/StochasticVehicleScheduling/solution/algorithms/local_search.jl b/src/StochasticVehicleScheduling/solution/algorithms/local_search.jl index 87fe47b..49ae00c 100644 --- a/src/StochasticVehicleScheduling/solution/algorithms/local_search.jl +++ b/src/StochasticVehicleScheduling/solution/algorithms/local_search.jl @@ -11,8 +11,8 @@ function solve_deterministic_VSP( (; city, graph) = instance travel_times = [ - distance(task1.end_point, task2.start_point) for - task1 in city.tasks, task2 in city.tasks + distance(task1.end_point, task2.start_point) for task1 in city.tasks, + task2 in city.tasks ] model = model_builder() @@ -21,7 +21,7 @@ function solve_deterministic_VSP( nb_nodes = nv(graph) job_indices = 2:(nb_nodes - 1) - @variable(model, x[i = 1:nb_nodes, j = 1:nb_nodes; has_edge(graph, i, j)], Bin) + @variable(model, x[i=1:nb_nodes, j=1:nb_nodes; has_edge(graph, i, j)], Bin) @objective( model, diff --git a/src/Utils/policy.jl b/src/Utils/policy.jl index 8d9fd7b..796cbf0 100644 --- a/src/Utils/policy.jl +++ b/src/Utils/policy.jl @@ -38,17 +38,17 @@ function evaluate_policy!( reset!(env; reset_rng=true, seed=seed) end total_reward = 0.0 - local labeled_dataset + labeled_dataset = DataSample[] while !is_terminated(env) y = policy(env; kwargs...) features, state = observe(env) state_copy = deepcopy(state) # To avoid mutation issues reward = step!(env, y) sample = DataSample(; x=features, y=y, state=state_copy, reward=reward) - if @isdefined labeled_dataset - push!(labeled_dataset, sample) + if isempty(labeled_dataset) + labeled_dataset = typeof(sample)[sample] else - labeled_dataset = [sample] + push!(labeled_dataset, sample) end total_reward += reward end diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..0df5f5b --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,25 @@ +[deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +DecisionFocusedLearningBenchmarks = "2fbe496a-299b-4c81-bab5-c44dfc55cf20" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" +UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" +Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[sources] +DecisionFocusedLearningBenchmarks = {path = ".."} + +[compat] +Aqua = "0.8.14" +Test = "1" diff --git a/test/argmax.jl b/test/argmax.jl index 3ceab6d..ba5723b 100644 --- a/test/argmax.jl +++ b/test/argmax.jl @@ -1,4 +1,4 @@ -@testitem "Argmax" begin +@testset "Argmax" begin using DecisionFocusedLearningBenchmarks instance_dim = 10 diff --git a/test/argmax_2d.jl b/test/argmax_2d.jl index 70e4a0a..089e013 100644 --- a/test/argmax_2d.jl +++ b/test/argmax_2d.jl @@ -1,4 +1,4 @@ -@testitem "Argmax2D" begin +@testset "Argmax2D" begin using DecisionFocusedLearningBenchmarks using Plots diff --git a/test/code.jl b/test/code.jl index ad2e5f7..af617fc 100644 --- a/test/code.jl +++ b/test/code.jl @@ -1,4 +1,4 @@ -@testitem "Aqua" begin +@testset "Aqua" begin using Aqua Aqua.test_all( DecisionFocusedLearningBenchmarks; @@ -7,19 +7,22 @@ ) end -@testitem "JET" begin +@testset "JET" begin using JET - JET.test_package(DecisionFocusedLearningBenchmarks; target_defined_modules=true) + JET.test_package( + DecisionFocusedLearningBenchmarks; + target_modules=[DecisionFocusedLearningBenchmarks], + ) end -@testitem "JuliaFormatter" begin +@testset "JuliaFormatter" begin using JuliaFormatter @test JuliaFormatter.format( DecisionFocusedLearningBenchmarks; verbose=false, overwrite=false ) end -@testitem "Documenter" begin +@testset "Documenter" begin using Documenter Documenter.doctest(DecisionFocusedLearningBenchmarks) end diff --git a/test/dynamic_assortment.jl b/test/dynamic_assortment.jl index de8bf41..a3feddb 100644 --- a/test/dynamic_assortment.jl +++ b/test/dynamic_assortment.jl @@ -1,8 +1,6 @@ -@testsnippet DAPSetup begin - const DAP = DecisionFocusedLearningBenchmarks.DynamicAssortment -end +const DAP = DecisionFocusedLearningBenchmarks.DynamicAssortment -@testitem "DynamicAssortment - Benchmark Construction" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Benchmark Construction" begin # Test default constructor b = DynamicAssortmentBenchmark() @test b.N == 20 @@ -28,7 +26,7 @@ end @test DAP.max_steps(b) == 80 end -@testitem "DynamicAssortment - Instance Generation" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Instance Generation" begin b = DynamicAssortmentBenchmark(; N=5, d=3, K=2) rng = MersenneTwister(42) @@ -53,7 +51,7 @@ end @test DAP.prices(instance) == instance.prices end -@testitem "DynamicAssortment - Environment Initialization" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Environment Initialization" begin b = DynamicAssortmentBenchmark(; N=5, d=2, K=2, max_steps=10) instance = DAP.Instance(b, MersenneTwister(42)) @@ -80,7 +78,7 @@ end @test DAP.prices(env) == instance.prices end -@testitem "DynamicAssortment - Environment Reset" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Environment Reset" begin b = DynamicAssortmentBenchmark(; N=3, d=1, K=2, max_steps=5) instance = DAP.Instance(b, MersenneTwister(42)) env = DAP.Environment(instance; seed=123) @@ -107,7 +105,7 @@ end @test env.features ≈ expected_features end -@testitem "DynamicAssortment - Hype Update Logic" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Hype Update Logic" begin b = DynamicAssortmentBenchmark(; N=5, d=1, K=2) instance = DAP.Instance(b, MersenneTwister(42)) env = DAP.Environment(instance; seed=123) @@ -135,7 +133,7 @@ end @test all(hype .== 1.0) # Should not affect any item hype end -@testitem "DynamicAssortment - Choice Probabilities" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Choice Probabilities" begin b = DynamicAssortmentBenchmark(; N=3, d=1, K=2) instance = DAP.Instance(b, MersenneTwister(42)) env = DAP.Environment(instance; seed=123) @@ -167,7 +165,7 @@ end @test probs[4] ≈ 1.0 # Only no-purchase available end -@testitem "DynamicAssortment - Expected Revenue" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Expected Revenue" begin b = DynamicAssortmentBenchmark(; N=3, d=1, K=2) instance = DAP.Instance(b, MersenneTwister(42)) env = DAP.Environment(instance; seed=123) @@ -183,7 +181,7 @@ end @test revenue == 0.0 # Only no-purchase available with price 0 end -@testitem "DynamicAssortment - Environment Step" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Environment Step" begin b = DynamicAssortmentBenchmark(; N=3, d=1, K=2, max_steps=5) instance = DAP.Instance(b, MersenneTwister(42)) env = DAP.Environment(instance; seed=123) @@ -219,7 +217,7 @@ end @test_throws AssertionError step!(env, assortment) end -@testitem "DynamicAssortment - Endogenous vs Exogenous" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Endogenous vs Exogenous" begin # Test endogenous environment (features change with purchases) b_endo = DynamicAssortmentBenchmark(; N=3, d=1, K=2, exogenous=false) instance_endo = DAP.Instance(b_endo, MersenneTwister(42)) @@ -243,7 +241,7 @@ end @test all(env_exo.d_features .== 0.0) # Delta features should remain zero end -@testitem "DynamicAssortment - Observation" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Observation" begin b = DynamicAssortmentBenchmark(; N=3, d=2, max_steps=10) instance = DAP.Instance(b, MersenneTwister(42)) env = DAP.Environment(instance; seed=123) @@ -266,7 +264,7 @@ end @test obs1 != obs2 # Observations should differ after purchase end -@testitem "DynamicAssortment - Policies" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Policies" begin using Statistics: mean b = DynamicAssortmentBenchmark(; N=5, d=2, K=3, max_steps=20) @@ -307,7 +305,7 @@ end @test sum(greedy_action) == DAP.assortment_size(env) end -@testitem "DynamicAssortment - Model and Maximizer Integration" setup = [Imports, DAPSetup] begin +@testset "DynamicAssortment - Model and Maximizer Integration" begin b = DynamicAssortmentBenchmark(; N=4, d=3, K=2) # Test statistical model generation diff --git a/test/dynamic_vsp.jl b/test/dynamic_vsp.jl index 40b4307..825b319 100644 --- a/test/dynamic_vsp.jl +++ b/test/dynamic_vsp.jl @@ -1,4 +1,4 @@ -@testitem "DVSP" begin +@testset "DVSP" begin using DecisionFocusedLearningBenchmarks.DynamicVehicleScheduling using Statistics: mean diff --git a/test/dynamic_vsp_plots.jl b/test/dynamic_vsp_plots.jl index 30e2adc..e079086 100644 --- a/test/dynamic_vsp_plots.jl +++ b/test/dynamic_vsp_plots.jl @@ -1,6 +1,5 @@ -@testitem "Dynamic VSP Plots" begin - using DecisionFocusedLearningBenchmarks.DynamicVehicleScheduling - const DVSP = DecisionFocusedLearningBenchmarks.DynamicVehicleScheduling +@testset "Dynamic VSP Plots" begin + import DecisionFocusedLearningBenchmarks.DynamicVehicleScheduling as DVSP using Plots # Create test benchmark and data (similar to scripts/a.jl) diff --git a/test/fixed_size_shortest_path.jl b/test/fixed_size_shortest_path.jl index ccf85d5..e1360e1 100644 --- a/test/fixed_size_shortest_path.jl +++ b/test/fixed_size_shortest_path.jl @@ -1,4 +1,4 @@ -@testitem "FixedSizeShortestPath" begin +@testset "FixedSizeShortestPath" begin using DecisionFocusedLearningBenchmarks.FixedSizeShortestPath using Graphs diff --git a/test/portfolio_optimization.jl b/test/portfolio_optimization.jl index 28980ff..96895b8 100644 --- a/test/portfolio_optimization.jl +++ b/test/portfolio_optimization.jl @@ -1,4 +1,4 @@ -@testitem "Portfolio Optimization" begin +@testset "Portfolio Optimization" begin using DecisionFocusedLearningBenchmarks d = 50 diff --git a/test/ranking.jl b/test/ranking.jl index 0a74bf3..4f9d437 100644 --- a/test/ranking.jl +++ b/test/ranking.jl @@ -1,4 +1,4 @@ -@testitem "Ranking" begin +@testset "Ranking" begin using DecisionFocusedLearningBenchmarks instance_dim = 10 diff --git a/test/runtests.jl b/test/runtests.jl index 2b90cad..914a6e4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,26 @@ -using TestItemRunner +using Test +using DecisionFocusedLearningBenchmarks +using Random -@testsnippet Imports begin - using DecisionFocusedLearningBenchmarks - using Random -end +@testset "DecisionFocusedLearningBenchmarks tests" begin + @testset "Code quality" begin + include("code.jl") + end + + include("utils.jl") -@run_package_tests verbose = true + include("argmax.jl") + include("argmax_2d.jl") + include("ranking.jl") + include("subset_selection.jl") + include("fixed_size_shortest_path.jl") + include("warcraft.jl") + include("vsp.jl") + include("portfolio_optimization.jl") + + @testset "Dynamic Vehicle Scheduling Problem" begin + include("dynamic_vsp.jl") + include("dynamic_vsp_plots.jl") + end + include("dynamic_assortment.jl") +end diff --git a/test/subset_selection.jl b/test/subset_selection.jl index 6bbbc09..952420d 100644 --- a/test/subset_selection.jl +++ b/test/subset_selection.jl @@ -1,4 +1,4 @@ -@testitem "Subset selection" begin +@testset "Subset selection" begin using DecisionFocusedLearningBenchmarks n = 25 diff --git a/test/utils.jl b/test/utils.jl index 3848b53..2e487cf 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,4 +1,4 @@ -@testitem "Grid graphs" begin +@testset "Grid graphs" begin using DecisionFocusedLearningBenchmarks.Utils using DecisionFocusedLearningBenchmarks.Utils: count_edges, get_path, index_to_coord using Graphs @@ -24,9 +24,17 @@ end end -@testitem "DataSample" begin +@testset "DataSample" begin using DecisionFocusedLearningBenchmarks using StableRNGs + using StatsBase: + ZScoreTransform, + UnitRangeTransform, + fit, + transform, + transform!, + reconstruct, + reconstruct! rng = StableRNG(1234) @@ -50,16 +58,6 @@ end @test occursin("y_true", s) @test occursin("instance=\"this is an instance\"", s) - # Test StatsBase methods - using StatsBase: - ZScoreTransform, - UnitRangeTransform, - fit, - transform, - transform!, - reconstruct, - reconstruct! - # Create a dataset for testing N = 5 dataset = [random_sample() for _ in 1:N] @@ -119,7 +117,7 @@ end end end -@testitem "Maximizers" begin +@testset "Maximizers" begin using DecisionFocusedLearningBenchmarks.Utils: TopKMaximizer top_k = TopKMaximizer(3) @test top_k([1, 2, 3, 4, 5]) == [0, 0, 1, 1, 1] diff --git a/test/vsp.jl b/test/vsp.jl index 1c3b5fd..0d6f5d2 100644 --- a/test/vsp.jl +++ b/test/vsp.jl @@ -1,4 +1,4 @@ -@testitem "Stochastic VSP" begin +@testset "Stochastic VSP" begin using DecisionFocusedLearningBenchmarks using DecisionFocusedLearningBenchmarks.StochasticVehicleScheduling using Graphs diff --git a/test/warcraft.jl b/test/warcraft.jl index e5a7de0..5a7a564 100644 --- a/test/warcraft.jl +++ b/test/warcraft.jl @@ -1,4 +1,4 @@ -@testitem "Warcraft" begin +@testset "Warcraft" begin using DecisionFocusedLearningBenchmarks using DecisionFocusedLearningBenchmarks.Utils: objective_value using Plots From a4fc7d6e1bb80cd2f2645f90a8eefae6be0cb287 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 14:59:39 +0100 Subject: [PATCH 5/8] try to fix tests in CI --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5322192..b57d6fd 100644 --- a/Project.toml +++ b/Project.toml @@ -59,7 +59,7 @@ LinearAlgebra = "1" Metalhead = "0.9.4" NPZ = "0.4" Plots = "1" -Printf = "1.11.0" +Printf = "1" Random = "1" Requires = "1.3.0" SCIP = "0.12" From 43004ddf501f2c1686c70906c36713d96a253e30 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 16:14:03 +0100 Subject: [PATCH 6/8] bttling with CI --- test/Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Project.toml b/test/Project.toml index 0df5f5b..2099f27 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -22,4 +22,6 @@ DecisionFocusedLearningBenchmarks = {path = ".."} [compat] Aqua = "0.8.14" +JET = "0.11" +JuliaFormatter = "1" Test = "1" From 02db8c97aa7669effca6c559b9ef1f2ee0694010 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 16:20:04 +0100 Subject: [PATCH 7/8] battling with CI --- test/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Project.toml b/test/Project.toml index 2099f27..a839e2e 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -22,6 +22,6 @@ DecisionFocusedLearningBenchmarks = {path = ".."} [compat] Aqua = "0.8.14" -JET = "0.11" +JET = "0.9, 0.10, 0.11" JuliaFormatter = "1" Test = "1" From 98a7c245080e2d4b69e358eb1e2ce2a67101c8e9 Mon Sep 17 00:00:00 2001 From: BatyLeo Date: Wed, 26 Nov 2025 16:44:51 +0100 Subject: [PATCH 8/8] fix coverage --- test/utils.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/utils.jl b/test/utils.jl index 2e487cf..f469db1 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -58,6 +58,8 @@ end @test occursin("y_true", s) @test occursin("instance=\"this is an instance\"", s) + @test propertynames(sample) == (:x, :θ, :y, :info, :instance) + # Create a dataset for testing N = 5 dataset = [random_sample() for _ in 1:N]