From 1896e1e98c0e04333eb9cde15dd98dd1fa84aad9 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Sat, 15 May 2021 00:03:36 +0200 Subject: [PATCH] More EP tests. --- src/ElementaryPath/ep.jl | 2 +- test/ep.jl | 159 ++++++++++++++++++++++++++++++--------- 2 files changed, 126 insertions(+), 35 deletions(-) diff --git a/src/ElementaryPath/ep.jl b/src/ElementaryPath/ep.jl index 84ae3cf..fac4067 100644 --- a/src/ElementaryPath/ep.jl +++ b/src/ElementaryPath/ep.jl @@ -57,7 +57,7 @@ end # Solution. struct ElementaryPathSolution{T, O <: CombinatorialObjective} <: - CombinatorialInstance + CombinatorialSolution instance::ElementaryPathInstance{T, O} variables::Vector{Edge{T}} solutions::Dict{T, Vector{Edge{T}}} diff --git a/test/ep.jl b/test/ep.jl index 00f9e15..7e121dd 100644 --- a/test/ep.jl +++ b/test/ep.jl @@ -1,18 +1,4 @@ @testset "ElementaryPath" begin - @testset "Copying" begin - g = path_digraph(3) - add_edge!(g, 2, 1) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 3.0, Edge(2, 1) => 25.0) - - i = ElementaryPathInstance(g, rewards, 1, 3) - i2 = copy(i) - @test i.graph == i2.graph - @test i.rewards == i2.rewards - @test i.src == i2.src - @test i.dst == i2.dst - @test i.objective == i2.objective - end - @testset "Transforming a solution" begin g = path_digraph(3) add_edge!(g, 1, 3) @@ -57,32 +43,31 @@ @testset "Elementary paths" begin @testset "Interface" begin + g = path_digraph(3) + rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) + + @testset "Global interface" begin + i = ElementaryPathInstance(g, rewards, 1, 2) + @test objective(i) == Maximise() + end + @testset "Source not in graph" begin - g = path_digraph(3) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) @test_throws ErrorException ElementaryPathInstance(g, rewards, 50, 2) end @testset "Destination not in graph" begin - g = path_digraph(3) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) @test_throws ErrorException ElementaryPathInstance(g, rewards, 1, 50) end @testset "Source is destination" begin - g = path_digraph(3) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) @test_throws ErrorException ElementaryPathInstance(g, rewards, 1, 1) end @testset "Source is destination" begin - g = path_digraph(3) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) @test_throws ErrorException ElementaryPathInstance(g, rewards, 1, 1) end @testset "An edge has a reward but is not in the graph" begin - g = path_digraph(3) rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0, Edge(1, 3) => 1.0) @test_throws ErrorException ElementaryPathInstance(g, rewards, 1, 2) end @@ -102,6 +87,56 @@ "The graph contains a positive-cost cycle around edge 2 -> 1.", ) solve(i, BellmanFordAlgorithm()) end + + @testset "Copy" begin + g = path_digraph(3) + add_edge!(g, 2, 1) + rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 3.0, Edge(2, 1) => 25.0) + + i = ElementaryPathInstance(g, rewards, 1, 3) + i2 = copy(i) + @test i.graph == i2.graph + @test i.rewards == i2.rewards + @test i.src == i2.src + @test i.dst == i2.dst + @test i.objective == i2.objective + end + + @testset "Approximation" begin + i = ElementaryPathInstance(g, rewards, 1, 3) + + @test approximation_ratio(i, DynamicProgramming()) == 1.0 + @test approximation_term(i, DynamicProgramming()) == 0.0 + + @test approximation_ratio(i, DefaultLinearFormulation(Cbc.Optimizer)) == 1.0 + @test approximation_term(i, DefaultLinearFormulation(Cbc.Optimizer)) == 0.0 + end + + @testset "Make solution" begin + i = ElementaryPathInstance(g, rewards, 1, 3) + d = Dict(Edge(1, 3) => 0.4, Edge(1, 2) => 0.8, Edge(2, 3) => 0.7) + s = make_solution(i, d) + + @test s.instance == i + @test length(s.variables) == 2 + @test Edge(1, 3) ∉ s.variables + @test Edge(1, 2) ∈ s.variables + @test Edge(2, 3) ∈ s.variables + end + + @testset "Solution helpers" begin + i = ElementaryPathInstance(g, rewards, 1, 3) + + # Infeasible solution. + s = ElementaryPathSolution(i, Edge{Int}[]) + @test value(s) == -Inf + + # Valid solution. + d = Dict(Edge(1, 3) => 0.4, Edge(1, 2) => 0.8, Edge(2, 3) => 0.7) + s = make_solution(i, d) + + @test value(s) == 4 + end end @testset "Basic" begin @@ -163,23 +198,79 @@ @testset "Budgeted elementary paths" begin @testset "Interface" begin + graph = path_digraph(3) + rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) + weights = Dict(Edge(1, 2) => 1, Edge(2, 3) => 1) + + @testset "Global interface" begin + i = MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), weights) + @test objective(i) == Maximise() + end + @testset "More edges have rewards than weights" begin g = path_digraph(4) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0, Edge(4, 1) => 1.0) - weights = Dict(Edge(1, 2) => 1, Edge(2, 3) => 1) - @test_throws ErrorException MinimumBudget(ElementaryPathInstance(g, rewards, 1, 2), weights) + r = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0, Edge(4, 1) => 1.0) + w = Dict(Edge(1, 2) => 1, Edge(2, 3) => 1) + @test_throws ErrorException MinimumBudget(ElementaryPathInstance(g, r, 1, 2), w) end @testset "Negative weight or budget" begin - g = path_digraph(3) - rewards = Dict(Edge(1, 2) => 1.0, Edge(2, 3) => 1.0) - weights = Dict(Edge(1, 2) => 1, Edge(2, 3) => 1) - @test_throws ErrorException MinimumBudget(ElementaryPathInstance(g, rewards, 1, 2), weights, -1) + w = Dict(Edge(1, 2) => 1, Edge(2, 3) => 1) + @test_throws ErrorException MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), w, -1) + + w[Edge(4, 2)] = 1 + @test_throws ErrorException MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), w) + w[Edge(4, 2)] = -1 + @test_throws ErrorException MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), w) + end + + @testset "Copy" begin + i = MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), weights) + i2 = copy(i) + @test i.weights == i2.weights + @test i.min_budget == i2.min_budget + @test i.compute_all_values == i2.compute_all_values + @test i.instance.graph == i2.instance.graph + @test i.instance.rewards == i2.instance.rewards + @test i.instance.src == i2.instance.src + @test i.instance.dst == i2.instance.dst + @test i.instance.objective == i2.instance.objective + end + + @testset "Approximation" begin + i = MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), weights) + + @test approximation_ratio(i, DynamicProgramming()) == 1.0 + @test approximation_term(i, DynamicProgramming()) == 0.0 + + @test approximation_ratio(i, DefaultLinearFormulation(Cbc.Optimizer)) == 1.0 + @test approximation_term(i, DefaultLinearFormulation(Cbc.Optimizer)) == 0.0 + end + + @testset "Make solution" begin + i = MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), weights) + d = Dict(Edge(1, 3) => 0.4, Edge(1, 2) => 0.8, Edge(2, 3) => 0.7) + s = make_solution(i, d) + + @test s.instance == i + @test length(s.variables) == 2 + @test Edge(1, 3) ∉ s.variables + @test Edge(1, 2) ∈ s.variables + @test Edge(2, 3) ∈ s.variables + end + + @testset "Solution helpers" begin + i = MinimumBudget(ElementaryPathInstance(graph, rewards, 1, 2), weights) + + # Infeasible solution. + s = BudgetedElementaryPathSolution(i, Edge{Int}[]) + @test value(s) == -Inf + + # Valid solution. + d = Dict(Edge(1, 3) => 0.4, Edge(1, 2) => 0.8, Edge(2, 3) => 0.7) + s = make_solution(i, d) - weights[Edge(4, 2)] = 1 - @test_throws ErrorException MinimumBudget(ElementaryPathInstance(g, rewards, 1, 2), weights) - weights[Edge(4, 2)] = -1 - @test_throws ErrorException MinimumBudget(ElementaryPathInstance(g, rewards, 1, 2), weights) + @test value(s) == 2 end end