diff --git a/src/Games.jl b/src/Games.jl index ec377689..4fbe3f35 100644 --- a/src/Games.jl +++ b/src/Games.jl @@ -68,6 +68,7 @@ include("repeated_game.jl") include("random.jl") include("support_enumeration.jl") include("generators/Generators.jl") +include("vertex_enumeration.jl") export # Types @@ -101,6 +102,9 @@ export random_pure_actions, random_mixed_actions, # Support Enumeration - support_enumeration, support_enumeration_task + support_enumeration, support_enumeration_task, + + # Vertex Enumeration + vertex_enumeration, vertex_enumeration_task end # module diff --git a/src/vertex_enumeration.jl b/src/vertex_enumeration.jl new file mode 100644 index 00000000..1abd50b2 --- /dev/null +++ b/src/vertex_enumeration.jl @@ -0,0 +1,102 @@ +function vertex_enumeration(g::NormalFormGame{2}; + plib=SimplePolyhedraLibrary{Float64}()) + + c = Channel(0) + task = vertex_enumeration_task(c, g, plib) + bind(c, task) + schedule(task) + NEs = Tuple{Vector{Real}, Vector{Real}}[NE for NE in c] + + return NEs + +end + +function vertex_enumeration_task(c::Channel, + g::NormalFormGame{2}, + plib) + + task = Task( + () -> _vertex_enumeration_producer(c, g, plib) + ) + + return task + +end + +function _vertex_enumeration_producer(c::Channel, + g::NormalFormGame{2, T}, + plib) + + n, m = size(g.players[1].payoff_array) + + # create Representation for player 1 + p1, p2 = construction_BRP(g, plib) + V1 = points(p1) + simplex1 = [] + for pidx in eachindex(points(p1)) + push!(simplex1, [idx.value for idx in incidenthalfspaceindices(p1, pidx)]) + end + + V2 = points(p2) + simplex2 = [] + for pidx in eachindex(points(p2)) + push!(simplex2, [idx.value for idx in incidenthalfspaceindices(p2, pidx)]) + end + + ZERO_LABELING_BITS = (1 << (n+m)) - (1 << m) + COMPLETE_LABELING_BITS = 1 << (n+m) - 1 + + for (i, v1) in enumerate(V1) + labelings_bits1 = labelings_bits(simplex1[i]) + if labelings_bits1 == ZERO_LABELING_BITS + continue + end + for (j, v2) in enumerate(V2) + labelings_bits2 = labelings_bits(simplex2[j]) + if xor(labelings_bits1, labelings_bits2) == COMPLETE_LABELING_BITS + put!(c, (_get_mixed_action(v1), + _get_mixed_action(v2))) + end + end + end + +end + +function construction_BRP(g::NormalFormGame{2, T}, plib) + + n, m = size(g.players[1].payoff_array) + + # create Representation for player 1 + C = Matrix{T}(n+m, n) + C[1:m, :] = g.players[2].payoff_array + C[m+1:end, :] = -eye(T, n) + b1 = Vector{T}(n+m) + b1[1:m] = one(T) + b1[m+1:end] = zero(T) + H1 = hrep(C, b1) + p1 = polyhedron(H1, plib) + + # create Representation for player 2 + D = Matrix{T}(n+m, m) + D[1:m, :] = -eye(T, m) + D[m+1:end, :] = g.players[1].payoff_array + b2 = Vector{T}(n+m) + b2[1:m] = zero(T) + b2[m+1:end] = one(T) + H2 = hrep(D, b2) + p2 = polyhedron(H2, plib) + + return p1, p2 +end + +function labelings_bits(inchalfindices::Vector{T}) where T <: Integer + b = 0 + for i in inchalfindices + b += 1 << (i-1) + end + return b +end + +function _get_mixed_action(a::Vector{T}) + return a ./ sum(a) +end diff --git a/test/runtests.jl b/test/runtests.jl index b3dfdbfc..885a3c3f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,5 +6,6 @@ include("test_repeated_game.jl") include("test_normal_form_game.jl") include("test_random.jl") include("test_support_enumeration.jl") +include("test_vertex_enumeration.jl") include("generators/runtests.jl") diff --git a/test/test_vertex_enumeration.jl b/test/test_vertex_enumeration.jl new file mode 100644 index 00000000..d0ec655b --- /dev/null +++ b/test/test_vertex_enumeration.jl @@ -0,0 +1,91 @@ +@testset "Testing vertex Enumeration" begin + + @testset "test 3 by 2 non-degenerate normal form game(Float)" begin + g = NormalFormGame(Player([3.0 3.0; 2.0 5.0; 0.0 6.0]), + Player([3.0 2.0 3.0; 2.0 6.0 1.0])) + NEs = [([0.0, 1/3, 2/3], [1/3, 2/3]), + ([0.8, 0.2, 0.0], [2/3, 1/3]), + ([1.0, 0.0, 0.0], [1.0, 0.0])] + + for (actions_computed, actions) in zip(NEs, vertex_enumeration(g)) + for (action_computed, action) in zip(actions_computed, actions) + @test action_computed ≈ action + @test eltype(action_computed) <: AbstractFloat + end + end + end + + # @testset "test 3 by 2 non-degenerate normal form game(Int)" begin + # g = NormalFormGame(Player([3 3; 2 5; 0 6]), + # Player([3 2 3; 2 6 1])) + # NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]), + # ([0.8, 0.2, 0.0], [2/3, 1/3]), + # ([0.0, 1/3, 2/3], [1/3, 2/3])] + + # for (actions_computed, actions) in zip(NEs, support_enumeration(g)) + # for (action_computed, action) in zip(actions_computed, actions) + # @test action_computed ≈ action + # @test eltype(action_computed) <: AbstractFloat + # end + # end + # end + + # @testset "test 3 by 2 non-degenerate normal form game(Rational)" begin + # g = NormalFormGame(Player([3//1 3//1; 2//1 5//1; 0//1 6//1]), + # Player([3//1 2//1 3//1; 2//1 6//1 1//1])) + # NEs = [([1//1, 0//1, 0//1], [1//1, 0//1]), + # ([4//5, 1//5, 0//1], [2//3, 1//3]), + # ([0//1, 1//3, 2//3], [1//3, 2//3])] + + # for (actions_computed, actions) in zip(NEs, support_enumeration(g)) + # for (action_computed, action) in zip(actions_computed, actions) + # @test action_computed ≈ action + # @test eltype(action_computed) <: Rational + # end + # end + # end + + @testset "test 3 by 2 degenerate normal form game(Float)" begin + g = NormalFormGame(Player([1.0 -1.0; -1.0 1.0; 0.0 0.0]), + Player([1.0 0.0 0.0; 0.0 0.0 0.0])) + NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]), + ([0.0, 1.0, 0.0], [0.0, 1.0])] + + for (actions_computed, actions) in zip(NEs, vertex_enumeration(g)) + for (action_computed, action) in zip(actions_computed, actions) + @test action_computed ≈ action + @test eltype(action_computed) <: AbstractFloat + end + end + end + + # @testset "test 3 by 2 degenerate normal form game(Int)" begin + # g = NormalFormGame(Player([1 -1; -1 1; 0 0]), + # Player([1 0 0; 0 0 0])) + # NEs = [([1.0, 0.0, 0.0], [1.0, 0.0]), + # ([0.0, 1.0, 0.0], [0.0, 1.0])] + + # for (actions_computed, actions) in zip(NEs, support_enumeration(g)) + # for (action_computed, action) in zip(actions_computed, actions) + # @test action_computed ≈ action + # @test eltype(action_computed) <: AbstractFloat + # end + # end + # end + + # @testset "test 3 by 2 degenerate normal form game(Rational)" begin + # g = NormalFormGame(Player([1//1 -1//1; -1//1 1//1; 0//1 0//1]), + # Player([1//1 0//1 0//1; 0//1 0//1 0//1])) + # NEs = [([1//1, 0//1, 0//1], [1//1, 0//1]), + # ([0//1, 1//1, 0//1], [0//1, 1//1])] + + # for (actions_computed, actions) in zip(NEs, support_enumeration(g)) + # for (action_computed, action) in zip(actions_computed, actions) + # @test action_computed ≈ action + # @test eltype(action_computed) <: Rational + # end + # end + # end + + +end