#16954: Game Theory: Build class for normal form games as well as ability to 
obtain
Nash equilibria
-------------------------------------+-------------------------------------
       Reporter:  vinceknight        |        Owner:
           Type:  enhancement        |       Status:  needs_work
       Priority:  major              |    Milestone:  sage-6.4
      Component:  game theory        |   Resolution:
       Keywords:  Game Theory,       |    Merged in:
  Normal Form Games                  |    Reviewers:  Karl-Dieter Crisman
        Authors:  Vince Knight,      |  Work issues:
  James Campbell                     |       Commit:
Report Upstream:  N/A                |  8775f12e2644129b2a790ad91f37b853a273d2b6
         Branch:                     |     Stopgaps:
  u/vinceknight/mainly_fixes_to_docs |
   Dependencies:                     |
-------------------------------------+-------------------------------------

Comment (by vinceknight):

 Replying to [comment:23 kcrisman]:

 Ok so I actually think you've found a bug (in your testing section) so I'm
 spending some time on that over the next couple of days.
 In the meantime I addressed the various other issues so thought I might as
 well push those now.

 > I don't think I'll get to everything in one comment, but assume that if
 I don't mention it it's either okay or I'm still getting to it.
 >
 > ----
 > > > Parser comments.
 > > >  * Minor - this could be made slightly more professional-sounding.
 Also, double ticks for code markup; maybe even {{{``'lrs'``}}} if it is a
 string, I'm not sure about this.
 > > > {{{
 > > >     At present the only parser included is the for `lrs` algorithm
 however
 > > >     this will be expanded to `gambit` soon.
 > > > }}}
 > >
 > > Have changed wording and also ticks but I'm not sure if it's quite
 what you meant.
 >
 > No.  What you changed it to is
 > {{{
 > #!diff
 > -    At present the only parser included is for 'lrs' algorithm however
 > -    this is actively being expanded to 'gambit'.
 > +    At present the only parser included is for `lrs` algorithm however
 > +    this is actively being expanded to `gambit`.
 > }}}
 > but what we need is for {{{`lrs`}}} or the previous {{{'lrs'}}} to
 become {{{``'lrs'``}}}.  Notice the double backticks (typeset as code) and
 the single quote because, well, it needs single quotes because it's a
 string (such as in the example {{{sage:
 matching_pennies.obtain_Nash(algorithm='lrs')}}}).  That said, it probably
 can be kept {{{'lrs'}}} in some contexts, I think it's just especially
 important in places like
 > {{{
 >     1. ``lrs`` (requires lrs)
 >     2. ``enumeration``
 > }}}
 > where not having the quotes could mean people will type in something
 nasty and wrong.

 Believe I have now addressed this.

 >
 > ----
 >
 > Here is a dumb comment - you have {{{between gambit and sage.}}} but
 then it should be {{{Sage}}}.  Trivial, I know, I apologize but I just
 happened to see it.

 Changed.

 >
 > > >  * `        LaTeX method shows nothing interesting for games with
 more players::` - then you should probably use the `...` like so:
 > > > {{{
 > > > \text{\texttt{<bound{ }method{ }NormalFormGame...[False,{ }False,{
 }False]{\char`\}}>}}
 > > > }}}
 > > >    or the like.
 > >
 > > Have gone with your suggestion.
 >
 > Yikes!  I see now I was assuming too much information.  What I meant by
 that is that the LaTeX method is fine, but that in ''doctesting'' it one
 can use three periods in a row to substitute for arbitrary sequences.  So
 keep the original LaTeX method, but add ... in the middle of it so that
 the doctest is actually legible and it's clear what it's testing for.

 I understand now :) Have fixed.

 Replying to [comment:24 kcrisman]:
 > > >  * Since `lrs` is an optional package, will any of the doctests in
 the parser file need to be marked optional?  I assume not, just checking.
 (Maybe the one using the subprocess stuff?)
 > >
 > > This was indeed an oversight on our part: have added the optional
 tests.
 >
 > Usually our practice is to mark ''only'' the doctest that would actually
 fail without the optional package.  (After all, testing the other stuff is
 worthwhile.)    Am I correct that only the stuff depending on {{{process =
 Popen(['nash', g1_name, g2_name], stdout=PIPE)}}} needs to be optional?

 One or two others needed to stay optional but yes this has been addressed.
 >
 >
 > ----
 >
 > > > > > algorithm='enumeration' or not? You have in one spot ``"support
 enumeration"`` which sounds horrible.
 > > > > Not too sure I understand. 'support enumeration' is the actual
 name of the algorithm which is what we need to pass as an argument (so
 have shorted to enumeration) like we could also pass lrs. Would
 support_enumeration be any better?
 > > > What I mean is that when you put something in quotes and double
 backticks, it looks like code.  You can call it support enumeration and
 still use `'enumeration'` as the algorithm keyword, but then you need to
 make it quite clear what the difference is.  I agree that shorter is
 better in this case.  No double backticks (as later in the doc), not a
 problem.
 > >
 > > Cool.
 > >
 > You ''still'' have {{{``enumeration``}}} instead of
 {{{``"enumeration"``}}} at one point, though you do have {{{``"lrs"``}}}
 correctly there.
 >

 Have fixed this.

 > ----
 >
 > {{{
 > #!diff
 > -    playing strategy `i` is given by (`(Ay)_i`)::
 > +    playing strategy `i` is given by the matrix/vector multiplication:
 > +    (`(Ay)_i`)::
 > }}}
 > And even after you move it, it is confusing.  Why a colon before the
 colons?  What is {{{i}}}?  I know but the reader may not.

 Have added more explenation.
 >
 > ----
 >
 > More trivialities:
 >  * Should {{{dictionary like interface}}} perhaps have a hyphen instead
 of a space?

 Done.

 >  * Similarly, {{{A game with 1 equilibria}}} - I think the singular is
 {{{equilibrium}}}.

 Done.

 >  * {{{initiating}}} probably should be {{{initializing}}}, unless this
 is secret society?

 Yup: done :)

 >  * The sentence
 > {{{
 > However, if one writes down a smaller number than the other, this
 smaller
 > }}}
 >    probably needs a blank line in front of it or the documentation will
 look like a humongous paragraph.  I think I mentioned this before.

 Apologies: done.

 >  * {{{At present the algorithms for the computation of equilibria only
 solve 2 player}}} - "solves"

 Unless I'm making a grammatical mistake (which I might well be) I don't
 think that's correct? 'The algorithms solve 2 player games'?

 > ----
 > {{{
 > sage: p += plot((A * vector([y, 1 - y]))[1], y, 0, 1, color='red',
 legend_label='$u_1(r_2, (y, 1-y))$')
 > }}}
 > but typically one shows the plot (or allows the user to) when using this
 to make a point.  Maybe just add {{{; p}}} to the end of that line.

 Have fixed (wasn't sure if tests would no longer pass but they do).

 > ----
 >
 > Thinking aloud:  Should
 > {{{
 > "Normal Form Game with the following utilities: {}"
 > }}}
 > instead be
 > {{{
 > "Normal Form Game with the following utilities:\n{}"
 > }}}
 > I don't know if Sage often has two-line string representations, though.

 I have left it as is for now as I haven't seen many two-line string
 representations.
 >
 > ----
 >
 > I'm a bit scared by the following.
 > {{{
 > #!diff
 > -        sage: g.obtain_Nash()
 > -        [[(1, 0, 0), (1, 0, 0)], [(0, 1, 0), (0, 1, 0)], [(0, 0, 1),
 (0, 0, 1)]]
 > +        sage: g.obtain_Nash(algorithm='enumeration')
 > +        [[(1, 0, 0), (1, 0, 0)]]
 > }}}
 > Shouldn't lrs and enumeration give the same Nash equilibria?

 This might be part of the potential bug and I need to address. In a sense
 it's ok because the algorithm 'doesn't make sense for degenerate games'
 but I think something is wrong with pure strategy NE which enumeration
 should find.
 >
 > ----
 >
 > > >  * Minor but made the code harder to understand:
 > > > {{{
 > > > for p2_strategy in range(self.players[1].num_strategies):
 > > > for i in range(self.players[0].num_strategies):
 > > > }}}
 > > >    even though they fill exactly the same role.  Same with
 > > > {{{
 > > > for p2_strategy in range(len(p2_support)):
 > > > for j in range(len(p1_support)):
 > > > }}}
 > >
 > > Have tried to clarify as suggested: let me know if it still isn't
 quite right.
 > >
 > Hmm, I kind of meant the opposite - shouldn't `j` be a `p1_strategy` and
 so forth?  Perhaps I'm misunderstanding this.  For stuff like this,
 variable names that mean something are crucial.  Plus, the new code still
 mixes letters and `strategy`s.

 I agree, in fact I think I've gotten myself confused with what you were
 asking now. It's clear now and will mention more in comment later on.

 >
 > ----
 >
 > > >  * These mixed strategies
 > > >    seem strange, but I assume correct - can you confirm they are
 ''exact'' answers?
 > >
 > > They were in fact incorrect but have now fixed: answers are definitely
 correct now.
 > >
 > Well, I have no independent way to check that but hopefully yes!
 >
 > ----
 > > >  * I am scared of the syntax `sage: f.add_player(2)`.  Am I adding
 the player named `2`?  Am I adding two new players?  And why do I do this
 ''twice'' in a row?  Using "gambit syntax" is nice but would need to be
 fully explained.  By the way, this is the kind of thing that really does
 do better in the "top matter".
 > >
 > > This is adding a player with 2 strategies. Doing it twice in a row is
 because we're adding two players. Tried to think of a way to make it more
 verbose but couldn't come to a better conclusion than this but very open
 to any suggestions.
 > >
 > Maybe at the very least in situations like
 > {{{
 >         sage: g = NormalFormGame()
 >         sage: g.add_player(2)
 >         sage: g.add_player(2)
 >         sage: g.add_player(2)  # Creating a game with three players
 > }}}
 > you could, early in the docs, say instead
 > {{{
 >         sage: g = NormalFormGame()
 >         sage: g.add_player(2)  # adding first player with two strategies
 >         sage: g.add_player(2)  # adding second player with two
 strategies
 >         sage: g.add_player(2)  # Creating a game with three players
 > }}}
 > or something along those lines.

 Have added as you suggest throughout (for all similar examples).

 >
 > ----
 > > >  * I would have expected this to list the bimatrix entries.  Instead
 I get a very boring list of the possible pure strategies.
 > > > {{{
 > > >             sage: for key in prisoners_dilemma:
 > > >             ....:     print key
 > > > }}}
 > >
 > > This goes back to wanting a game to behave like a dictionary (which in
 turn goes back to wanting gambit like syntax): have changed it though so
 it prints things out a bit nicer.
 > >
 > Well, it doesn't print things nicer, just the doctest does.  So I guess
 what you are saying is that the keys must be pure strategy pairs to comply
 with gambit?

 Yes.

 >
 > ----
 > > >  * In this same example, you have it that the best strategy is to
 say the stuff is worth two, but you have
 > > > {{{
 > > > [[(1, 0, 0, 0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 0, 0, 0, 0)]]
 > > > }}}
 > > >    Doubtless this corresponds to 100% choosing the first strategy,
 but that is not clear since the way the problem is stated makes it sound
 like there are 99 strategies or something.
 > >
 > > Have added another sentence highlighting that this is a 'smaller'
 example.
 > >
 > I still don't understand, so for sure the reader won't.  You have to
 tell the reader what the output ''means'', since they do not see the
 number TWO in the output and if they are not a programmer they won't
 recognize it from the matrices.  I think here you understand the code so
 well that it is just obvious to you.

 I've added more explenation.

 >
 > ----
 >
 > > >  * Should it be `obtain_Nash` or `obtain_nash`?
 > >
 > > Not entirely sure: I felt a bit better going with `Nash`... I would
 like to keep Nash if that's not a problem.
 > >
 > Given that Sage usually lowercases even proper names most of the time
 (such as Schlegel projection or Conway polynomials or Schonheim bound) I
 think we need to go with `obtain_nash`.  Especially because in programming
 there are kind of two conventions - either `camelCase` or `camel_case`,
 but not `camel_Case` and that is what it looks like, even if that is not
 the intent.

 Have fixed throughout.

 >
 >
 > ----
 >
 > Getting closer...


 >
 > ----
 > More to come.
 Replying to [comment:25 kcrisman]:
 > >> Returning [] wouldn't be quite right from the mathematical point of
 view (empty set as opposed to a set that doesn't exist): False seemed to
 make more sense to us. Obviously wouldn't make a different from the point
 of view of the actual algorithm so let me know if you feel strongly about
 it.
 > > My suggestion would be to return None; this is what we do for crystals
 for a similar situation. AFAIK, there is no standard (or other similar
 case for that matter) in Sage about this.
 > Hmm, maybe that is better indeed.

 Have gone with `None`.

 Replying to [comment:26 kcrisman]
 > More random bits.  Looking very good, though.
 >
 > ----
 >
 > {{{
 > +Here is a game being constructed using gambit syntax (note that a
 > +``NormalFormGame`` object acts like a dictionary with strategy tuples
 as
 > +keys and payoffs as their values)::
 > }}}
 > I think that if you make it clear that they are '''pure''' strategy
 tuples I will be a lot happier with this syntax.
 >

 Have clarified this.

 > ----
 >
 > {{{
 > Proceedings of the national academy of sciences
 > }}}
 > I think PNAS is capitalized, but I could be wrong.  Interesting place
 for a math paper, incidentally, you don't get too many in there!

 Have capitalized.

 >
 > ----
 >
 > I just have to point out about the RPSLS
 > > The topic of this article may not meet Wikipedia's general notability
 guideline.
 > :-)  But maybe then http://www.samkass.com/theories/RPSSL.html is a
 better, if more brittle, reference.  At least it does have the CC license,
 phew!  Otherwise maybe you couldn't use it ;-)

 Changed the reference.

 Replying to [comment:27 kcrisman]:
 > Okay, back to the algorithm.  Here was one thing I definitely was
 confused by before.
 > > Equivalently we can consider consecutive rows of A (instead of all
 pairs of strategies).
 > Huh?  I distinctly remember that part of the code, but I don't see it in
 the references.  So maybe this is just some very basic linear systems
 thing I am not remembering, but if you can clear it up for me I'd be
 grateful.

 This isn't documented in the references but is a small linear system
 trick.
 I haven't changed the docs but here's the idea (if you think it needs to
 go in just let me know):

 We want to find a strategy such that u(x,s) = u for all s for some u: ie
 all the pure strategies must have the same utility against that particular
 mixed strategy.
 In practice we don't actually care about the value of u so to set up the
 linear system it's just as easy to say that u(x,s_1)=u(x,s_2) for all s_1,
 s_2.
 What we're doing here in our algorithm is creating a chain of equality
 where we have consecutive pairs of strategies: u(x,s_1)=u(x,s_2),
 u(x,s_2)=u(x,s_3), ... u(x,x_m)=u(x,s_1) (the last equality there isn't
 really needed).
 Does that help? Do you think more is needed in the docs? (If you do, I'm
 happy to put something in but feel that it's perhaps not needed - happy to
 be wrong).


 >
 > Along these lines, you then need to change
 > {{{
 > where `A` has been modified to only contain the row corresponding to
 `S(\rho_1)`
 > }}}
 > to
 > {{{
 > where `A` has been modified to only contain the rows corresponding to
 `S(\rho_1)`)
 > }}}
 > (since there is a necessary parenthesis (oh, WHY do the Brits call them
 brackets when brackets are square? (truly, I don't know))).

 Fixed.

 >
 > > > >  * Minor but made the code harder to understand:
 > > > Have tried to clarify as suggested: let me know if it still isn't
 quite right.
 > > Hmm, I kind of meant the opposite - shouldn't `j` be a `p1_strategy`
 and so forth?  Perhaps I'm misunderstanding this.  For stuff like this,
 variable names that mean something are crucial.  Plus, the new code still
 mixes letters and `strategy`s.
 > Oh, ''now'' I get it (thanks to your improved exposition and CUP's kind
 provision of [http://blog.oddhead.com/2007/09/17/computational-aspects-of-
 prediction-markets-book-chapter-and-extended-bibliography/ a free download
 of AGT]).  These are maybe to distinguish between using ''all'' strategies
 and just the ones in the truncated list of ones in the support?
 >
 > Or if not, then I stick with my original request.

 The equations of are linear system correspond to strategy pairs so I've
 now changed this to be more verbose.

 >
 > By the way, you can just move the comments like this
 > {{{
 > # Coefficients of linear system that ensure indifference between two
 consecutive strategies of the support of p1
 > }}}
 > to the line immediately preceding, to ensure good readability.

 Done.

 >
 > I note that in the built doc for some reason there is documentation for
 `sage.game_theory.normal_form_game.PIPE`.  I couldn't tell you how to
 remove it but it does seem odd.

 I have no idea what to do about this and/or how it has come about... Will
 continue to investigate unless someone has a good suggestion?

 >
 > ----
 >
 > So now one can move to the testing phase!
 Replying to [comment:29 kcrisman]:
 > Sorry for the mountain, but getting it all done at once is better for my
 workflow...
 >
 > I get the following output in the notebook.
 > {{{
 > Normal Form Game with the following                     utilities: {(0,
 > 1): [1, 1], (1, 0): [0, 0], (0, 0): [3, 2], (1, 1): [2, 3]}
 > }}}
 > Note the very long space.  I think this is because of
 > {{{
 > +        return "Normal Form Game with the following \
 > +                    utilities: {}".format(self.utilities)
 > }}}
 > and so it kept all those spaces, since this is a string.

 Was trying to stick to PEP8 but looking at that it was very clumsily done:
 have addressed now.

 Replying to [comment:30 kcrisman]:
 > Okay, now testing commences.  I am primarily testing whether the two
 algorithms give the same output.  I am also trying to compare output with
 various third-party online software, just to make sure no horrible goofs.
 >
 > ----
 >
 > {{{
 > sage: N = NormalFormGame([matrix(2,[0,-1,-2,-1]),matrix(2,[1,0,0,2])])
 > sage: N.obtain_Nash(algorithm='enumeration')
 > [[(1, 0), (1, 0)]]
 > sage: N.obtain_Nash(algorithm='lrs')
 > [[(2/3, 1/3), (0, 1)], [(0, 1), (0, 1)], [(1, 0), (1, 0)]]
 > }}}
 > This one is degenerate because one column of the row player's matrix is
 all the same, but I wonder why enumeration doesn't at least get the two
 ''pure'' equilibria?   Is it possible to warn about degenerate ones in the
 code, or would that indeed be too hard?  Maybe that should be implemented
 at the algorithm level... but at least the situation where a column of the
 row player or row of the column player have two of the same number could
 be checked for.  So far, that is all the discrepancies I get.
 >
 > Or maybe that's for an upgrade ticket!
 >
 > ----
 >
 > Here is something I don't like.  In all cases (including when gambit is
 added), the return type from `obtain_nash/obtain_Nash` should be the same.
 Well, it sort of is.
 > {{{
 > sage: N = NormalFormGame([matrix(2,[2,-3,0,-1]),matrix(2,[0,1,2,-2])])
 > sage: N.obtain_Nash(algorithm='enumeration')
 > [[(4/5, 1/5), (1/2, 1/2)]]
 > sage: N.obtain_Nash(algorithm='lrs')
 > [[(4/5, 1/5), (1/2, 1/2)]]
 > sage: N.obtain_Nash(algorithm='enumeration') ==
 N.obtain_Nash(algorithm='lrs')
 > False
 > sage: type(N.obtain_Nash(algorithm='lrs')[0][0])
 > <type 'tuple'>
 > sage: type(N.obtain_Nash(algorithm='enumeration')[0][0])
 > <type 'sage.modules.vector_rational_dense.Vector_rational_dense'>
 > }}}
 > Umm.  This is because you solved a linear system, and that returns a
 vector.  However, I'm not sure what the right return type is - because
 with gambit presumably the return will be a non-rational vector or
 something.  Maybe better to return all as tuples?  Or all as vectors?
 >
 > An argument for vectors is to easily ''always'' do this
 > {{{
 > sage:
 
N.obtain_Nash(algorithm='enumeration')[0][0]*matrix(2,[2,-3,0,-1])*N.obtain_Nash(algorithm='enumeration')[0][1]
 > -1/2
 > sage:
 
vector(N.obtain_Nash(algorithm='lrs')[0][0])*matrix(2,[2,-3,0,-1])*vector(N.obtain_Nash(algorithm='lrs')[0][1])
 > }}}
 > without having to put things in vectors.
 >
 > An argument against is that one might not ''want'' the additional
 structure of a vector - after all, a (mixed) strategy is really more of a
 discrete probability distribution.
 >
 > But the return types should be as close as possible to each other,
 anyway.

 I agree: I'm likely to go with tuples but will think about it.

 >
 > ----
 > {{{
 > sage: N =
 NormalFormGame([matrix(2,[7,-8,-4,-8,7,0]),matrix(2,[-9,-1,-8,3,2,3])])
 > sage: N.obtain_Nash(algorithm='lrs')[[(0, 1), (0, 0, 1)]]
 > sage: N.obtain_Nash(algorithm='enumeration')
 > []
 > }}}
 > Okay, I get it, degenerate.  But why doesn't enumeration at least return
 ''an'' equilibrium?
 >
 > ----
 >
 > Here's another one.  I am baffled by these outputs (just showing the
 matrices and results).
 > {{{
 > [-7 -5  5]
 > [ 5  5  3]
 > [ 1 -6  1]
 > [ -9   7   9]
 > [  6  -2  -3]
 > [ -4   6 -10]
 > [[(0, 1, 0), (1, 0, 0)], [(1/3, 2/3, 0), (1/7, 0, 6/7)], [(1, 0, 0), (0,
 0, 1)]]  #enum
 > [[(0, 1, 0), (0, 0, 1)], [(1/3, 2/3, 0), (0, 1/6, 5/6)], [(1, 0, 0),
 (1/7, 0, 6/7)]]  #lrs
 > }}}
 > In particular, what is up with `[(0, 1, 0), (0, 0, 1)]`?  If row plays
 middle and column plays right, then row should move to top and/or column
 should move to left or middle.   I buy `[(0, 1, 0), (1, 0, 0)]`.  Am I
 reading the matrices wrong now that they are 3x3?    Note that the
 [http://banach.lse.ac.uk/ LSE online version of lrs online] gives most of
 these equilibria, but not `[(0, 1, 0), (0, 0, 1)]`.  Hopefully I'm just
 interpreting something wrong, though.
 >
 > ----
 >
 > Otherwise I am pretty happy, this really does seem to give good output
 in most cases and the algorithms agree where desired.  Lots of very minor
 things to fix above but this will be a robust addition to Sage, thanks for
 working so hard on it.

 As I said above I think you have actually found a bug in the algorithm
 here. My hunch is that it's something to do with pruning so will take a
 look at that: your examples are going to serve as tests and will be
 incorporated in. **Thank you very much for spotting this.**

--
Ticket URL: <http://trac.sagemath.org/ticket/16954#comment:35>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/d/optout.

Reply via email to