@@ -7,6 +7,8 @@ It currently only has tools for solving two player repeated
7
7
games, but could be extended to do more with some effort.
8
8
=#
9
9
10
+ using Polyhedra
11
+
10
12
"""
11
13
RepeatedGame{N,T}
12
14
@@ -525,3 +527,185 @@ function outerapproximation(
525
527
526
528
return vertices
527
529
end
530
+
531
+ """
532
+ AS(rpd; maxiter=1000, plib=default_library(2, Float64), tol=1e-5, u=nothing)
533
+
534
+ Using AS algorithm to compute the set of payoff pairs of all pure-strategy
535
+ subgame-perfect equilibria with public randomization for any repeated
536
+ two-player games with perfect monitoring and discounting, following
537
+ Abreu and Sannikov (2014).
538
+
539
+ # Arguments
540
+
541
+ - `rpd::RepeatedGame{2, T}` : Two player repeated game with T<:Real.
542
+ - `maxiter::Integer` : Maximum number of iterations.
543
+ - `plib`: Allows users to choose a particular package for the geometry
544
+ computations.
545
+ (See [Polyhedra.jl](https://github.com/JuliaPolyhedra/Polyhedra.jl)
546
+ docs for more info). By default, it chooses to use SimplePolyhedraLibrary.
547
+ - `tol::Float64` : Tolerance in differences of set.
548
+ - `u` : The punishment payoff pair if any player deviates. In default,
549
+ we use minimax payoff pair. If there is better guess, you can specify it
550
+ by passing a `Vector` with length 2.
551
+
552
+ # Returns
553
+
554
+ - `::Matrix{T}` : Vertices of the set of payoff pairs.
555
+ """
556
+ function AS (rpd:: RepeatedGame{2} ; maxiter:: Integer = 1000 ,
557
+ plib= default_library (2 , Float64), tol:: Float64 = 1e-5 ,
558
+ u:: Union{AbstractVector{TU}, Nothing} = nothing ) where {TU}
559
+
560
+ lib = similar_library (plib, 2 , Float64)
561
+
562
+ # Initialize W0 with each entries of payoff bimatrix
563
+ v_old = _payoff_points (Float64, rpd. sg)
564
+
565
+ if isnothing (u)
566
+ u = Float64[minimum (rpd. sg. players[1 ]. payoff_array),
567
+ minimum (rpd. sg. players[2 ]. payoff_array)]
568
+ else
569
+ u = convert (Vector{Float64}, u)
570
+ end
571
+
572
+ # create VRepresentation and Polyhedron and get rid of redundant vertices
573
+ p = polyhedron (vrep (v_old), lib)
574
+ removevredundancy! (p)
575
+ H = hrep (p)
576
+
577
+ # calculate the best deviation gains
578
+ # normalize with (1-delta)/delta
579
+ best_dev_gains1, best_dev_gains2 = (1 - rpd. delta)/ rpd. delta .* _best_dev_gains (rpd. sg)
580
+
581
+ for iter = 1 : maxiter
582
+
583
+ v_new = Float64[] # to store new vertices
584
+ # step 1
585
+ for a2 in 1 : rpd. sg. nums_actions[2 ]
586
+ for a1 in 1 : rpd. sg. nums_actions[1 ]
587
+ payoff1 = rpd. sg. players[1 ]. payoff_array[a1, a2]
588
+ payoff2 = rpd. sg. players[2 ]. payoff_array[a2, a1]
589
+ IC1 = u[1 ] + best_dev_gains1[a1, a2]
590
+ IC2 = u[2 ] + best_dev_gains2[a2, a1]
591
+
592
+ # check if the payoff point is interior
593
+ # first check if it satisifies IC
594
+ if all ([payoff1, payoff2] .> [IC1, IC2])
595
+ # then check if it is in the polyhedron
596
+ if [payoff1, payoff2] in H
597
+ push! (v_new, payoff1, payoff2)
598
+ end
599
+ end
600
+
601
+ # find out the intersections of polyhedron and IC boundaries
602
+ p_IC = polyhedron (hrep (- Matrix {Float64} (I, 2 , 2 ), - [IC1, IC2]), lib)
603
+ p_inter = intersect (p_IC, p)
604
+ Vmat = MixedMatVRep (vrep (p_inter)). V
605
+ for i in 1 : size (Vmat, 1 )
606
+ if Vmat[i, 1 ] ≈ IC1 || Vmat[i, 2 ] ≈ IC2
607
+ push! (v_new, (rpd. delta * Vmat[i, :] +
608
+ (1 - rpd. delta) * [payoff1, payoff2]). .. )
609
+ end
610
+ end
611
+ end
612
+ end
613
+
614
+ v_new = reshape (v_new, 2 , :)'
615
+
616
+ # get rid of redundant points
617
+ p = polyhedron (vrep (v_new), lib)
618
+ removevredundancy! (p)
619
+
620
+ # check if it's converged
621
+ # Use deduplicated vertices for convergence check
622
+ v_dedup = MixedMatVRep (vrep (p)). V
623
+ # first check if the numbers of vertices are the same
624
+ if size (v_dedup) == size (v_old)
625
+ # then check the euclidean distance
626
+ if norm (v_dedup- v_old) < tol
627
+ println (" converged in $(iter) iterations" )
628
+ break
629
+ end
630
+ end
631
+
632
+ # check if maxiter is reached
633
+ if iter == maxiter
634
+ @warn " Maximum Iteration Reached"
635
+ end
636
+
637
+ v_old = v_dedup
638
+ H = hrep (p)
639
+
640
+ # step 2
641
+ # update u
642
+ u_ = [minimum (v_new[:, 1 ]),
643
+ minimum (v_new[:, 2 ])]
644
+ if any (u_ .> u)
645
+ u = u_
646
+ end
647
+
648
+ end
649
+
650
+ # To ensure the return is Matrix{Float64}
651
+ vr = vrep (p)
652
+ pts = points (vr)
653
+ vertices = Matrix {Float64} (undef, (length (pts), 2 ))
654
+ for (i, pt) in enumerate (pts)
655
+ vertices[i, :] = pt
656
+ end
657
+
658
+ return vertices
659
+ end
660
+
661
+ """
662
+ _payoff_points(::Type{T}, g)
663
+
664
+ Return a matrix with each row being a payoff pair point in the two dimensional
665
+ space.
666
+
667
+ # Arguments
668
+
669
+ - `g::NormalFormGame{2}` : Two-player NormalFormGame.
670
+
671
+ # Returns
672
+
673
+ - `v::Matrix{T}` : Matrix with size n by 2, where n is the number of
674
+ action profiles. Each row corresponds to one payoff pair.
675
+ """
676
+ function _payoff_points (:: Type{T} , g:: NormalFormGame{2} ) where T
677
+
678
+ nums_action_profiles = prod (g. nums_actions)
679
+ v = Matrix {T} (undef, nums_action_profiles, 2 )
680
+ v[:, 1 ] = reshape (g. players[1 ]. payoff_array, nums_action_profiles)
681
+ v[:, 2 ] = reshape (g. players[2 ]. payoff_array' , nums_action_profiles)
682
+
683
+ return v
684
+ end
685
+
686
+ """
687
+ _best_dev_gains(g)
688
+
689
+ Calculate the payoff gains from deviating from the current action to
690
+ the best response for each player.
691
+
692
+ # Arguments
693
+
694
+ - `g::NormalFormGame{2, T}` : Two-player NormalFormGame.
695
+
696
+ # Returns
697
+
698
+ - `::Tuple{Matrix{T}, Matrix{T}}` : Tuple of best deviating gain matrices
699
+ for two players. For example, for the first matrix `best_dev_gains1`,
700
+ `best_dev_gains1[i, j]` is the payoff gain for player 1 for deviating
701
+ to the best response from ith action given player 2 choosing jth action.
702
+ """
703
+ function _best_dev_gains (g:: NormalFormGame{2, T} ) where T
704
+
705
+ best_dev_gains1 = (maximum (g. players[1 ]. payoff_array; dims= 1 )
706
+ .- g. players[1 ]. payoff_array)
707
+ best_dev_gains2 = (maximum (g. players[2 ]. payoff_array; dims= 1 )
708
+ .- g. players[2 ]. payoff_array)
709
+
710
+ return best_dev_gains1, best_dev_gains2
711
+ end
0 commit comments