From cb10ed77c952f3a40a57840a7473d33e708817c1 Mon Sep 17 00:00:00 2001 From: Ted Turocy Date: Thu, 5 Oct 2023 14:00:01 +0100 Subject: [PATCH 1/6] Some further organisational improvements to documentation. This improves the organisation of the documentation and cleans up some outdated content: * Use IPython Sphinx extension in the user guide for sample codes * Refactor longer pages for better structure and to take advantage of sidebar in pydata theme * Add cards to front page of documentation to highlight key parts of the documentation. --- doc/.gitignore | 1 + doc/conf.py | 78 +-- doc/contents.rst | 2 +- doc/formats.agg.rst | 119 +++++ doc/formats.bagg.rst | 41 ++ doc/formats.efg.rst | 186 +++++++ doc/formats.nfgout.rst | 87 +++ doc/formats.nfgpay.rst | 92 ++++ doc/formats.rst | 551 +------------------ doc/gui.dominance.rst | 109 ++++ doc/gui.efg.rst | 427 +++++++++++++++ doc/gui.export.rst | 74 +++ doc/gui.general.rst | 110 ++++ doc/gui.nash.rst | 206 ++++++++ doc/gui.nfg.rst | 151 ++++++ doc/gui.rst | 1106 +-------------------------------------- doc/index.rst | 68 ++- doc/intro.rst | 20 +- doc/poker.efg | 14 + doc/pygambit.api.rst | 1 + doc/pygambit.user.rst | 251 +++++---- doc/requirements.txt | 2 + doc/tools.convert.rst | 66 +++ doc/tools.enummixed.rst | 95 ++++ doc/tools.enumpoly.rst | 86 +++ doc/tools.enumpure.rst | 97 ++++ doc/tools.gnm.rst | 62 +++ doc/tools.ipa.rst | 49 ++ doc/tools.lcp.rst | 79 +++ doc/tools.liap.rst | 73 +++ doc/tools.logit.rst | 114 ++++ doc/tools.lp.rst | 72 +++ doc/tools.rst | 909 +------------------------------- doc/tools.simpdiv.rst | 80 +++ 34 files changed, 2767 insertions(+), 2711 deletions(-) create mode 100644 doc/.gitignore create mode 100644 doc/formats.agg.rst create mode 100644 doc/formats.bagg.rst create mode 100644 doc/formats.efg.rst create mode 100644 doc/formats.nfgout.rst create mode 100644 doc/formats.nfgpay.rst create mode 100644 doc/gui.dominance.rst create mode 100644 doc/gui.efg.rst create mode 100644 doc/gui.export.rst create mode 100644 doc/gui.general.rst create mode 100644 doc/gui.nash.rst create mode 100644 doc/gui.nfg.rst create mode 100644 doc/poker.efg create mode 100644 doc/tools.convert.rst create mode 100644 doc/tools.enummixed.rst create mode 100644 doc/tools.enumpoly.rst create mode 100644 doc/tools.enumpure.rst create mode 100644 doc/tools.gnm.rst create mode 100644 doc/tools.ipa.rst create mode 100644 doc/tools.lcp.rst create mode 100644 doc/tools.liap.rst create mode 100644 doc/tools.logit.rst create mode 100644 doc/tools.lp.rst create mode 100644 doc/tools.simpdiv.rst diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..eedd89b45 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +api diff --git a/doc/conf.py b/doc/conf.py index 1a61e76e9..0ecd38408 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,8 +10,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -25,6 +23,9 @@ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.napoleon', + 'IPython.sphinxext.ipython_console_highlighting', + 'IPython.sphinxext.ipython_directive', + 'sphinx_design', ] # Add any paths that contain templates here, relative to this directory. @@ -34,14 +35,14 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8' +# source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Gambit' -copyright = '1994-2023, The Gambit Project' +copyright = '1994-2023, The Gambit Project' # noqa # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -54,40 +55,40 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting.[ -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- @@ -99,26 +100,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -127,71 +128,70 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Gambitdoc' - # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Gambit.tex', 'Gambit Documentation', - 'The Gambit Project', 'manual'), + ('index', 'Gambit.tex', 'Gambit Documentation', + 'The Gambit Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True diff --git a/doc/contents.rst b/doc/contents.rst index 3c7cf8d48..00eeba801 100644 --- a/doc/contents.rst +++ b/doc/contents.rst @@ -7,9 +7,9 @@ Detailed table of contents :maxdepth: 3 intro - gui tools pygambit + gui samples build formats diff --git a/doc/formats.agg.rst b/doc/formats.agg.rst new file mode 100644 index 000000000..3c04cd314 --- /dev/null +++ b/doc/formats.agg.rst @@ -0,0 +1,119 @@ +.. _file-formats-agg: + +The action graph game (.agg) file format +======================================== + +Action graph games (AGGs) are a compact representation of simultaneous-move games with structured utility functions. +For more information on AGGs, the following paper gives a comprehensive discussion. + + A.X. Jiang, K. Leyton-Brown and N. Bhat, `Action-Graph Games `_, + Games and Economic Behavior, Volume 71, Issue 1, January 2011, Pages 141-173. + +Each file in this format describes an action graph game. +In order for the file to be recognized as AGG by GAMBIT, the initial line of the file should be:: + + #AGG + +The rest of the file consists of 8 sections, separated by whitespaces. Lines with starting '#' are treated as comments and are allowed between sections. + +#. The number of players, n. + +#. The number of action nodes, \|S\|. + +#. The number of function nodes, \|P\|. + +#. Size of action set for each player. This is a row of n integers: + + \|S\ :sub:`1`\ \| \|S\ :sub:`2`\ \| .... \|S\ :sub:`n`\ \| + +#. Each Player's action set. We have N rows; row i has \|S\ :sub:`i`\ \| integers in ascending order, + which are indices of Action nodes. Action nodes are indexed from 0 to \|S\|-1. + +#. The Action Graph. We have \|S\| + \|P\| nodes, indexed from 0 to \|S\| + \|P\|-1. + The function nodes are indexed after the action nodes. The graph + is represented as (\|S\| + \|P\|) neighbor lists, one list per row. + Rows 0 to \|S\| - 1 are for action nodes; rows \|S\| to \|S\| + \|P\|-1 are for + function nodes. In each row, the first number \|v\| specifies the + number of neighbors of the node. Then follows \|v\| numbers, + corresponding to the indices of the neighbors. + + We require that each function node has at least one neighbor, and + the neighbors of function nodes are action nodes. The action graph + restricted to the function nodes has to be a directed acyclic graph (DAG). + +#. Signatures of functions. This is \|P\| rows, each specifying the mapping + f_p that maps from the configuration of the function node p's neighbors to + an integer for p's "action count". Each function is specified by its "signature" + consisting of an integer type, possibly followed by further parameters. Several types of mapping are + implemented: + + * Types 0-3 require no further input. + + * Type 0: Sum. i.e. The action count of a function node p is the sum of + the action counts of p's neighbors. + * Type 1: Existence: boolean for whether the sum of the counts of + neighbors are positive. + * Type 2: The index of the neighbor with the highest index that has + non-zero counts, or \|S\| + \|P\| if none applies. + * Type 3: The index of the neighbor with the lowest index that has + non-zero counts, or \|S\| + \|P\| if none applies. + + * Types 10-13 are extended versions of type 0-3, each requiring + further parameters of an integer default value and a list of weights, + \|S\| integers enclosed in square brackets. Each action node is thus associated with an integer weight. + + * Type 10: Extended Sum. Each instance of an action in p's neighborhood being chosen contributes the + weight of that action to the sum. These are added to the default value. + * Type 11: Extended Existence: boolean for whether the extended sum is positive. The input default value + and weights are required to be nonnegative. + * Type 12: The weight of the neighbor with the highest index that has + non-zero counts, or the default value if none applies. + * Type 13: The weight of the neighbor with the lowest index that has + non-zero counts, or the default value if none applies. + + The following is an example of the signatures for an AGG with three action nodes and two function nodes:: + + 2 + 10 0 [2 3 4] + +#. The payoff function for each action node. So we have + \|S\| subblocks of numbers. Payoff function for action s is a mapping + from configurations to real numbers. Configurations are + represented as a tuple of integers; the size of the tuple is the size + of the neighborhood of s. Each configuration specifies the action counts + for the neighbors of s, in the same order as the neighbor list of s. + + The first number of each subblock specifies + the type of the payoff function. There are multiple ways of representing + payoff functions; we (or other people) can extend the file format by + defining new types of payoff functions. We define two basic types: + + *Type 0* + The complete representation. The set of possible + configurations can be derived from the action graph. This set of + configurations can also be sorted in lexicographical order. So we can + just specify the payoffs without explicitly giving the configurations. + So we just need to give one row of real numbers, which correspond to + payoffs for the ordered set of configurations. + + If action s is in multiple players' action sets (say players + i, j), + then it is possible that the set of possible configurations + given s\ :sub:`i` + is different from the set of possible configurations given + s\ :sub:`j`\ . + In such cases, we need to specify payoffs for the union of the + sets of configurations (sorted in lexicographical order). + + *Type 1* + The mapping representation, in which we specify the configurations + and the corresponding payoffs. For the payoff function of action s, + first give Delta_s, the number of elements in the mapping. + Then follows Delta_s rows. In each row, first specify the configuration, + which is a tuple of integers, enclosed by a pair of brackets "[" and "]", then the payoff. + For example, the following specifies a payoff function of type 1, with two configurations:: + + 1 2 + [1 0] 2.5 + [1 1] -1.2 + diff --git a/doc/formats.bagg.rst b/doc/formats.bagg.rst new file mode 100644 index 000000000..356314548 --- /dev/null +++ b/doc/formats.bagg.rst @@ -0,0 +1,41 @@ +The Bayesian action graph game (.bagg) format +============================================= + +Bayesian action graph games (BAGGs) are a compact representation of Bayesian (i.e., incomplete-information) games. +For more information on BAGGs, the following paper gives a detailed discussion. + + A.X. Jiang and K. Leyton-Brown, `Bayesian Action-Graph Games `_. NIPS, 2010. + +Each file in this format describes a BAGG. +In order for the file to be recognized as BAGG by GAMBIT, the initial line of the file should be:: + + #BAGG + +The rest of the file consists of the following sections, +separated by whitespaces. Lines with starting '#' are treated as comments and are allowed between sections. + +#. The number of Players, n. +#. The number of action nodes, \|S\|. +#. The number of function nodes, \|P\|. + +#. The number of types for each player, as a row of n integers. +#. Type distribution for each player. The distributions are assumed to be independent. + Each distribution is represented as a row of real numbers. + The following example block gives the type distributions for a BAGG with two players and two types for each player:: + + 0.5 0.5 + 0.2 0.8 + +#. Size of type-action set for each player's each type. + +#. Type-action set for each player's each type. + Each type-action set is represented as a row of integers in ascending order, + which are indices of action nodes. Action nodes are indexed from 0 to \|S\|-1. + +#. The action graph: same as in `the AGG format`_. + +#. types of functions: same as in `the AGG format`_. + +#. utility function for each action node: same as in `the AGG format`_. + +.. _the AGG format: file-formats-agg_ diff --git a/doc/formats.efg.rst b/doc/formats.efg.rst new file mode 100644 index 000000000..87d180079 --- /dev/null +++ b/doc/formats.efg.rst @@ -0,0 +1,186 @@ +.. _file-formats-efg: + +The extensive game (.efg) file format +===================================== + +The extensive game (.efg) file format has been used by Gambit, with +minor variations, to represent extensive games since circa 1994. It +replaced an earlier format, which had no particular name but which had +the conventional extension .dt1. It is intended that some new formats +will be introduced in the future; however, this format will be +supported by Gambit, possibly through the use of converter programs to +those putative future formats, for the foreseeable future. + + +A sample file +------------- + +This is a sample file illustrating the general format of the file. +This file is similar to the one distributed in the Gambit distribution +under the name bayes1a.efg:: + + EFG 2 R "General Bayes game, one stage" { "Player 1" "Player 2" } + c "ROOT" 1 "(0,1)" { "1G" 0.500000 "1B" 0.500000 } 0 + c "" 2 "(0,2)" { "2g" 0.500000 "2b" 0.500000 } 0 + p "" 1 1 "(1,1)" { "H" "L" } 0 + p "" 2 1 "(2,1)" { "h" "l" } 0 + t "" 1 "Outcome 1" { 10.000000 2.000000 } + t "" 2 "Outcome 2" { 0.000000 10.000000 } + p "" 2 1 "(2,1)" { "h" "l" } 0 + t "" 3 "Outcome 3" { 2.000000 4.000000 } + t "" 4 "Outcome 4" { 4.000000 0.000000 } + p "" 1 1 "(1,1)" { "H" "L" } 0 + p "" 2 2 "(2,2)" { "h" "l" } 0 + t "" 5 "Outcome 5" { 10.000000 2.000000 } + t "" 6 "Outcome 6" { 0.000000 10.000000 } + p "" 2 2 "(2,2)" { "h" "l" } 0 + t "" 7 "Outcome 7" { 2.000000 4.000000 } + t "" 8 "Outcome 8" { 4.000000 0.000000 } + c "" 3 "(0,3)" { "2g" 0.500000 "2b" 0.500000 } 0 + p "" 1 2 "(1,2)" { "H" "L" } 0 + p "" 2 1 "(2,1)" { "h" "l" } 0 + t "" 9 "Outcome 9" { 4.000000 2.000000 } + t "" 10 "Outcome 10" { 2.000000 10.000000 } + p "" 2 1 "(2,1)" { "h" "l" } 0 + t "" 11 "Outcome 11" { 0.000000 4.000000 } + t "" 12 "Outcome 12" { 10.000000 2.000000 } + p "" 1 2 "(1,2)" { "H" "L" } 0 + p "" 2 2 "(2,2)" { "h" "l" } 0 + t "" 13 "Outcome 13" { 4.000000 2.000000 } + t "" 14 "Outcome 14" { 2.000000 10.000000 } + p "" 2 2 "(2,2)" { "h" "l" } 0 + t "" 15 "Outcome 15" { 0.000000 4.000000 } + t "" 16 "Outcome 16" { 10.000000 0.000000 } + + + + +Structure of the prologue +------------------------- + +The extensive gamefile consists of two parts: the prologue, or header, +and the list of nodes, or body. In the example file, the prologue is +the first line. (Again, this is just a consequence of the formatting +we have chosen and is not a requirement of the file structure itself.) + +The prologue is constructed as follows. The file begins with the token +EFG , identifying it as an extensive gamefile. Next is the digit 2 ; +this digit is a version number. Since only version 2 files have been +supported for more than a decade, all files have a 2 in this position. +Next comes the letter R . The letter R used to distinguish files which +had rational numbers for numerical data; this distinction is obsolete, +so all new files should have R in this position. + +The prologue continues with the title of the game. Following the title +is a list of the names of the players defined in the game. This list +follows the convention found elsewhere in the file of being surrounded +by curly braces and delimited by whitespace (but not commas, +semicolons, or any other character). The order of the players is +significant; the first entry in the list will be numbered as player 1, +the second entry as player 2, and so forth. At the end of the prologue +is an optional text comment field. + + + +Structure of the body (list of nodes) +------------------------------------- + +The body of the file lists the nodes which comprise the game tree. +These nodes are listed in the prefix traversal of the tree. The prefix +traversal for a subtree is defined as being the root node of the +subtree, followed by the prefix traversal of the subtree rooted by +each child, in order from first to last. Thus, for the whole tree, the +root node appears first, followed by the prefix traversals of its +child subtrees. For convenience, the game above follows the convention +of one line per node. + +Each node entry begins with an unquoted character indicating the type +of the node. There are three node types: + + + ++ c for a chance node ++ p for a personal player node ++ t for a terminal node + +Each node type will be discussed individually below. There are three +numbering conventions which are used to identify the information +structure of the tree. Wherever a player number is called for, the +integer specified corresponds to the index of the player in the player +list from the prologue. The first player in the list is numbered 1, +the second 2, and so on. Information sets are identified by an +arbitrary positive integer which is unique within the player. Gambit +generates these numbers as 1, 2, etc. as they appear first in the +file, but there are no requirements other than uniqueness. The same +integer may be used to specify information sets for different players; +this is not ambiguous since the player number appears as well. +Finally, outcomes are also arbitrarily numbered in the file format in +the same way in which information sets are, except for the special +number 0 which indicates the null outcome. + +Information sets and outcomes may (and frequently will) appear +multiple times within a game. By convention, the second and subsequent +times an information set or outcome appears, the file may omit the +descriptive information for that information set or outcome. +Alternatively, the file may specify the descriptive information again; +however, it must precisely match the original declaration of the +information set or outcome. If any part of the description is omitted, +the whole description must be omitted. + +Outcomes may appear at nonterminal nodes. In these cases, payoffs are +interepreted as incremental payoffs; the payoff to a player for a +given path through the tree is interpreted as the sum of the payoffs +at the outcomes encountered on that path (including at the terminal +node). This is ideal for the representation of games with well- +defined"stages"; see, for example, the file bayes2a.efg in the Gambit +distribution for a two-stage example of the Bayesian game represented +previously. + +In the following lists, fields which are omittable according to the +above rules are indicated by the label (optional). + +**Format of chance (nature) nodes.** Entries for chance nodes begin +with the character c . Following this, in order, are + + + ++ a text string, giving the name of the node ++ a positive integer specifying the information set number ++ (optional) the name of the information set ++ (optional) a list of actions at the information set with their + corresponding probabilities ++ a nonnegative integer specifying the outcome ++ (optional)the payoffs to each player for the outcome + + + +**Format of personal (player) nodes.** Entries for personal player +decision nodes begin with the character p . Following this, in order, +are: + + + ++ a text string, giving the name of the node ++ a positive integer specifying the player who owns the node ++ a positive integer specifying the information set ++ (optional) the name of the information set ++ (optional) a list of action names for the information set ++ a nonnegative integer specifying the outcome ++ (optional) the name of the outcome ++ the payoffs to each player for the outcome + + + +**Format of terminal nodes.** Entries for terminal nodes begin with +the character t . Following this, in order, are: + + + ++ a text string, giving the name of the node ++ a nonnegative integer specifying the outcome ++ (optional) the name of the outcome ++ the payoffs to each player for the outcome + + + +There is no explicit end-of-file delimiter for the file. diff --git a/doc/formats.nfgout.rst b/doc/formats.nfgout.rst new file mode 100644 index 000000000..5216ab666 --- /dev/null +++ b/doc/formats.nfgout.rst @@ -0,0 +1,87 @@ +The strategic game (.nfg) file format, outcome version +====================================================== + +This file format defines a strategic N-player game. In this version, +the payoffs are defined by means of outcomes, which may appear more +than one place in the game table. This may give a more compact means +of representing a game where many different strategy combinations map +to the same consequences for the players. For a version of this format +in which payoffs are listed explicitly, without identification by +outcomes, see the previous section. + + + +A sample file +------------- + +This is a sample file illustrating the general format of the file. +This file defines the same game as the example in the previous +section:: + + NFG 1 R "Selten (IJGT, 75), Figure 2, normal form" { "Player 1" "Player 2" } + + { + { "1" "2" "3" } + { "1" "2" } + } + + { + { "" 1, 1 } + { "" 0, 2 } + { "" 0, 2 } + { "" 1, 1 } + { "" 0, 3 } + { "" 2, 0 } + } + 1 2 3 4 5 6 + + + + +Structure of the prologue +------------------------- + +The prologue is constructed as follows. The file begins with the token +NFG , identifying it as a strategic gamefile. Next is the digit 1 ; +this digit is a version number. Since only version 1 files have been +supported for more than a decade, all files have a 1 in this position. +Next comes the letter R . The letter R used to distinguish files which +had rational numbers for numerical data; this distinction is obsolete, +so all new files should have R in this position. + +The prologue continues with the title of the game. Following the title +is a list of the names of the players defined in the game. This list +follows the convention found elsewhere in the file of being surrounded +by curly braces and delimited by whitespace (but not commas, +semicolons, or any other character). The order of the players is +significant; the first entry in the list will be numbered as player 1, +the second entry as player 2, and so forth. + +Following the list of players is a list of strategies. This is a +nested list; each player's strategies are given as a list of text +labels, surrounded by curly braces. + +The nested strategy list is followed by an optional text comment +field. + +The prologue closes with a list of outcomes. This is also a nested +list. Each outcome is specified by a text string, followed by a list +of numerical payoffs, one for each player defined. The payoffs may +optionally be separated by commas, as in the example file. The +outcomes are implicitly numbered in the order they appear; the first +outcome is given the number 1, the second 2, and so forth. + + +Structure of the body (list of outcomes) +---------------------------------------- + +The body of the file is a list of outcome indices. These are presented +in the same lexicographic order as the payoffs in the payoff file +format; please see the documentation of that format for the +description of the ordering. For each entry in the table, a +nonnegative integer is given, corresponding to the outcome number +assigned as described in the prologue section. The special outcome +number 0 is reserved for the "null" outcome, which is defined as a +payoff of zero to all players. The number of entries in this list, +then, should be the same as the product of the number of strategies +for all players in the game. diff --git a/doc/formats.nfgpay.rst b/doc/formats.nfgpay.rst new file mode 100644 index 000000000..5712c5b69 --- /dev/null +++ b/doc/formats.nfgpay.rst @@ -0,0 +1,92 @@ +.. _file-formats-nfg: + +The strategic game (.nfg) file format, payoff version +===================================================== + +This file format defines a strategic N-player game. In this version, +the payoffs are listed in a tabular format. See the next section for a +version of this format in which outcomes can be used to identify an +equivalence among multiple strategy profiles. + + + +A sample file +------------- + +This is a sample file illustrating the general format of the file. +This file is distributed in the Gambit distribution under the name +e02.nfg:: + + NFG 1 R "Selten (IJGT, 75), Figure 2, normal form" + { "Player 1" "Player 2" } { 3 2 } + + 1 1 0 2 0 2 1 1 0 3 2 0 + + +Structure of the prologue +------------------------- + +The prologue is constructed as follows. The file begins with the token +NFG , identifying it as a strategic gamefile. Next is the digit 1 ; +this digit is a version number. Since only version 1 files have been +supported for more than a decade, all files have a 1 in this position. +Next comes the letter R . The letter R used to distinguish files which +had rational numbers for numerical data; this distinction is obsolete, +so all new files should have R in this position. + +The prologue continues with the title of the game. Following the title +is a list of the names of the players defined in the game. This list +follows the convention found elsewhere in the file of being surrounded +by curly braces and delimited by whitespace (but not commas, +semicolons, or any other character). The order of the players is +significant; the first entry in the list will be numbered as player 1, +the second entry as player 2, and so forth. + +Following the list of players is a list of positive integers. This +list specifies the number of strategies available to each player, +given in the same order as the players are listed in the list of +players. + +The prologue concludes with an optional text comment field. + + +Structure of the body (list of payoffs) +--------------------------------------- + +The body of the format lists the payoffs in the game. This is a "flat" +list, not surrounded by braces or other punctuation. + +The assignment of the numeric data in this list to the entries in the +strategic game table proceeds as follows. The list begins with the +strategy profile in which each player plays their first strategy. The +payoffs to all players in this contingency are listed in the same +order as the players are given in the prologue. This, in the example +file, the first two payoff entries are 1 1 , which means, when both +players play their first strategy, player 1 receives a payoff of 1, +and player 2 receives a payoff of 1. + +Next, the strategy of the first player is incremented. Thus, player +1's strategy is incremented to his second strategy. In this case, when +player 1 plays his second strategy and player 2 his first strategy, +the payoffs are 0 2 : a payoff of 0 to player 1 and a payoff of 2 to +player 2. + +Now the strategy of the first player is again incremented. Thus, the +first player is playing his third strategy, and the second player his +first strategy; the payoffs are again 0 2 . + +Now, the strategy of the first player is incremented yet again. But, +the first player was already playing strategy number 3 of 3. Thus, his +strategy now "rolls over" to 1, and the strategy of the second player +increments to 2. Then, the next entries 1 1 correspond to the payoffs +of player 1 and player 2, respectively, in the case where player 1 +plays his second strategy, and player 2 his first strategy. + +In general, the ordering of contingencies is done in the same way that +we count: incrementing the least-significant digit place in the number +first, and then incrementing more significant digit places in the +number as the lower ones "roll over." The only differences are that +the counting starts with the digit 1, instead of 0, and that the +"base" used for each digit is not 10, but instead is the number of +strategies that player has in the game. + diff --git a/doc/formats.rst b/doc/formats.rst index 70532d694..34d5e7d22 100644 --- a/doc/formats.rst +++ b/doc/formats.rst @@ -1,7 +1,8 @@ .. _file-formats: +*************************** Game representation formats -=========================== +*************************** This section documents the file formats recognized by Gambit. These file formats are text-based and designed to be readable and editable @@ -21,7 +22,7 @@ and the like. Conventions common to all file formats --------------------------------------- +====================================== Several conventions are common to the interpretation of the file formats listed below. @@ -42,20 +43,16 @@ interface. In all cases, these labels are surrounded by the quotation character ("). The use of an explicit " character within a text label can be accomplished by preceding the embedded " characters with a backwards slash (\). -**Example 5-1. Escaping quotes in a text label** - This is an alternate version of the first line of the example file, in which the title of the game contains the term Bayesian game in -quotation marks. - -.. sourcecode:: python +quotation marks:: EFG 2 R "An example of a \"Bayesian game\"" { "Player 1" "Player 2" } -**Numerical data.** yNumerical data, namely, the payoffs at outcomes, +**Numerical data.** Numerical data, namely, the payoffs at outcomes, and the action probabilities for chance nodes, may be expressed in integer, decimal, or rational formats. In all cases, numbers are understood by Gambit to be exact, and represented as such internally. @@ -77,542 +74,18 @@ concern, and the current Gambit implementation now behaves in such a way as to give the "expected" result when decimal numbers appear in the file format. +.. toctree:: + :maxdepth: 2 -.. _file-formats-efg: - -The extensive game (.efg) file format -------------------------------------- - -The extensive game (.efg) file format has been used by Gambit, with -minor variations, to represent extensive games since circa 1994. It -replaced an earlier format, which had no particular name but which had -the conventional extension .dt1. It is intended that some new formats -will be introduced in the future; however, this format will be -supported by Gambit, possibly through the use of converter programs to -those putative future formats, for the foreseeable future. - - -A sample file -~~~~~~~~~~~~~ - -This is a sample file illustrating the general format of the file. -This file is similar to the one distributed in the Gambit distribution -under the name bayes1a.efg . - -.. sourcecode:: python - - EFG 2 R "General Bayes game, one stage" { "Player 1" "Player 2" } - c "ROOT" 1 "(0,1)" { "1G" 0.500000 "1B" 0.500000 } 0 - c "" 2 "(0,2)" { "2g" 0.500000 "2b" 0.500000 } 0 - p "" 1 1 "(1,1)" { "H" "L" } 0 - p "" 2 1 "(2,1)" { "h" "l" } 0 - t "" 1 "Outcome 1" { 10.000000 2.000000 } - t "" 2 "Outcome 2" { 0.000000 10.000000 } - p "" 2 1 "(2,1)" { "h" "l" } 0 - t "" 3 "Outcome 3" { 2.000000 4.000000 } - t "" 4 "Outcome 4" { 4.000000 0.000000 } - p "" 1 1 "(1,1)" { "H" "L" } 0 - p "" 2 2 "(2,2)" { "h" "l" } 0 - t "" 5 "Outcome 5" { 10.000000 2.000000 } - t "" 6 "Outcome 6" { 0.000000 10.000000 } - p "" 2 2 "(2,2)" { "h" "l" } 0 - t "" 7 "Outcome 7" { 2.000000 4.000000 } - t "" 8 "Outcome 8" { 4.000000 0.000000 } - c "" 3 "(0,3)" { "2g" 0.500000 "2b" 0.500000 } 0 - p "" 1 2 "(1,2)" { "H" "L" } 0 - p "" 2 1 "(2,1)" { "h" "l" } 0 - t "" 9 "Outcome 9" { 4.000000 2.000000 } - t "" 10 "Outcome 10" { 2.000000 10.000000 } - p "" 2 1 "(2,1)" { "h" "l" } 0 - t "" 11 "Outcome 11" { 0.000000 4.000000 } - t "" 12 "Outcome 12" { 10.000000 2.000000 } - p "" 1 2 "(1,2)" { "H" "L" } 0 - p "" 2 2 "(2,2)" { "h" "l" } 0 - t "" 13 "Outcome 13" { 4.000000 2.000000 } - t "" 14 "Outcome 14" { 2.000000 10.000000 } - p "" 2 2 "(2,2)" { "h" "l" } 0 - t "" 15 "Outcome 15" { 0.000000 4.000000 } - t "" 16 "Outcome 16" { 10.000000 0.000000 } - - - - -Structure of the prologue -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The extensive gamefile consists of two parts: the prologue, or header, -and the list of nodes, or body. In the example file, the prologue is -the first line. (Again, this is just a consequence of the formatting -we have chosen and is not a requirement of the file structure itself.) - -The prologue is constructed as follows. The file begins with the token -EFG , identifying it as an extensive gamefile. Next is the digit 2 ; -this digit is a version number. Since only version 2 files have been -supported for more than a decade, all files have a 2 in this position. -Next comes the letter R . The letter R used to distinguish files which -had rational numbers for numerical data; this distinction is obsolete, -so all new files should have R in this position. - -The prologue continues with the title of the game. Following the title -is a list of the names of the players defined in the game. This list -follows the convention found elsewhere in the file of being surrounded -by curly braces and delimited by whitespace (but not commas, -semicolons, or any other character). The order of the players is -significant; the first entry in the list will be numbered as player 1, -the second entry as player 2, and so forth. At the end of the prologue -is an optional text comment field. - - - -Structure of the body (list of nodes) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The body of the file lists the nodes which comprise the game tree. -These nodes are listed in the prefix traversal of the tree. The prefix -traversal for a subtree is defined as being the root node of the -subtree, followed by the prefix traversal of the subtree rooted by -each child, in order from first to last. Thus, for the whole tree, the -root node appears first, followed by the prefix traversals of its -child subtrees. For convenience, the game above follows the convention -of one line per node. - -Each node entry begins with an unquoted character indicating the type -of the node. There are three node types: - - - -+ c for a chance node -+ p for a personal player node -+ t for a terminal node - -Each node type will be discussed individually below. There are three -numbering conventions which are used to identify the information -structure of the tree. Wherever a player number is called for, the -integer specified corresponds to the index of the player in the player -list from the prologue. The first player in the list is numbered 1, -the second 2, and so on. Information sets are identified by an -arbitrary positive integer which is unique within the player. Gambit -generates these numbers as 1, 2, etc. as they appear first in the -file, but there are no requirements other than uniqueness. The same -integer may be used to specify information sets for different players; -this is not ambiguous since the player number appears as well. -Finally, outcomes are also arbitrarily numbered in the file format in -the same way in which information sets are, except for the special -number 0 which indicates the null outcome. - -Information sets and outcomes may (and frequently will) appear -multiple times within a game. By convention, the second and subsequent -times an information set or outcome appears, the file may omit the -descriptive information for that information set or outcome. -Alternatively, the file may specify the descriptive information again; -however, it must precisely match the original declaration of the -information set or outcome. If any part of the description is omitted, -the whole description must be omitted. - -Outcomes may appear at nonterminal nodes. In these cases, payoffs are -interepreted as incremental payoffs; the payoff to a player for a -given path through the tree is interpreted as the sum of the payoffs -at the outcomes encountered on that path (including at the terminal -node). This is ideal for the representation of games with well- -defined"stages"; see, for example, the file bayes2a.efg in the Gambit -distribution for a two-stage example of the Bayesian game represented -previously. - -In the following lists, fields which are omittable according to the -above rules are indicated by the label (optional). - -**Format of chance (nature) nodes.** Entries for chance nodes begin -with the character c . Following this, in order, are - - - -+ a text string, giving the name of the node -+ a positive integer specifying the information set number -+ (optional) the name of the information set -+ (optional) a list of actions at the information set with their - corresponding probabilities -+ a nonnegative integer specifying the outcome -+ (optional)the payoffs to each player for the outcome - - - -**Format of personal (player) nodes.** Entries for personal player -decision nodes begin with the character p . Following this, in order, -are: - - - -+ a text string, giving the name of the node -+ a positive integer specifying the player who owns the node -+ a positive integer specifying the information set -+ (optional) the name of the information set -+ (optional) a list of action names for the information set -+ a nonnegative integer specifying the outcome -+ (optional) the name of the outcome -+ the payoffs to each player for the outcome - - - -**Format of terminal nodes.** Entries for terminal nodes begin with -the character t . Following this, in order, are: - - - -+ a text string, giving the name of the node -+ a nonnegative integer specifying the outcome -+ (optional) the name of the outcome -+ the payoffs to each player for the outcome - - - -There is no explicit end-of-file delimiter for the file. - - - -.. _file-formats-nfg: - -The strategic game (.nfg) file format, payoff version ------------------------------------------------------ - -This file format defines a strategic N-player game. In this version, -the payoffs are listed in a tabular format. See the next section for a -version of this format in which outcomes can be used to identify an -equivalence among multiple strategy profiles. - - - -A sample file -~~~~~~~~~~~~~ - -This is a sample file illustrating the general format of the file. -This file is distributed in the Gambit distribution under the name -e02.nfg . - -.. sourcecode:: python - - NFG 1 R "Selten (IJGT, 75), Figure 2, normal form" - { "Player 1" "Player 2" } { 3 2 } - - 1 1 0 2 0 2 1 1 0 3 2 0 - - - - -Structure of the prologue -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The prologue is constructed as follows. The file begins with the token -NFG , identifying it as a strategic gamefile. Next is the digit 1 ; -this digit is a version number. Since only version 1 files have been -supported for more than a decade, all files have a 1 in this position. -Next comes the letter R . The letter R used to distinguish files which -had rational numbers for numerical data; this distinction is obsolete, -so all new files should have R in this position. - -The prologue continues with the title of the game. Following the title -is a list of the names of the players defined in the game. This list -follows the convention found elsewhere in the file of being surrounded -by curly braces and delimited by whitespace (but not commas, -semicolons, or any other character). The order of the players is -significant; the first entry in the list will be numbered as player 1, -the second entry as player 2, and so forth. - -Following the list of players is a list of positive integers. This -list specifies the number of strategies available to each player, -given in the same order as the players are listed in the list of -players. - -The prologue concludes with an optional text comment field. - - -Structure of the body (list of payoffs) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The body of the format lists the payoffs in the game. This is a "flat" -list, not surrounded by braces or other punctuation. - -The assignment of the numeric data in this list to the entries in the -strategic game table proceeds as follows. The list begins with the -strategy profile in which each player plays their first strategy. The -payoffs to all players in this contingency are listed in the same -order as the players are given in the prologus. This, in the example -file, the first two payoff entries are 1 1 , which means, when both -players play their first strategy, player 1 receives a payoff of 1, -and player 2 receives a payoff of 1. - -Next, the strategy of the first player is incremented. Thus, player -1's strategy is incremented to his second strategy. In this case, when -player 1 plays his second strategy and player 2 his first strategy, -the payoffs are 0 2 : a payoff of 0 to player 1 and a payoff of 2 to -player 2. - -Now the strategy of the first player is again incremented. Thus, the -first player is playing his third strategy, and the second player his -first strategy; the payoffs are again 0 2 . - -Now, the strategy of the first player is incremented yet again. But, -the first player was already playing strategy number 3 of 3. Thus, his -strategy now "rolls over" to 1, and the strategy of the second player -increments to 2. Then, the next entries 1 1 correspond to the payoffs -of player 1 and player 2, respectively, in the case where player 1 -plays his second strategy, and player 2 his first strategy. - -In general, the ordering of contingencies is done in the same way that -we count: incrementing the least-significant digit place in the number -first, and then incrementing more significant digit places in the -number as the lower ones "roll over." The only differences are that -the counting starts with the digit 1, instead of 0, and that the -"base" used for each digit is not 10, but instead is the number of -strategies that player has in the game. - - -The strategic game (.nfg) file format, outcome version ------------------------------------------------------- - -This file format defines a strategic N-player game. In this version, -the payoffs are defined by means of outcomes, which may appear more -than one place in the game table. This may give a more compact means -of representing a game where many different strategy combinations map -to the same consequences for the players. For a version of this format -in which payoffs are listed explicitly, without identification by -outcomes, see the previous section. - - - -A sample file -~~~~~~~~~~~~~ - -This is a sample file illustrating the general format of the file. -This file defines the same game as the example in the previous -section. - -.. sourcecode:: python - - NFG 1 R "Selten (IJGT, 75), Figure 2, normal form" { "Player 1" "Player 2" } - - { - { "1" "2" "3" } - { "1" "2" } - } - - { - { "" 1, 1 } - { "" 0, 2 } - { "" 0, 2 } - { "" 1, 1 } - { "" 0, 3 } - { "" 2, 0 } - } - 1 2 3 4 5 6 - - - - -Structure of the prologue -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The prologue is constructed as follows. The file begins with the token -NFG , identifying it as a strategic gamefile. Next is the digit 1 ; -this digit is a version number. Since only version 1 files have been -supported for more than a decade, all files have a 1 in this position. -Next comes the letter R . The letter R used to distinguish files which -had rational numbers for numerical data; this distinction is obsolete, -so all new files should have R in this position. - -The prologue continues with the title of the game. Following the title -is a list of the names of the players defined in the game. This list -follows the convention found elsewhere in the file of being surrounded -by curly braces and delimited by whitespace (but not commas, -semicolons, or any other character). The order of the players is -significant; the first entry in the list will be numbered as player 1, -the second entry as player 2, and so forth. - -Following the list of players is a list of strategies. This is a -nested list; each player's strategies are given as a list of text -labels, surrounded by curly braces. - -The nested strategy list is followed by an optional text comment -field. - -The prologue closes with a list of outcomes. This is also a nested -list. Each outcome is specified by a text string, followed by a list -of numerical payoffs, one for each player defined. The payoffs may -optionally be separated by commas, as in the example file. The -outcomes are implicitly numbered in the order they appear; the first -outcome is given the number 1, the second 2, and so forth. - - -Structure of the body (list of outcomes) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The body of the file is a list of outcome indices. These are presented -in the same lexicographic order as the payoffs in the payoff file -format; please see the documentation of that format for the -description of the ordering. For each entry in the table, a -nonnegative integer is given, corresponding to the outcome number -assigned as described in the prologue section. The special outcome -number 0 is reserved for the "null" outcome, which is defined as a -payoff of zero to all players. The number of entries in this list, -then, should be the same as the product of the number of strategies -for all players in the game. - - -.. _file-formats-agg: - -The action graph game (.agg) file format ----------------------------------------- - -Action graph games (AGGs) are a compact representation of simultaneous-move games with structured utility functions. -For more information on AGGs, the following paper gives a comprehensive discussion. - - A.X. Jiang, K. Leyton-Brown and N. Bhat, `Action-Graph Games `_, Games and Economic Behavior, Volume 71, Issue 1, January 2011, Pages 141-173. - -Each file in this format describes an action graph game. -In order for the file to be recognized as AGG by GAMBIT, the initial line of the file should be:: - - #AGG - -The rest of the file consists of 8 sections, separated by whitespaces. Lines with starting '#' are treated as comments and are allowed between sections. - -#. The number of players, n. - -#. The number of action nodes, \|S\|. - -#. The number of function nodes, \|P\|. - -#. Size of action set for each player. This is a row of n integers: - - \|S\ :sub:`1`\ \| \|S\ :sub:`2`\ \| .... \|S\ :sub:`n`\ \| - -#. Each Player's action set. We have N rows; row i has \|S\ :sub:`i`\ \| integers in ascending order, - which are indices of Action nodes. Action nodes are indexed from 0 to \|S\|-1. - -#. The Action Graph. We have \|S\|+\|P\| nodes, indexed from 0 to \|S\|+\|P\|-1. - The function nodes are indexed after the action nodes. The graph - is represented as (\|S\|+\|P\|) neighbor lists, one list per row. - Rows 0 to \|S\|-1 are for action nodes; rows \|S\| to \|S\|+\|P\|-1 are for - function nodes. In each row, the first number \|v\| specifies the - number of neighbors of the node. Then follows \|v\| numbers, - corresponding to the indices of the neighbors. - - We require that each function node has at least one neighbor, and - the neighbors of function nodes are action nodes. The action graph - restricted to the function nodes has to be a directed acyclic graph (DAG). - -#. Signatures of functions. This is \|P\| rows, each specifying the mapping - f_p that maps from the configuration of the function node p's neighbors to - an integer for p's "action count". Each function is specified by its "signature" - consisting of an integer type, possibly followed by further parameters. Several types of mapping are - implemented: - - * Types 0-3 require no further input. - - * Type 0: Sum. i.e. The action count of a function node p is the sum of - the action counts of p's neighbors. - * Type 1: Existence: boolean for whether the sum of the counts of - neighbors are positive. - * Type 2: The index of the neighbor with the highest index that has - non-zero counts, or \|S\|+\|P\| if none applies. - * Type 3: The index of the neighbor with the lowest index that has - non-zero counts, or \|S\|+\|P\| if none applies. - - * Types 10-13 are extended versions of type 0-3, each requiring - further parameters of an integer default value and a list of weights, - \|S\| integers enclosed in square brackets. Each action node is thus associated with an integer weight. - - * Type 10: Extended Sum. Each instance of an action in p's neighborhood being chosen contributes the - weight of that action to the sum. These are added to the default value. - * Type 11: Extended Existence: boolean for whether the extended sum is positive. The input default value - and weights are required to be nonnegative. - * Type 12: The weight of the neighbor with the highest index that has - non-zero counts, or the default value if none applies. - * Type 13: The weight of the neighbor with the lowest index that has - non-zero counts, or the default value if none applies. - - The following is an example of the signatures for an AGG with three action nodes and two function nodes:: - - 2 - 10 0 [2 3 4] - -#. The payoff function for each action node. So we have - \|S\| subblocks of numbers. Payoff function for action s is a mapping - from configurations to real numbers. Configurations are - represented as a tuple of integers; the size of the tuple is the size - of the neighborhood of s. Each configuration specifies the action counts - for the neighbors of s, in the same order as the neighbor list of s. - - The first number of each subblock specifies - the type of the payoff function. There are multiple ways of representing - payoff functions; we (or other people) can extend the file format by - defining new types of payoff functions. We define two basic types: - - *Type 0* - The complete representation. The set of possible - configurations can be derived from the action graph. This set of - configurations can also be sorted in lexicographical order. So we can - just specify the payoffs without explicitly giving the configurations. - So we just need to give one row of real numbers, which correspond to - payoffs for the ordered set of configurations. - - If action s is in multiple players' action sets (say players - i, j), - then it is possible that the set of possible configurations - given s\ :sub:`i` - is different from the set of possible configurations given - s\ :sub:`j`\ . - In such cases, we need to specify payoffs for the union of the - sets of configurations (sorted in lexicographical order). - - *Type 1* - The mapping representation, in which we specify the configurations - and the corresponding payoffs. For the payoff function of action s, - first give Delta_s, the number of elements in the mapping. - Then follows Delta_s rows. In each row, first specify the configuration, - which is a tuple of integers, enclosed by a pair of brackets "[" and "]", then the payoff. - For example, the following specifies a payoff function of type 1, with two configurations:: - - 1 2 - [1 0] 2.5 - [1 1] -1.2 - -The Bayesian action graph game (.bagg) format ---------------------------------------------- - -Bayesian action graph games (BAGGs) are a compact representation of Bayesian (i.e., incomplete-information) games. -For more information on BAGGs, the following paper gives a detailed discussion. - - A.X. Jiang and K. Leyton-Brown, `Bayesian Action-Graph Games `_. NIPS, 2010. - -Each file in this format describes a BAGG. -In order for the file to be recognized as BAGG by GAMBIT, the initial line of the file should be:: - - #BAGG - -The rest of the file consists of the following sections, -separated by whitespaces. Lines with starting '#' are treated as comments and are allowed between sections. + formats.efg + formats.nfgpay + formats.nfgout + formats.agg + formats.bagg -#. The number of Players, n. -#. The number of action nodes, \|S\|. -#. The number of function nodes, \|P\|. - -#. The number of types for each player, as a row of n integers. -#. Type distribution for each player. The distributions are assumed to be independent. - Each distribution is represented as a row of real numbers. - The following example block gives the type distributions for a BAGG with two players and two types for each player:: - - 0.5 0.5 - 0.2 0.8 -#. Size of type-action set for each player's each type. -#. Type-action set for each player's each type. - Each type-action set is represented as a row of integers in ascending order, - which are indices of action nodes. Action nodes are indexed from 0 to \|S\|-1. -#. The action graph: same as in `the AGG format`_. -#. types of functions: same as in `the AGG format`_. -#. utility function for each action node: same as in `the AGG format`_. -.. _the AGG format: file-formats-agg_ diff --git a/doc/gui.dominance.rst b/doc/gui.dominance.rst new file mode 100644 index 000000000..3022369b2 --- /dev/null +++ b/doc/gui.dominance.rst @@ -0,0 +1,109 @@ +.. _dominated-strategies: + +Investigating dominated strategies and actions +============================================== + +Selecting :menuselection:`Tools --> Dominance` +toggles the appearance of a toolbar which +can be used to investigate the structure of dominated strategies and +actions. + + + +Dominated actions in extensive game +----------------------------------- + +In extensive games, the dominance toolbar controls the elimination of actions which are conditionally dominated. + +.. image:: screens/pokerdom1.* + :width: 33% + :alt: the poker game, with the dominance toolbar shown + :align: right + :target: _images/pokerdom1.png + +Actions may be eliminated based on two criteria: + +:guilabel:`Strict dominance` + The action is always worse than another, + regardless of beliefs at the information set; + +:guilabel:`Strict or weak dominance` + There is another action at the information + set that is always at least as good as the action, and strictly better + in some cases. + +.. image:: screens/pokerdom2.* + :width: 33% + :alt: the poker game, with the dominated action eliminated + :align: right + :target: _images/pokerdom2.png + +For example, in the poker game, it is strictly dominated for Fred to +choose Fold after Red. Clicking the next level icon +removes the dominated action from the game display. + +The tree layout remains unchanged, including nodes which can only be +reached using actions which have been eliminated. To compress the tree +to remove the unreachable nodes, check the box labeled +:guilabel:`Show only +reachable nodes`. + +For this game, no further actions can be eliminated. In general, +further steps of elimination can be done by again clicking the next +level icon. The toolbar keeps track of the number of levels of +elimination currently shown; the previous level icon moves up one +level of elimination. + +.. image:: screens/pokerdom3.* + :width: 33% + :alt: the poker game, with only reachable actions shown + :align: right + :target: _images/pokerdom3.png + +The elimination of multiple levels can be automated using the fast +forward icon , which iteratively eliminates dominated actions until no +further actions can be eliminated. The rewind icon restores the +display to the full game. + + + +Dominated strategies in strategic games +--------------------------------------- + +The dominance toolbar operates in strategic games in the same way as +the in the extensive game. Strategies can be eliminated iteratively +based on whether they are strictly or weakly dominated. + +.. image:: screens/pddom1.* + :width: 33% + :alt: the prisoner's dilemma example, with dominated + strategies indicated + :align: right + :target: _images/pddom1.png + +When the dominance toolbar is shown, the strategic game table contains +indicators of strategies that are dominated. +In the prisoner's dilemma, the Cooperate strategy is strictly +dominated for both players. This strict dominance is indicated by the +solid "X" drawn across the corresponding strategy labels for both +players. In addition, the payoffs corresponding to the dominated +strategies are also drawn with a solid "X" across them. Thus, any +contingency in the table containing at least one "X" is a contingency +that can only be reached by at least one player playing a strategy +that is dominated. + + +Strategies that are weakly dominated are similarly indicated, except +the "X" shape is drawn using a thinner, dashed line instead of the +thick, solid line. + +.. image:: screens/pddom2.* + :width: 33% + :alt: the prisoner's dilemma example, with dominated + strategies removed + :align: right + :target: _images/pddom2.png + +Clicking the next level icon removes the strictly dominated strategies +from the display. + diff --git a/doc/gui.efg.rst b/doc/gui.efg.rst new file mode 100644 index 000000000..4eaafd00c --- /dev/null +++ b/doc/gui.efg.rst @@ -0,0 +1,427 @@ +Extensive games +=============== + +The graphical interface provides a flexible set of operations for +constructing and editing general extensive games. These are outlined +below. + + + +Creating a new extensive game +----------------------------- + +To create a new extensive game, select +:menuselection:`File --> New --> Extensive game`, or +click on the new extensive game icon . The extensive game created is a +trivial game with two players, named by default +:guilabel:`Player 1` and :guilabel:`Player 2`, +with one node, which is both the root and terminal node of the game. +In addition, extensive games have a special player labeled +:guilabel:`Chance`, +which is used to represent random events not controlled by any of the +strategic players in the game. + + +.. _adding-moves: + +Adding moves +------------ + +There are two options for adding moves to a tree: drag-and-drop +and the :guilabel:`Insert move` dialog. + +#. Moves can be added to the + tree using a drag-and-drop idiom. From the player list window, drag + the player icon located to the left of the player who will have the + move to any terminal node in the game tree. The tree will be extended + with a new move for that player, with two actions at the new move. + Adding a move for the chance player is done the same way, except the + dice icon appearing to the left of the chance player in the player + list window is used instead of the player icon. For the chance player, + the two actions created will each be given a probability weight of + one-half. If the desired move has more than two actions, additional + actions can be added by dragging the same player's icon to the move + node; this will add one action to the move each time this is done. + +.. image:: screens/insertmove.* + :width: 33% + :alt: insert move dialog + :align: right + :target: _images/insertmove.png + +2. Click on any terminal node in + the tree, and select :menuselection:`Edit --> Insert move` + to display the :guilabel:`insert move` dialog. + The dialog is intended to read like a sentence: + + + The first control specifies the player who will make the move. The + move can be assigned to a new player by specifying + :guilabel:`Insert move for a new player here`. + + The second control selects the information set to which to add the + move. To create the move in a new information set, select + :guilabel:`at a new information set` for this control. + + The third control sets the number of actions. This control is + disabled unless the second control is set to + :guilabel:`at a new information set`. + Otherwise, it is set automatically to the number of actions at + the selected information set. + +The two methods can be useful in different contexts. +The drag-and-drop approach +is a bit quicker to use, especially when creating trees that have few +actions at each move. The dialog approach is a bit more flexible, in +that a move can be added for a new, as-yet-undefined player, a move +can be added directly into an existing information set, and a move can +be immediately given more than two actions. + +.. _copying-trees: + +Copying and moving subtrees +--------------------------- + +Many extensive games have structures that appear in multiple parts of +the tree. It is often efficient to create the structure once, and then +copy it as needed elsewhere. + +Gambit provides a convenient idiom for this. Clicking on any +nonterminal node and dragging to any terminal node implements a move +operation, which moves the entire subtree rooted at the original, +nonterminal node to the terminal node. + +To turn the operation into a copy operation: + ++ On Windows and Linux systems, hold down the :kbd:`Ctrl` key during + the operation. ++ On OS X, hold down the :kbd:`Cmd` key when starting the + drag operation, then release prior to dropping. + +The entire subtree rooted at the original node is copied, +starting at the terminal node. In this copy operation, each node in +the copied image is placed in the same information set as the +corresponding node in the original subtree. + +Copying a subtree to a terminal node in that subtree is also +supported. In this case, the copying operation is halted when reaching +the terminal node, to avoid an infinite loop. Thus, this feature +can also be helpful in constructing multiple-stage games. + + + +Removing parts of a game tree +----------------------------- + +Two deletion operations are supported on extensive games. To delete +the entire subtree rooted at a node, click on that node and select +:menuselection:`Edit --> Delete subtree`. + +To delete an individual move from the game, click on one of the direct +children of that node, and select +:menuselection:`Edit --> Delete parent`. This operation +deletes the parent node, as well as all the children of the parent +other than the selected node. The selected child node now takes the +place of the parent node in the tree. + + + +Managing information sets +------------------------- + + +Gambit provides several methods to help manage the information +structure in an extensive game. + +When building a tree, new moves can be placed in a given information +set using the :ref:`Insert move dialog `. +Additionally, new moves can be +created using the drag-and-drop idiom by holding down the :kbd:`Shift` +key and dragging a node in the tree. During the drag operation, the +cursor changes to the move icon . Dropping the move icon on another +node places the target node in the same information set as the node +where the drag operation began. + +.. image:: screens/editnode.* + :width: 33% + :alt: node properties dialog + :align: right + :target: _images/editnode.png + +The information set to which a node belongs can also be set by +selecting :menuselection:`Edit --> Node`. This displays the +:guilabel:`node properties` dialog. +The :guilabel:`Information set` dropdown defaults +to the current information set to which the node belongs, and contains +a list of all other information sets in the game which are compatible +with the node, that is, which have the same number of +actions. Additionally, the node can be moved to a new, singleton +information set by setting this dropdown to the :guilabel:`New +information set` entry. + + + +When building out a game tree using the :ref:`drag-and-drop approach +` to copying portions of the tree, +the nodes created in +the copy of the subtree remain in the same information set as the +corresponding nodes in the original subtree. In many cases, though, +these trees differ in the information available to some or all of the +players. To help speed the process of adjusting information sets in +bulk, Gambit offers a "reveal" operation, which breaks information +sets based on the action taken at a particular node. Click on a node +at which the action taken is to be made known subsequently to other +players, and select :menuselection:`Edit --> Reveal`. This displays a +dialog listing the players in the game. Check the boxes next to the +players who observe the outcome of the move at the node, and click +:guilabel:`OK`. The information sets at nodes below the selected one +are adjusted based on the action selected at this node. + +.. note:: + + The reveal operation only has an effect at the time it is done. In + particular, it does not enforce the separation of information sets + based on this information during subsequent editing of the game. + + +Outcomes and payoffs +-------------------- + +Gambit supports the specification of payoffs at any node in a game +tree, whether terminal or not. Each node is created with +no outcome attached; in this case, the payoff at each node is zero to +all players. These are indicated in the game tree by the presence of +a :guilabel:`(u)` in light grey to the right of a node. + +To set the payoffs at a node, double-click on the +:guilabel:`(u)` to the right +of the node. This creates a new outcome at the node, with payoffs of +zero for all players, and displays an editor to set the payoff of the +first player. + +The payoff to a player for an outcome can be edited by double-clicking +on the payoff entry. This action creates a text edit control in which +the payoff to that player can be modified. Edits to the payoff can be +accepted by pressing the :kbd:`Enter` key. In addition, accepting the +payoff by pressing the :kbd:`Tab` key both stores the changes to the +player's payoff, and advances the editor to the payoff for the next +player at that outcome. + +Outcomes may also be moved or copied using a drag-and-drop idiom. +Left-clicking and dragging an outcome to another node moves the +outcome from the original node to the target node. Copying an outcome +may be accomplished by doing this same action while holding down the +Control (:kbd:`Ctrl`) key on the keyboard. + + + +When using the copy idiom described above, the action assigns the same +outcome to both the involved nodes. Therefore, if subsequently the +payoffs of the outcome are edited, the payoffs at both nodes will be +modified. To copy the outcome in such a way that the outcome at the +target node is a different outcome from the one at the source, but +with the same payoffs, hold down the :kbd:`Shift` key instead of the +:kbd:`Control` key while dragging. + +To remove an outcome from a node, click on the node, and +select :menuselection:`Edit --> Remove outcome`. + + + +Formatting and labeling the tree +-------------------------------- + +Gambit offers some options for customizing the display of game trees. + + +Labels on nodes and branches +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The information displayed at the nodes and on the branches of the tree +can be configured by selecting :menuselection:`Format --> Labels`, +which displays the :guilabel:`tree labels` dialog. + +.. image:: screens/labels.* + :width: 33% + :alt: tree labels dialog + :align: right + :target: _images/labels.png + +Above and below each node, the following information can be displayed: + + +:guilabel:`No label` + The space is left blank. + +:guilabel:`The node's label` + The text label assigned to the node. (This is the + default labeling above each node.) + +:guilabel:`The player's name` + The name of the player making the move at the node. + +:guilabel:`The information set's label` + The name of the information set to + which the node belongs. + +:guilabel:`The information set's number` + A unique identifier of the information + set, in the form player number:information set number. (This is the + default labeling below each node.) + +:guilabel:`The realization probability` + The probability the node is reached. + (Only displayed when a behavior strategy is selected to be displayed + on the tree.) + +:guilabel:`The belief probability` + The probability a player assigns to being at + the node, conditional on reaching the information set. (Only displayed + when a behavior strategy is selected to be displayed on the tree.) + +:guilabel:`The payoff of reaching the node` + The expected payoff to the player + making the choice at the node, conditional on reaching the node. (Only + displayed when a behavior strategy is selected to be displayed on the + tree.) + + +Above and below each branch, the following information can be +displayed: + + +:guilabel:`No label` + The space is left blank. + +:guilabel:`The name of the action` + The name of the action taken on the branch. + (This it the default labeling above the branch.) + +:guilabel:`The probability the action is played` + For chance actions, the + probability the branch is taken; this is always displayed. For player + actions, the probability the action is taken in the selected profile + (only displayed when a behavior strategy is selected to be displayed + on the tree). In some cases, behavior strategies do not fully specify + behavior sufficiently far off the equilibrium path; in such cases, an + asterisk is shown for such action probabilities. (This is the default + labeling below each branch.) + +:guilabel:`The value of the action` + The expected payoff to the player of taking + the action, conditional on reaching the information set. (Only + displayed when a behavior strategy is selected to be displayed on the + tree.) + + + +.. _gui-tree-layout: + +Controlling the layout of the tree +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gambit implements an automatic system for layout out game trees, which +provides generally good results for most games. These can be adjusted +by selecting :menuselection:`Format --> Layout`. +The layout parameters are organized on three tabs. + +.. image:: screens/layoutnodes.* + :width: 33% + :alt: layout options dialog, nodes tab + :align: right + :target: _images/layoutnodes.png + +The first tab, +labeled :guilabel:`Nodes`, controls the size, location, and +rendering of nodes in the tree. +Nodes can be indicated using one +of five tokens: a horizontal line (the "traditional" Gambit style from +previous versions), a box, a diamond, an unfilled circle, and a filled +circle). These can be set independently to distinguish chance and +terminal nodes from player nodes. + +The sizing of nodes can be configured for best results. Gambit styling +from previous versions used the horizontal line tokens with relatively +long lines; when using the other tokens, smaller node sizes often look +better. + +.. image:: screens/layoutbranches.* + :width: 33% + :alt: layout options dialog, branches tab + :align: right + :target: _images/layoutbranches.png + +The layout algorithm is based upon identifying the location of +terminal nodes. The vertical spacing between these nodes can be set; +making this value larger will tend to give the tree a larger vertical +extent. + +The second tab, +labeled :guilabel:`Branches`, controls the display of the branches +of the tree. +The traditional Gambit way of drawing branches is a "fork-tine" +approach, in which there is a flat part at the end of each branch at +which labels are displayed. Alternatively, branches can be drawn +directly between nodes by setting :guilabel:`Draw branches` +to using straight +lines between nodes. With this setting, labels are now displayed at +points along the (usually) diagonal branches. Labels are usually shown +horizontally; however, they can be drawn rotated parallel to the +branches by setting :guilabel:`Draw labels` to rotated. + +The rotated label drawing is experimental, and does not always look +good on screen. + +.. image:: screens/layoutinfosets.* + :width: 33% + :alt: layout options dialog, information sets tab + :align: right + :target: _images/layoutinfosets.png + +The length used for branches and their tines, if drawn, can be +configured. Longer branch and tine lengths give more space for longer +labels to be drawn, at the cost of giving the tree a larger horizontal +extent. + +Finally, display of the information sets in the game is configured +under the tab labeled :guilabel:`Information sets`. +Members of information sets are +by default connected using a "bubble" similar to that drawn in +textbook diagrams of games. The can be modified to use a single line +to connect nodes in the same information set. In conjunction with +using lines for nodes, this can sometimes lead to a more compact +representation of a tree where there are many information sets at the +same horizontal location. + +The layout of the tree may be such that members of the same +information set appear at different horizontal locations in the tree. +In such a case, by default, Gambit draws a horizontal arrow pointing +rightward or leftward to indicate the continuation of the information +set, as illustrated in the diagram nearby. + +.. image:: screens/connectinfoset.* + :width: 33% + :alt: information sets spanning multiple levels + :align: right + :target: _images/connectinfoset.png + +These connections can be disabled by setting +:guilabel:`Connect members of information +sets` to :guilabel:`only when on the same level`. +In addition, drawing information +set indicators can be disabled entirely by setting this to invisibly +(don't draw indicators). + + +Selecting fonts and colors +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To select the font used to draw the labels in the tree, select +:menuselection:`Format --> Font`. +The standard font selection dialog for the operating +system is displayed, showing the fonts available on the system. Since +available fonts vary across systems, when opening a workbook on a +system different from the system on which it was saved, Gambit tries +to match the font style as closely as possible when the original font +is not available. + +The color-coding for each player can be changed by clicking on the +color icon to the left of the corresponding player. + diff --git a/doc/gui.export.rst b/doc/gui.export.rst new file mode 100644 index 000000000..e2e2333fe --- /dev/null +++ b/doc/gui.export.rst @@ -0,0 +1,74 @@ +Printing and exporting games +============================ + +Gambit supports (almost) WYSIWYG (what you see is what you get) output +of both extensive and strategic games, both to a printer and to +several graphical formats. For all of these operations, the game is +drawn exactly as currently displayed on the screen, including whether +the extensive or strategic representation is used, the layout, colors +for players, dominance and probability indicators, and so forth. + + + +Printing a game +--------------- + +To print the game, press :kbd:`Ctrl`-:kbd:`P`, select +:menuselection:`File --> Print`, or click +the printer icon on the toolbar. The game is scaled so that the +printout fits on one page, while maintaining the same ratio of +horizontal to vertical size; that is, the scaling factor is the same +in both horizontal and vertical dimensions. + +Note that especially for extensive games, one dimension of the tree is +much larger than the other. Typically, the extent of the tree +vertically is much greater than its horizontal extent. Because the +printout is scaled to fit on one page, printing such a tree will +generally result in what appears to be a thin line running vertically +down the center of the page. This is in fact the tree, shrunk so the +large vertical dimension fits on the page, meaning that the horizontal +dimension, scaled at the same ratio, becomes very tiny. + + + +Saving to a graphics file +------------------------- + +Gambit supports export to five graphical file formats: + + ++ Windows bitmaps ( .bmp ) ++ JPEG, a lossy compressed format ( .jpg , .jpeg ) ++ PNG, a lossless compressed format ( .png ); these are similar to + GIFs ++ Encapsulated PostScript ( .ps ) ++ Scalable vector graphics ( .svg ) + +To export a game to one of these formats, select +:menuselection:`File --> Export`, and +select the corresponding menu entry. + +The Windows bitmap and PNG formats are generally recommended for +export, as they both are lossless formats, which will reproduce the +game image exactly as in Gambit. PNG files use a lossless compression +algorithm, so they are typically much smaller than the Windows bitmap +for the same game. Not all image viewing and manipulation tools handle +PNG files; in those cases, use the Windows bitmap output instead. JPEG +files use a compression algorithm that only approximates the original +version, which often makes it ill-suited for use in saving game +images, since it often leads to "blocking" in the image file. + +For all three of these formats, the dimensions of the exported graphic +are determined by the dimensions of the game as drawn on screen. Image +export is only supported for games which are less than about 65000 +pixels in either the horizontal or vertical dimensions. This is +unlikely to be a practical problem, since such games are so large they +usually cannot be drawn in such a way that a human can make sense of +them. + +Encapsulated PostScript output is generally useful for inclusion in +LaTeX and other scientific document preparation systems. This is a +vector-based output, and thus can be rescaled much more effectively +than the other output formats. + + diff --git a/doc/gui.general.rst b/doc/gui.general.rst new file mode 100644 index 000000000..d4568d6bf --- /dev/null +++ b/doc/gui.general.rst @@ -0,0 +1,110 @@ +General concepts +================ + +General layout of the main window +--------------------------------- + +.. image:: screens/overview.* + :width: 33% + :alt: the default extensive game at launch + :align: right + :target: _images/overview.png + +The frame presenting a game consists of two principal panels. The main +panel, to the right, displays the game graphically; in this case, +showing the game tree of a simple one-card poker game. To the left is +the player panel, which lists the players in the game; here, Fred and +Alice are the players. Note that where applicable, information is +color-coded to match the colors assigned to the players: Fred's moves +and payoffs are all presented in red, and Alice's in blue. The color +assigned to a player can be changed by clicking on the color icon +located to the left of the player's name on the player panel. Player +names are edited by clicking on the player's name, and editing the +name in the text control that appears. + +Two additional panels are available. Selecting +:menuselection:`Tools --> Dominance` toggles +the display of an additional toolbar across the top of the window. +This toolbar controls the indication and elimination of actions or +strategies that are dominated. The use of this toolbar is discussed in +:ref:`dominated-strategies`. + +Selecting :menuselection:`View --> Profiles`, +or clicking the show profiles icon on the +toolbar, toggles the display of the list of computed strategy profiles +on the game. More on the way the interface handles the computation of +Nash equilibria and other kinds of strategy profiles is presented +in :ref:`computing-equilibria`. + + + +Payoffs and probabilities in Gambit +----------------------------------- + +Gambit stores all payoffs in games in an arbitrary-precision format. +Payoffs may be entered as decimal numbers with arbitrarily many +decimal places. In addition, Gambit supports representing payoffs +using rational numbers. So, for example, in any place in which a +payoff may appear, either an outcome of an extensive game or a payoff +entry in a strategic game, the payoff one-tenth may be entered either +as .1 or 1/10. + +The advantage of this format is that, in certain circumstances, Gambit +may be able to compute equilibria exactly. In addition, some methods +for computing equilibria construct good numerical approximations to +equilibrium points. For these methods, the computed equilibria are +stored in floating-point format. To increase the number of decimal +places shown for these profiles, click the increase decimals icon . To +decrease the number of decimal places shown, click the decrease +decimals icon . + +Increasing or decreasing the number of decimals displayed in +computed strategy profiles will not have any effect on the display of +outcome payoffs in the game itself, since those are stored in +arbitrary precision. + + + +A word about file formats +------------------------- + +The graphical interface manipulates several different file formats for +representing games. This section gives a quick overview of those +formats. + +Gambit has for many years supported two file formats for representing +games, one for extensive games (typically using the filename extension +.efg) and one for strategic games (typically using the filename +extension .nfg). These file formats are recognized by all Gambit +versions dating back to release 0.94 in 1995. (Users interested in the +details of these file formats can consult :ref:`file-formats` +for more information.) + +Beginning with release 2005.12.xx, the graphical interface now reads +and writes a new file format, which is referred to as a"Gambit +workbook." This extended file format stores not only the +representation of the game, but also additional information, including +parameters for laying out the game tree, the colors assigned to +players, any equilibria or other analysis done on the game, and so +forth. So, for example, the workbook file can be used to store the +analysis of a game and then return to it. These files by convention +end in the extension .gbt. + +The graphical interface will read files in all three formats: .gbt, +.efg, and .nfg. The "Save" and "Save as" commands, however, always +save in the Gambit workbook (.gbt) format. To save the game itself as +an extensive (.efg) or strategic (.nfg) game, use the items on the +"Export" submenu of the "File" menu. This is useful in interfacing +with older versions of Gambit, with other tools which read and write +those formats, and in using the underlying Gambit analysis command- +line tools directly, as those programs accept .efg or .nfg game files. +Users primarily interested in using Gambit solely via the graphical +interface are encouraged to use the workbook (.gbt) format. + + + +As it is a new format, the Gambit workbook format is still under +development and may change in details. It is intended that newer +versions of the graphical interface will still be able to read +workbook files written in older formats. + diff --git a/doc/gui.nash.rst b/doc/gui.nash.rst new file mode 100644 index 000000000..43f8b8381 --- /dev/null +++ b/doc/gui.nash.rst @@ -0,0 +1,206 @@ +.. _computing-equilibria: + +Computing Nash equilibria +========================= + +Gambit offers broad support for computing Nash equilibria in both +extensive and strategic games. To access the provided algorithms for +computing equilibria, select :menuselection:`Tools --> Equilibrium`, +or click on the +calculate icon on the toolbar. + + +Selecting the method of computing equilibria +-------------------------------------------- + +The process of computing Nash equilibria in extensive and strategic +games is similar. This section focuses on the case of extensive games; +the process for strategic games is analogous, except the extensive +game-specific features, such as displaying the profiles on the game +tree, are not applicable. + +Gambit provides guidance on the options for computing Nash equilibria +in a dialog. +The methods applicable to a particular game depend on three criteria: +the number of equilibria to compute, whether the computation is to be +done on the extensive or strategic games, and on details of the game, +such as whether the game has two players or more, and whether the game +is constant-sum. + +.. image:: screens/nash.* + :width: 33% + :alt: dialog for computing Nash equilibria + :align: right + :target: _images/nash.png + +The first step in finding equilibria is to specify how many equilibria +are to be found. Some algorithms for computing equilibria are adapted +to finding a single equilibrium, while others attempt to compute the +whole equilibrium set. The first drop-down in the dialog specifies how +many equilibria to compute. In this drop-down there are options for +:guilabel:`as many equilibria as possible` and, for two-player games, +:guilabel:`all equilibria`. For some games, there exist algorithms which will +compute many equilibria (relatively) efficiently, but are not +guaranteed to find all equilibria. + +To simplify this process of choosing the method to compute equilibria +in the second drop-down, Gambit provides for any game "recommended" +methods for computing one, some, and all Nash equilibria, +respectively. These methods are selected based on experience as to the +efficiency and reliability of the methods, and should generally work +well on most games. For more control over the process, the user can +select from the second drop-down in the dialog one of the appropriate +methods for computing equilibria. This list only shows the methods +which are appropriate for the game, given the selection of how many +equilibria to compute. More details on these methods are contained +in :ref:`command-line`. + +.. image:: screens/computing.* + :width: 33% + :alt: dialog for monitoring computation of equilibria + :align: right + :target: _images/computing.png + +Finally, for extensive games, there is an option of whether to use the +extensive or strategic game for computation. In general, computation +using the extensive game is preferred, since it is often a +significantly more compact representation of the strategic +characeteristics of the game than the reduced strategic game is. + +For even moderate sized games, computation of equilibrium can be a +time-intensive process. Gambit runs all computations in the +background, and displays a dialog +showing all equilibria computed so +far. The computation can be cancelled at any time by clicking on the +cancel icon , which terminates the computation but keeps any +equilibria computed. + + + +Viewing computed profiles in the game +------------------------------------- + +After computing equilibria, a panel showing the list of equilibria +computed is displayed automatically. The display of this panel can be +toggled by selecting :menuselection:`View --> Profiles`, +or clicking on the playing card +icon on the toolbar. + +.. image:: screens/profiles.* + :width: 33% + :alt: poker game with the unique equilibrium displayed + :align: right + :target: _images/profiles.png + +This game has a unique equilibrium in which Fred raises after Red with +probability one, and raises with probability one-third after Black. +Alice, at her only information set, plays meet with probability two- +thirds and raise with probability one-third. + +This equilibrium is displayed in a table in the profiles panel. If +more than one equilibrium is found, this panel lists all equilibria +found. Equilibria computed are grouped by separate computational runs; +computing equilibria using a different method (or different settings) +will add a second list of profiles. The list of profiles displayed is +selected using the drop-down at the top left of the profiles panel; in +the screenshot, it is set to +:guilabel:`Profiles 1`. A +brief description of the method used to compute the equilibria is +listed across the top of the profiles panel. + +The currently selected equilibrium is shown in bold in the profiles +listing, and information about this equilibrium is displayed in the +extensive game. In the figure, the probabilities of selecting each +action are displayed below each branch of the tree. (This is the +default Gambit setting; see :ref:`gui-tree-layout` +for configuring the labeling of trees.) +Each branch of the tree also shows a black line, the length of which +is proportional to the probability with which the action is played. + +.. image:: screens/beliefs.* + :width: 33% + :alt: poker game with the beliefs at Alice's top node + :align: right + :target: _images/beliefs.png + +Clicking on any node in the tree displays additional information about +the profile at that node. +The player panel displays +information relevant to the selected node, including the payoff to all players +conditional on reaching the node, as well as information about Alice's +beliefs at the node. + +The computed profiles can also be viewed in the reduced strategic +game. Clicking on the strategic game icon changes the view to the +reduced strategic form of the game, and shows the equilibrium profiles +converted to mixed strategies in the strategic game. + + + +Computing quantal response equilibria +------------------------------------- + +Gambit provides methods for computing the logit quantal response +equilibrium correspondence for extensive games [McKPal98]_ +and strategic games [McKPal95]_, +using the tracing method of [Tur05]_. + +.. image:: screens/qre.* + :width: 33% + :alt: quantal response equilibria + :align: right + :target: _images/qre.png + + +To compute the correspondence, select :menuselection:`Tools --> Qre`. +If viewing an +extensive game, the agent quantal response equilibrium correspondence +is computed; if viewing a strategic game (including the reduced +strategic game derived from an extensive game), the correspondence is +computed in mixed strategies. + +The computed correspondence values can be saved to a CSV (comma- +separated values) file by clicking the button labeled +:guilabel:`Save correspondence to .csv file`. +This format is suitable for reading by a +spreadsheet or graphing application. + + + +Quantal response equilibria in strategic games (experimental) +------------------------------------------------------------- + +There is an experimental graphing interface for quantal response +equilibria in strategic games. +The graph by default plots the probabilities of all strategies, color- +coded by player, as a function of the lambda parameter. The lambda +values on the horizontal axis are plotted using a sigmoid +transformation; the Graph scaling value controls the shape of this +transformation. Lower values of the scaling give more graph space to +lower values of lambda; higher values of the scaling give more space +to higher values of lambda. + +.. image:: screens/logit.* + :width: 33% + :alt: quantal response equilibria graphing + :align: right + :target: _images/logit.png + +The strategies graphed are indicated in the panel at the left of the +window. Clicking on the checkbox next to a strategy toggles whether it +is displayed in the graph. + +The data points computed in the correspondence can be viewed (as in +the extensive game example above) by clicking on the show data icon on +the toolbar. The data points can be saved to a CSV file by clicking on +the . + +To zoom in on a portion of the graph of interest, hold down the left +mouse button and drag a rectangle on the graph. The plot window zooms +in on the portion of the graph selected by that rectangle. To restore +the graph view to the full graph, click on the zoom to fit icon . + +To print the graph as shown, click on the print icon . Note that this +is very experimental, and the output may not be very satisfactory yet. + + diff --git a/doc/gui.nfg.rst b/doc/gui.nfg.rst new file mode 100644 index 000000000..69ae17f63 --- /dev/null +++ b/doc/gui.nfg.rst @@ -0,0 +1,151 @@ +Strategic games +=============== + +Gambit has full support for constructing and manipulating arbitrary +N-player strategic (also known as normal form) games. + +For extensive games, Gambit automatically computes the corresponding +reduced strategic game. To view the reduced strategic game +corresponding to an extensive game, select +:menuselection:`View --> Strategic game` or +click the strategic game table icon on the toolbar. + + + +The strategic games computed by Gambit as the reduced strategic game +of an extensive game cannot be modified directly. Instead, edit the +original extensive game; Gambit automatically recomputes the strategic +game after any changes to the extensive game. + +Strategic games may also be input directly. To create a new strategic +game, select :menuselection:`File --> New --> Strategic game`, +or click the new strategic game icon on the toolbar. + + + +Navigating a strategic game +--------------------------- + +Gambit displays a strategic game in table form. All players are +assigned to be either row players or column players, and the payoffs +for each entry in the strategic game table correspond to the payoffs +corresponding to the situation in which all the row players play the +strategy specified on that row for them, and all the column players +play the strategy specified on that column for them. + +.. image:: screens/pd1.* + :width: 33% + :alt: a prisoner's dilemma game + :align: right + :target: _images/pd1.png + +For games with two players, this presentation is by default configured +to be similar to the standard presenation of strategic games as +tables, in which one player is assigned to be the "row" player and the +other the "column" player. However, Gambit permits a more flexible +assignment, in which multiple players can be assigned to the rows and +multiple players to the columns. This is of particular use for games +with more than two players. In print, a three-player strategic game is +usually presented as a collection of tables, with one player choosing +the row, the second the column, and the third the table. Gambit +presents such games by hierarchially listing the strategies of one or +more players on both rows and columns. + +The hierarchical presentation of the table is similar to that of a +contingency table in a spreadsheet application. +Here, Alice, +shown in red, has her strategies listed on the rows of the table, and +Bob, shown in blue, has his strategies listed on the columns of the +table. + +The assignment of players to row and column roles is fully +customizable. To change the assignment of a player, drag the person +icon appearing to the left of the player's name on the player toolbar +to either of the areas in the payoff table displaying the strategy +labels. + +.. image:: screens/pd2.* + :width: 33% + :alt: a prisoner's dilemma game, with contingencies in + list style + :align: right + :target: _images/pd2.png + +For example, dragging the player icon from the left of Bob's name in +the list of players and dropping it on the right side of Alice's +strategy label column changes the display of the game as in +Here, the strategies are shown in a +hierarchical format, enumerating the outcomes of the game first by +Alice's (red) strategy choice, then by Bob's (blue) strategy choice. + +Alternatively, the game can be displayed by listing the outcomes with +Bob's strategy choice first, then Alice's. Drag Bob's player icon and +drop it on the left side of Alice's strategy choices, and the game +display changes to organize the outcomes first by Bob's action, then +by Alice's. + +The same dragging operation can be used to assign players to the +columns. Assigning multiple players to the columns gives the same +hierarchical presentation of those players' strategies. Dropping a +player above another player's strategy labels assigns him to a higher +level of the column player hierarchy; dropping a player below another +player's strategy labels assigns him to a lower level of the column +player hierarchy. + +.. image:: screens/pd3.* + :width: 33% + :alt: another view of the same prisoner's dilemma game. + :align: right + :target: _images/pd3.png + +As the assignment of players in the row and column +hierarchies changes, the ordering of the payoffs in each cell of the +table also changes. In all cases, the color-coding of the entries +identifies the player to whom each payoff corresponds. The ordering +convention is chosen so that for a two player game in which one player +is a row player and the other a column player, the row player's payoff +is shown first, followed by the column player, which is the most +common convention in print. + + + +Adding players and strategies +----------------------------- + +To add an additional player to the game, use the menu item +:menuselection:`Edit --> Add player`, +or the corresponding toolbar icon . The newly created player +has one strategy, by default labeled with the number :guilabel:`1`. + +Gambit supports arbitrary numbers of strategies for each player. To +add a new strategy for a player, click the new strategy icon located +to the left of that player's name. + +To edit the names of strategies, click on any cell in the strategic +game table where the strategy label appears, and edit the label using +the edit control. + + + +Editing payoffs +--------------- + +Payoffs for each player are specified individually for each +contingency, or collection of strategies, in the game. To edit any +payoff in the table, click that cell in the table and edit the payoff. +Pressing the Escape key (:kbd:`Esc`) cancels any editing of the payoff +and restores the previous value. + +To speed entry of many payoffs, as is typical when creating a new +game, accepting a payoff entry via the :kbd:`Tab` key automatically moves +the edit control to the next cell to the right. If the payoff is the +last payoff listed in a row of the table, the edit control wraps +around to the first payoff in the next row; if the payoff is in the +last row, the edit control wraps around to the first payoff in the +first row. So a strategic game payoff table can be quickly entered by +clicking on the first payoff in the upper-left cell of the table, +inputting the payoff for the first (row) player, pressing the :kbd:`Tab` +key, inputting the payoff for the second (column) player, pressing the +:kbd:`Tab` key, and so forth, until all the payoff entries in the table +have been filled. + diff --git a/doc/gui.rst b/doc/gui.rst index 4fac02cd9..4138b8fd3 100644 --- a/doc/gui.rst +++ b/doc/gui.rst @@ -1,7 +1,8 @@ .. _section-gui: +*********************** The graphical interface -======================= +*********************** Gambit's graphical user interface provides an "integrated development environment" to help visually construct @@ -25,1099 +26,12 @@ systematically, it is recommended to use :ref:`the Python package `. +.. toctree:: + :maxdepth: 2 -General concepts ----------------- - -General layout of the main window -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: screens/overview.* - :width: 33% - :alt: the default extensive game at launch - :align: right - :target: _images/overview.png - -The frame presenting a game consists of two principal panels. The main -panel, to the right, displays the game graphically; in this case, -showing the game tree of a simple one-card poker game. To the left is -the player panel, which lists the players in the game; here, Fred and -Alice are the players. Note that where applicable, information is -color-coded to match the colors assigned to the players: Fred's moves -and payoffs are all presented in red, and Alice's in blue. The color -assigned to a player can be changed by clicking on the color icon -located to the left of the player's name on the player panel. Player -names are edited by clicking on the player's name, and editing the -name in the text control that appears. - -Two additional panels are available. Selecting -:menuselection:`Tools --> Dominance` toggles -the display of an additional toolbar across the top of the window. -This toolbar controls the indication and elimination of actions or -strategies that are dominated. The use of this toolbar is discussed in -:ref:`dominated-strategies`. - -Selecting :menuselection:`View --> Profiles`, -or clicking the show profiles icon on the -toolbar, toggles the display of the list of computed strategy profiles -on the game. More on the way the interface handles the computation of -Nash equilibria and other kinds of strategy profiles is presented -in :ref:`computing-equilibria`. - - - -Payoffs and probabilities in Gambit -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Gambit stores all payoffs in games in an arbitrary-precision format. -Payoffs may be entered as decimal numbers with arbitrarily many -decimal places. In addition, Gambit supports representing payoffs -using rational numbers. So, for example, in any place in which a -payoff may appear, either an outcome of an extensive game or a payoff -entry in a strategic game, the payoff one-tenth may be entered either -as .1 or 1/10. - -The advantage of this format is that, in certain circumstances, Gambit -may be able to compute equilibria exactly. In addition, some methods -for computing equilibria construct good numerical approximations to -equilibrium points. For these methods, the computed equilibria are -stored in floating-point format. To increase the number of decimal -places shown for these profiles, click the increase decimals icon . To -decrease the number of decimal places shown, click the decrease -decimals icon . - -Increasing or decreasing the number of decimals displayed in -computed strategy profiles will not have any effect on the display of -outcome payoffs in the game itself, since those are stored in -arbitrary precision. - - - -A word about file formats -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The graphical interface manipulates several different file formats for -representing games. This section gives a quick overview of those -formats. - -Gambit has for many years supported two file formats for representing -games, one for extensive games (typically using the filename extension -.efg) and one for strategic games (typically using the filename -extension .nfg). These file formats are recognized by all Gambit -versions dating back to release 0.94 in 1995. (Users interested in the -details of these file formats can consult :ref:`file-formats` -for more information.) - -Beginning with release 2005.12.xx, the graphical interface now reads -and writes a new file format, which is referred to as a"Gambit -workbook." This extended file format stores not only the -representation of the game, but also additional information, including -parameters for laying out the game tree, the colors assigned to -players, any equilibria or other analysis done on the game, and so -forth. So, for example, the workbook file can be used to store the -analysis of a game and then return to it. These files by convention -end in the extension .gbt. - -The graphical interface will read files in all three formats: .gbt, -.efg, and .nfg. The "Save" and "Save as" commands, however, always -save in the Gambit workbook (.gbt) format. To save the game itself as -an extensive (.efg) or strategic (.nfg) game, use the items on the -"Export" submenu of the "File" menu. This is useful in interfacing -with older versions of Gambit, with other tools which read and write -those formats, and in using the underlying Gambit analysis command- -line tools directly, as those programs accept .efg or .nfg game files. -Users primarily interested in using Gambit solely via the graphical -interface are encouraged to use the workbook (.gbt) format. - - - -As it is a new format, the Gambit workbook format is still under -development and may change in details. It is intended that newer -versions of the graphical interface will still be able to read -workbook files written in older formats. - - - -Extensive games ---------------- - -The graphical interface provides a flexible set of operations for -constructing and editing general extensive games. These are outlined -below. - - - -Creating a new extensive game -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To create a new extensive game, select -:menuselection:`File --> New --> Extensive game`, or -click on the new extensive game icon . The extensive game created is a -trivial game with two players, named by default -:guilabel:`Player 1` and :guilabel:`Player 2`, -with one node, which is both the root and terminal node of the game. -In addition, extensive games have a special player labeled -:guilabel:`Chance`, -which is used to represent random events not controlled by any of the -strategic players in the game. - - -.. _adding-moves: - -Adding moves -~~~~~~~~~~~~ - -There are two options for adding moves to a tree: drag-and-drop -and the :guilabel:`Insert move` dialog. - -#. Moves can be added to the - tree using a drag-and-drop idiom. From the player list window, drag - the player icon located to the left of the player who will have the - move to any terminal node in the game tree. The tree will be extended - with a new move for that player, with two actions at the new move. - Adding a move for the chance player is done the same way, except the - dice icon appearing to the left of the chance player in the player - list window is used instead of the player icon. For the chance player, - the two actions created will each be given a probability weight of - one-half. If the desired move has more than two actions, additional - actions can be added by dragging the same player's icon to the move - node; this will add one action to the move each time this is done. - -.. image:: screens/insertmove.* - :width: 33% - :alt: insert move dialog - :align: right - :target: _images/insertmove.png - -2. Click on any terminal node in - the tree, and select :menuselection:`Edit --> Insert move` - to display the :guilabel:`insert move` dialog. - The dialog is intended to read like a sentence: - - + The first control specifies the player who will make the move. The - move can be assigned to a new player by specifying - :guilabel:`Insert move for a new player here`. - + The second control selects the information set to which to add the - move. To create the move in a new information set, select - :guilabel:`at a new information set` for this control. - + The third control sets the number of actions. This control is - disabled unless the second control is set to - :guilabel:`at a new information set`. - Otherwise, it is set automatically to the number of actions at - the selected information set. - -The two methods can be useful in different contexts. -The drag-and-drop approach -is a bit quicker to use, especially when creating trees that have few -actions at each move. The dialog approach is a bit more flexible, in -that a move can be added for a new, as-yet-undefined player, a move -can be added directly into an existing information set, and a move can -be immediately given more than two actions. - -.. _copying-trees: - -Copying and moving subtrees -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Many extensive games have structures that appear in multiple parts of -the tree. It is often efficient to create the structure once, and then -copy it as needed elsewhere. - -Gambit provides a convenient idiom for this. Clicking on any -nonterminal node and dragging to any terminal node implements a move -operation, which moves the entire subtree rooted at the original, -nonterminal node to the terminal node. - -To turn the operation into a copy operation: - -+ On Windows and Linux systems, hold down the :kbd:`Ctrl` key during - the operation. -+ On OS X, hold down the :kbd:`Cmd` key when starting the - drag operation, then release prior to dropping. - -The entire subtree rooted at the original node is copied, -starting at the terminal node. In this copy operation, each node in -the copied image is placed in the same information set as the -corresponding node in the original subtree. - -Copying a subtree to a terminal node in that subtree is also -supported. In this case, the copying operation is halted when reaching -the terminal node, to avoid an infinite loop. Thus, this feature -can also be helpful in constructing multiple-stage games. - - - -Removing parts of a game tree -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Two deletion operations are supported on extensive games. To delete -the entire subtree rooted at a node, click on that node and select -:menuselection:`Edit --> Delete subtree`. - -To delete an individual move from the game, click on one of the direct -children of that node, and select -:menuselection:`Edit --> Delete parent`. This operation -deletes the parent node, as well as all the children of the parent -other than the selected node. The selected child node now takes the -place of the parent node in the tree. - - - -Managing information sets -~~~~~~~~~~~~~~~~~~~~~~~~~ - - -Gambit provides several methods to help manage the information -structure in an extensive game. - -When building a tree, new moves can be placed in a given information -set using the :ref:`Insert move dialog `. -Additionally, new moves can be -created using the drag-and-drop idiom by holding down the :kbd:`Shift` -key and dragging a node in the tree. During the drag operation, the -cursor changes to the move icon . Dropping the move icon on another -node places the target node in the same information set as the node -where the drag operation began. - -.. image:: screens/editnode.* - :width: 33% - :alt: node properties dialog - :align: right - :target: _images/editnode.png - -The information set to which a node belongs can also be set by -selecting :menuselection:`Edit --> Node`. This displays the -:guilabel:`node properties` dialog. -The :guilabel:`Information set` dropdown defaults -to the current information set to which the node belongs, and contains -a list of all other information sets in the game which are compatible -with the node, that is, which have the same number of -actions. Additionally, the node can be moved to a new, singleton -information set by setting this dropdown to the :guilabel:`New -information set` entry. - - - -When building out a game tree using the :ref:`drag-and-drop approach -` to copying portions of the tree, -the nodes created in -the copy of the subtree remain in the same information set as the -corresponding nodes in the original subtree. In many cases, though, -these trees differ in the information available to some or all of the -players. To help speed the process of adjusting information sets in -bulk, Gambit offers a "reveal" operation, which breaks information -sets based on the action taken at a particular node. Click on a node -at which the action taken is to be made known subsequently to other -players, and select :menuselection:`Edit --> Reveal`. This displays a -dialog listing the players in the game. Check the boxes next to the -players who observe the outcome of the move at the node, and click -:guilabel:`OK`. The information sets at nodes below the selected one -are adjusted based on the action selected at this node. - - -This is an operation that is easier to see than the explain. See the -poker tutorial -(`flash version -`_; -`PDF version -`_) -for an application of the -revelation operation in conjunction with the tree-copy operation. - - -.. note:: - - The reveal operation only has an effect at the time it is done. In - particular, it does not enforce the separation of information sets - based on this information during subsequent editing of the game. - - - - -Outcomes and payoffs -~~~~~~~~~~~~~~~~~~~~ - -Gambit supports the specification of payoffs at any node in a game -tree, whether terminal or not. Each node is created with -no outcome attached; in this case, the payoff at each node is zero to -all players. These are indicated in the game tree by the presence of -a :guilabel:`(u)` in light grey to the right of a node. - -To set the payoffs at a node, double-click on the -:guilabel:`(u)` to the right -of the node. This creates a new outcome at the node, with payoffs of -zero for all players, and displays an editor to set the payoff of the -first player. - -The payoff to a player for an outcome can be edited by double-clicking -on the payoff entry. This action creates a text edit control in which -the payoff to that player can be modified. Edits to the payoff can be -accepted by pressing the :kbd:`Enter` key. In addition, accepting the -payoff by pressing the :kbd:`Tab` key both stores the changes to the -player's payoff, and advances the editor to the payoff for the next -player at that outcome. - -Outcomes may also be moved or copied using a drag-and-drop idiom. -Left-clicking and dragging an outcome to another node moves the -outcome from the original node to the target node. Copying an outcome -may be accomplished by doing this same action while holding down the -Control (:kbd:`Ctrl`) key on the keyboard. - - - -When using the copy idiom described above, the action assigns the same -outcome to both the involved nodes. Therefore, if subsequently the -payoffs of the outcome are edited, the payoffs at both nodes will be -modified. To copy the outcome in such a way that the outcome at the -target node is a different outcome from the one at the source, but -with the same payoffs, hold down the :kbd:`Shift` key instead of the -:kbd:`Control` key while dragging. - -To remove an outcome from a node, click on the node, and -select :menuselection:`Edit --> Remove outcome`. - - - -Formatting and labeling the tree -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Gambit offers some options for customizing the display of game trees. - - -Labels on nodes and branches -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The information displayed at the nodes and on the branches of the tree -can be configured by selecting :menuselection:`Format --> Labels`, -which displays the :guilabel:`tree labels` dialog. - -.. image:: screens/labels.* - :width: 33% - :alt: tree labels dialog - :align: right - :target: _images/labels.png - -Above and below each node, the following information can be displayed: - - -:guilabel:`No label` - The space is left blank. - -:guilabel:`The node's label` - The text label assigned to the node. (This is the - default labeling above each node.) - -:guilabel:`The player's name` - The name of the player making the move at the node. - -:guilabel:`The information set's label` - The name of the information set to - which the node belongs. - -:guilabel:`The information set's number` - A unique identifier of the information - set, in the form player number:information set number. (This is the - default labeling below each node.) - -:guilabel:`The realization probability` - The probability the node is reached. - (Only displayed when a behavior strategy is selected to be displayed - on the tree.) - -:guilabel:`The belief probability` - The probability a player assigns to being at - the node, conditional on reaching the information set. (Only displayed - when a behavior strategy is selected to be displayed on the tree.) - -:guilabel:`The payoff of reaching the node` - The expected payoff to the player - making the choice at the node, conditional on reaching the node. (Only - displayed when a behavior strategy is selected to be displayed on the - tree.) - - -Above and below each branch, the following information can be -displayed: - - -:guilabel:`No label` - The space is left blank. - -:guilabel:`The name of the action` - The name of the action taken on the branch. - (This it the default labeling above the branch.) - -:guilabel:`The probability the action is played` - For chance actions, the - probability the branch is taken; this is always displayed. For player - actions, the probability the action is taken in the selected profile - (only displayed when a behavior strategy is selected to be displayed - on the tree). In some cases, behavior strategies do not fully specify - behavior sufficiently far off the equilibrium path; in such cases, an - asterisk is shown for such action probabilities. (This is the default - labeling below each branch.) - -:guilabel:`The value of the action` - The expected payoff to the player of taking - the action, conditional on reaching the information set. (Only - displayed when a behavior strategy is selected to be displayed on the - tree.) - - - -.. _gui-tree-layout: - -Controlling the layout of the tree -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Gambit implements an automatic system for layout out game trees, which -provides generally good results for most games. These can be adjusted -by selecting :menuselection:`Format --> Layout`. -The layout parameters are organized on three tabs. - -.. image:: screens/layoutnodes.* - :width: 33% - :alt: layout options dialog, nodes tab - :align: right - :target: _images/layoutnodes.png - -The first tab, -labeled :guilabel:`Nodes`, controls the size, location, and -rendering of nodes in the tree. -Nodes can be indicated using one -of five tokens: a horizontal line (the "traditional" Gambit style from -previous versions), a box, a diamond, an unfilled circle, and a filled -circle). These can be set independently to distinguish chance and -terminal nodes from player nodes. - -The sizing of nodes can be configured for best results. Gambit styling -from previous versions used the horizontal line tokens with relatively -long lines; when using the other tokens, smaller node sizes often look -better. - -.. image:: screens/layoutbranches.* - :width: 33% - :alt: layout options dialog, branches tab - :align: right - :target: _images/layoutbranches.png - -The layout algorithm is based upon identifying the location of -terminal nodes. The vertical spacing between these nodes can be set; -making this value larger will tend to give the tree a larger vertical -extent. - -The second tab, -labeled :guilabel:`Branches`, controls the display of the branches -of the tree. -The traditional Gambit way of drawing branches is a "fork-tine" -approach, in which there is a flat part at the end of each branch at -which labels are displayed. Alternatively, branches can be drawn -directly between nodes by setting :guilabel:`Draw branches` -to using straight -lines between nodes. With this setting, labels are now displayed at -points along the (usually) diagonal branches. Labels are usually shown -horizontally; however, they can be drawn rotated parallel to the -branches by setting :guilabel:`Draw labels` to rotated. - -The rotated label drawing is experimental, and does not always look -good on screen. - -.. image:: screens/layoutinfosets.* - :width: 33% - :alt: layout options dialog, information sets tab - :align: right - :target: _images/layoutinfosets.png - -The length used for branches and their tines, if drawn, can be -configured. Longer branch and tine lengths give more space for longer -labels to be drawn, at the cost of giving the tree a larger horizontal -extent. - -Finally, display of the information sets in the game is configured -under the tab labeled :guilabel:`Information sets`. -Members of information sets are -by default connected using a "bubble" similar to that drawn in -textbook diagrams of games. The can be modified to use a single line -to connect nodes in the same information set. In conjunction with -using lines for nodes, this can sometimes lead to a more compact -representation of a tree where there are many information sets at the -same horizontal location. - -The layout of the tree may be such that members of the same -information set appear at different horizontal locations in the tree. -In such a case, by default, Gambit draws a horizontal arrow pointing -rightward or leftward to indicate the continuation of the information -set, as illustrated in the diagram nearby. - -.. image:: screens/connectinfoset.* - :width: 33% - :alt: information sets spanning multiple levels - :align: right - :target: _images/connectinfoset.png - -These connections can be disabled by setting -:guilabel:`Connect members of information -sets` to :guilabel:`only when on the same level`. -In addition, drawing information -set indicators can be disabled entirely by setting this to invisibly -(don't draw indicators). - - -Selecting fonts and colors -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To select the font used to draw the labels in the tree, select -:menuselection:`Format --> Font`. -The standard font selection dialog for the operating -system is displayed, showing the fonts available on the system. Since -available fonts vary across systems, when opening a workbook on a -system different from the system on which it was saved, Gambit tries -to match the font style as closely as possible when the original font -is not available. - -The color-coding for each player can be changed by clicking on the -color icon to the left of the corresponding player. - - -Strategic games ---------------- - -Gambit has full support for constructing and manipulating arbitrary -N-player strategic (also known as normal form) games. - -For extensive games, Gambit automatically computes the corresponding -reduced strategic game. To view the reduced strategic game -corresponding to an extensive game, select -:menuselection:`View --> Strategic game` or -click the strategic game table icon on the toolbar. - - - -The strategic games computed by Gambit as the reduced strategic game -of an extensive game cannot be modified directly. Instead, edit the -original extensive game; Gambit automatically recomputes the strategic -game after any changes to the extensive game. - -Strategic games may also be input directly. To create a new strategic -game, select :menuselection:`File --> New --> Strategic game`, -or click the new strategic game icon on the toolbar. - - - -Navigating a strategic game -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Gambit displays a strategic game in table form. All players are -assigned to be either row players or column players, and the payoffs -for each entry in the strategic game table correspond to the payoffs -corresponding to the situation in which all the row players play the -strategy specified on that row for them, and all the column players -play the strategy specified on that column for them. - -.. image:: screens/pd1.* - :width: 33% - :alt: a prisoner's dilemma game - :align: right - :target: _images/pd1.png - -For games with two players, this presentation is by default configured -to be similar to the standard presenation of strategic games as -tables, in which one player is assigned to be the "row" player and the -other the "column" player. However, Gambit permits a more flexible -assignment, in which multiple players can be assigned to the rows and -multiple players to the columns. This is of particular use for games -with more than two players. In print, a three-player strategic game is -usually presented as a collection of tables, with one player choosing -the row, the second the column, and the third the table. Gambit -presents such games by hierarchially listing the strategies of one or -more players on both rows and columns. - -The hierarchical presentation of the table is similar to that of a -contingency table in a spreadsheet application. -Here, Alice, -shown in red, has her strategies listed on the rows of the table, and -Bob, shown in blue, has his strategies listed on the columns of the -table. - -The assignment of players to row and column roles is fully -customizable. To change the assignment of a player, drag the person -icon appearing to the left of the player's name on the player toolbar -to either of the areas in the payoff table displaying the strategy -labels. - -.. image:: screens/pd2.* - :width: 33% - :alt: a prisoner's dilemma game, with contingencies in - list style - :align: right - :target: _images/pd2.png - -For example, dragging the player icon from the left of Bob's name in -the list of players and dropping it on the right side of Alice's -strategy label column changes the display of the game as in -Here, the strategies are shown in a -hierarchical format, enumerating the outcomes of the game first by -Alice's (red) strategy choice, then by Bob's (blue) strategy choice. - -Alternatively, the game can be displayed by listing the outcomes with -Bob's strategy choice first, then Alice's. Drag Bob's player icon and -drop it on the left side of Alice's strategy choices, and the game -display changes to organize the outcomes first by Bob's action, then -by Alice's. - -The same dragging operation can be used to assign players to the -columns. Assigning multiple players to the columns gives the same -hierarchical presentation of those players' strategies. Dropping a -player above another player's strategy labels assigns him to a higher -level of the column player hierarchy; dropping a player below another -player's strategy labels assigns him to a lower level of the column -player hierarchy. - -.. image:: screens/pd3.* - :width: 33% - :alt: another view of the same prisoner's dilemma game. - :align: right - :target: _images/pd3.png - -As the assignment of players in the row and column -hierarchies changes, the ordering of the payoffs in each cell of the -table also changes. In all cases, the color-coding of the entries -identifies the player to whom each payoff corresponds. The ordering -convention is chosen so that for a two player game in which one player -is a row player and the other a column player, the row player's payoff -is shown first, followed by the column player, which is the most -common convention in print. - - - -Adding players and strategies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To add an additional player to the game, use the menu item -:menuselection:`Edit --> Add player`, -or the corresponding toolbar icon . The newly created player -has one strategy, by default labeled with the number :guilabel:`1`. - -Gambit supports arbitrary numbers of strategies for each player. To -add a new strategy for a player, click the new strategy icon located -to the left of that player's name. - -To edit the names of strategies, click on any cell in the strategic -game table where the strategy label appears, and edit the label using -the edit control. - - - -Editing payoffs -~~~~~~~~~~~~~~~ - -Payoffs for each player are specified individually for each -contingency, or collection of strategies, in the game. To edit any -payoff in the table, click that cell in the table and edit the payoff. -Pressing the Escape key (:kbd:`Esc`) cancels any editing of the payoff -and restores the previous value. - -To speed entry of many payoffs, as is typical when creating a new -game, accepting a payoff entry via the :kbd:`Tab` key automatically moves -the edit control to the next cell to the right. If the payoff is the -last payoff listed in a row of the table, the edit control wraps -around to the first payoff in the next row; if the payoff is in the -last row, the edit control wraps around to the first payoff in the -first row. So a strategic game payoff table can be quickly entered by -clicking on the first payoff in the upper-left cell of the table, -inputting the payoff for the first (row) player, pressing the :kbd:`Tab` -key, inputting the payoff for the second (column) player, pressing the -:kbd:`Tab` key, and so forth, until all the payoff entries in the table -have been filled. - - -.. _dominated-strategies: - -Investigating dominated strategies and actions ----------------------------------------------- - -Selecting :menuselection:`Tools --> Dominance` -toggles the appearance of a toolbar which -can be used to investigate the structure of dominated strategies and -actions. - - - -Dominated actions in extensive game -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In extensive games, the dominance toolbar controls the elimination of actions which are conditionally dominated. - -.. image:: screens/pokerdom1.* - :width: 33% - :alt: the poker game, with the dominance toolbar shown - :align: right - :target: _images/pokerdom1.png - -Actions may be eliminated based on two criteria: - -:guilabel:`Strict dominance` - The action is always worse than another, - regardless of beliefs at the information set; - -:guilabel:`Strict or weak dominance` - There is another action at the information - set that is always at least as good as the action, and strictly better - in some cases. - -.. image:: screens/pokerdom2.* - :width: 33% - :alt: the poker game, with the dominated action eliminated - :align: right - :target: _images/pokerdom2.png - -For example, in the poker game, it is strictly dominated for Fred to -choose Fold after Red. Clicking the next level icon -removes the dominated action from the game display. - -The tree layout remains unchanged, including nodes which can only be -reached using actions which have been eliminated. To compress the tree -to remove the unreachable nodes, check the box labeled -:guilabel:`Show only -reachable nodes`. - -For this game, no further actions can be eliminated. In general, -further steps of elimination can be done by again clicking the next -level icon. The toolbar keeps track of the number of levels of -elimination currently shown; the previous level icon moves up one -level of elimination. - -.. image:: screens/pokerdom3.* - :width: 33% - :alt: the poker game, with only reachable actions shown - :align: right - :target: _images/pokerdom3.png - -The elimination of multiple levels can be automated using the fast -forward icon , which iteratively eliminates dominated actions until no -further actions can be eliminated. The rewind icon restores the -display to the full game. - - - -Dominated strategies in strategic games -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The dominance toolbar operates in strategic games in the same way as -the in the extensive game. Strategies can be eliminated iteratively -based on whether they are strictly or weakly dominated. - -.. image:: screens/pddom1.* - :width: 33% - :alt: the prisoner's dilemma example, with dominated - strategies indicated - :align: right - :target: _images/pddom1.png - -When the dominance toolbar is shown, the strategic game table contains -indicators of strategies that are dominated. -In the prisoner's dilemma, the Cooperate strategy is strictly -dominated for both players. This strict dominance is indicated by the -solid "X" drawn across the corresponding strategy labels for both -players. In addition, the payoffs corresponding to the dominated -strategies are also drawn with a solid "X" across them. Thus, any -contingency in the table containing at least one "X" is a contingency -that can only be reached by at least one player playing a strategy -that is dominated. - - -Strategies that are weakly dominated are similarly indicated, except -the "X" shape is drawn using a thinner, dashed line instead of the -thick, solid line. - -.. image:: screens/pddom2.* - :width: 33% - :alt: the prisoner's dilemma example, with dominated - strategies removed - :align: right - :target: _images/pddom2.png - -Clicking the next level icon removes the strictly dominated strategies -from the display. - - -.. _computing-equilibria: - -Computing Nash equilibria -------------------------- - -Gambit offers broad support for computing Nash equilibria in both -extensive and strategic games. To access the provided algorithms for -computing equilibria, select :menuselection:`Tools --> Equilibrium`, -or click on the -calculate icon on the toolbar. - - -Selecting the method of computing equilibria -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The process of computing Nash equilibria in extensive and strategic -games is similar. This section focuses on the case of extensive games; -the process for strategic games is analogous, except the extensive -game-specific features, such as displaying the profiles on the game -tree, are not applicable. - -Gambit provides guidance on the options for computing Nash equilibria -in a dialog. -The methods applicable to a particular game depend on three criteria: -the number of equilibria to compute, whether the computation is to be -done on the extensive or strategic games, and on details of the game, -such as whether the game has two players or more, and whether the game -is constant-sum. - -.. image:: screens/nash.* - :width: 33% - :alt: dialog for computing Nash equilibria - :align: right - :target: _images/nash.png - -The first step in finding equilibria is to specify how many equilibria -are to be found. Some algorithms for computing equilibria are adapted -to finding a single equilibrium, while others attempt to compute the -whole equilibrium set. The first drop-down in the dialog specifies how -many equilibria to compute. In this drop-down there are options for -:guilabel:`as many equilibria as possible` and, for two-player games, -:guilabel:`all equilibria`. For some games, there exist algorithms which will -compute many equilibria (relatively) efficiently, but are not -guaranteed to find all equilibria. - -To simplify this process of choosing the method to compute equilibria -in the second drop-down, Gambit provides for any game "recommended" -methods for computing one, some, and all Nash equilibria, -respectively. These methods are selected based on experience as to the -efficiency and reliability of the methods, and should generally work -well on most games. For more control over the process, the user can -select from the second drop-down in the dialog one of the appropriate -methods for computing equilibria. This list only shows the methods -which are appropriate for the game, given the selection of how many -equilibria to compute. More details on these methods are contained -in :ref:`command-line`. - -.. image:: screens/computing.* - :width: 33% - :alt: dialog for monitoring computation of equilibria - :align: right - :target: _images/computing.png - -Finally, for extensive games, there is an option of whether to use the -extensive or strategic game for computation. In general, computation -using the extensive game is preferred, since it is often a -significantly more compact representation of the strategic -characeteristics of the game than the reduced strategic game is. - -For even moderate sized games, computation of equilibrium can be a -time-intensive process. Gambit runs all computations in the -background, and displays a dialog -showing all equilibria computed so -far. The computation can be cancelled at any time by clicking on the -cancel icon , which terminates the computation but keeps any -equilibria computed. - - - -Viewing computed profiles in the game -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -After computing equilibria, a panel showing the list of equilibria -computed is displayed automatically. The display of this panel can be -toggled by selecting :menuselection:`View --> Profiles`, -or clicking on the playing card -icon on the toolbar. - -.. image:: screens/profiles.* - :width: 33% - :alt: poker game with the unique equilibrium displayed - :align: right - :target: _images/profiles.png - -This game has a unique equilibrium in which Fred raises after Red with -probability one, and raises with probability one-third after Black. -Alice, at her only information set, plays meet with probability two- -thirds and raise with probability one-third. - -This equilibrium is displayed in a table in the profiles panel. If -more than one equilibrium is found, this panel lists all equilibria -found. Equilibria computed are grouped by separate computational runs; -computing equilibria using a different method (or different settings) -will add a second list of profiles. The list of profiles displayed is -selected using the drop-down at the top left of the profiles panel; in -the screenshot, it is set to -:guilabel:`Profiles 1`. A -brief description of the method used to compute the equilibria is -listed across the top of the profiles panel. - -The currently selected equilibrium is shown in bold in the profiles -listing, and information about this equilibrium is displayed in the -extensive game. In the figure, the probabilities of selecting each -action are displayed below each branch of the tree. (This is the -default Gambit setting; see :ref:`gui-tree-layout` -for configuring the labeling of trees.) -Each branch of the tree also shows a black line, the length of which -is proportional to the probability with which the action is played. - -.. image:: screens/beliefs.* - :width: 33% - :alt: poker game with the beliefs at Alice's top node - :align: right - :target: _images/beliefs.png - -Clicking on any node in the tree displays additional information about -the profile at that node. -The player panel displays -information relevant to the selected node, including the payoff to all players -conditional on reaching the node, as well as information about Alice's -beliefs at the node. - -The computed profiles can also be viewed in the reduced strategic -game. Clicking on the strategic game icon changes the view to the -reduced strategic form of the game, and shows the equilibrium profiles -converted to mixed strategies in the strategic game. - - - -Computing quantal response equilibria -------------------------------------- - -Gambit provides methods for computing the logit quantal response -equilibrium correspondence for extensive games [McKPal98]_ -and strategic games [McKPal95]_, -using the tracing method of [Tur05]_. - -.. image:: screens/qre.* - :width: 33% - :alt: quantal response equilibria - :align: right - :target: _images/qre.png - - -To compute the correspondence, select :menuselection:`Tools --> Qre`. -If viewing an -extensive game, the agent quantal response equilibrium correspondence -is computed; if viewing a strategic game (including the reduced -strategic game derived from an extensive game), the correspondence is -computed in mixed strategies. - -The computed correspondence values can be saved to a CSV (comma- -separated values) file by clicking the button labeled -:guilabel:`Save correspondence to .csv file`. -This format is suitable for reading by a -spreadsheet or graphing application. - - - -Quantal response equilibria in strategic games (experimental) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There is an experimental graphing interface for quantal response -equilibria in strategic games. -The graph by default plots the probabilities of all strategies, color- -coded by player, as a function of the lambda parameter. The lambda -values on the horizontal axis are plotted using a sigmoid -transformation; the Graph scaling value controls the shape of this -transformation. Lower values of the scaling give more graph space to -lower values of lambda; higher values of the scaling give more space -to higher values of lambda. - -.. image:: screens/logit.* - :width: 33% - :alt: quantal response equilibria graphing - :align: right - :target: _images/logit.png - -The strategies graphed are indicated in the panel at the left of the -window. Clicking on the checkbox next to a strategy toggles whether it -is displayed in the graph. - -The data points computed in the correspondence can be viewed (as in -the extensive game example above) by clicking on the show data icon on -the toolbar. The data points can be saved to a CSV file by clicking on -the . - -To zoom in on a portion of the graph of interest, hold down the left -mouse button and drag a rectangle on the graph. The plot window zooms -in on the portion of the graph selected by that rectangle. To restore -the graph view to the full graph, click on the zoom to fit icon . - -To print the graph as shown, click on the print icon . Note that this -is very experimental, and the output may not be very satisfactory yet. - - -Printing and exporting games ----------------------------- - -Gambit supports (almost) WYSIWYG (what you see is what you get) output -of both extensive and strategic games, both to a printer and to -several graphical formats. For all of these operations, the game is -drawn exactly as currently displayed on the screen, including whether -the extensive or strategic representation is used, the layout, colors -for players, dominance and probability indicators, and so forth. - - - -Printing a game -~~~~~~~~~~~~~~~ - -To print the game, press :kbd:`Ctrl`-:kbd:`P`, select -:menuselection:`File --> Print`, or click -the printer icon on the toolbar. The game is scaled so that the -printout fits on one page, while maintaining the same ratio of -horizontal to vertical size; that is, the scaling factor is the same -in both horizontal and vertical dimensions. - -Note that especially for extensive games, one dimension of the tree is -much larger than the other. Typically, the extent of the tree -vertically is much greater than its horizontal extent. Because the -printout is scaled to fit on one page, printing such a tree will -generally result in what appears to be a thin line running vertically -down the center of the page. This is in fact the tree, shrunk so the -large vertical dimension fits on the page, meaning that the horizontal -dimension, scaled at the same ratio, becomes very tiny. - - - -Saving to a graphics file -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Gambit supports export to five graphical file formats: - - -+ Windows bitmaps ( .bmp ) -+ JPEG, a lossy compressed format ( .jpg , .jpeg ) -+ PNG, a lossless compressed format ( .png ); these are similar to - GIFs -+ Encapsulated PostScript ( .ps ) -+ Scalable vector graphics ( .svg ) - -To export a game to one of these formats, select -:menuselection:`File --> Export`, and -select the corresponding menu entry. - -The Windows bitmap and PNG formats are generally recommended for -export, as they both are lossless formats, which will reproduce the -game image exactly as in Gambit. PNG files use a lossless compression -algorithm, so they are typically much smaller than the Windows bitmap -for the same game. Not all image viewing and manipulation tools handle -PNG files; in those cases, use the Windows bitmap output instead. JPEG -files use a compression algorithm that only approximates the original -version, which often makes it ill-suited for use in saving game -images, since it often leads to "blocking" in the image file. - -For all three of these formats, the dimensions of the exported graphic -are determined by the dimensions of the game as drawn on screen. Image -export is only supported for games which are less than about 65000 -pixels in either the horizontal or vertical dimensions. This is -unlikely to be a practical problem, since such games are so large they -usually cannot be drawn in such a way that a human can make sense of -them. - -Encapsulated PostScript output is generally useful for inclusion in -LaTeX and other scientific document preparation systems. This is a -vector-based output, and thus can be rescaled much more effectively -than the other output formats. - - + gui.general + gui.efg + gui.nfg + gui.dominance + gui.nash + gui.export diff --git a/doc/index.rst b/doc/index.rst index 792b51eba..5a11dce5c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,5 +1,8 @@ -Welcome -======= +################# +Welcome to Gambit +################# + + **Gambit** is a library of game theory software and tools for the construction and analysis of finite extensive and strategic games. @@ -17,9 +20,6 @@ Key features of Gambit include: * A :ref:`Python API ` for developing scripting applications. -Using and citing Gambit -~~~~~~~~~~~~~~~~~~~~~~~ - Gambit is Free/Open Source software, released under the terms of the `GNU General Public License `_, Version 2. @@ -42,10 +42,62 @@ Replace the version number and year as appropriate if you use a different release. -Table of Contents -~~~~~~~~~~~~~~~~~ +.. grid:: + + .. grid-item-card:: Python user guide + :columns: 6 + + An introduction to using the ``pygambit`` package + in Python. + + .. button-ref:: pygambit-user + :ref-type: ref + :click-parent: + :color: secondary + :expand: + + + .. grid-item-card:: Python API reference + :columns: 6 + + The complete reference to all the functionality + of ``pygambit``. + + .. button-ref:: pygambit-api + :ref-type: ref + :click-parent: + :color: secondary + :expand: + + + .. grid-item-card:: Command-line tools + :columns: 6 + + All Gambit's methods for equilibrium computation are + available via command-line programs. + + .. button-ref:: command-line + :ref-type: ref + :click-parent: + :color: secondary + :expand: + + .. grid-item-card:: Graphical interface + :columns: 6 + + Gambit's graphical interface lets you interactively + create, explore, and find equilibria of games. + + .. button-ref:: section-gui + :ref-type: ref + :click-parent: + :color: secondary + :expand: + + .. toctree:: + :hidden: :maxdepth: 1 intro @@ -62,7 +114,7 @@ Table of Contents contents -Or, see a :ref:`more detailed table of contents `. +.. Or, see a :ref:`more detailed table of contents `. .. Indices and tables .. ================== diff --git a/doc/intro.rst b/doc/intro.rst index 87854cdec..0cc9d7aaa 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -1,8 +1,9 @@ +********************* An overview of Gambit -===================== +********************* What is Gambit? ---------------- +=============== Gambit is a set of software tools for doing computation on finite, noncooperative games. These comprise a graphical interface for @@ -12,7 +13,7 @@ equilibria and other solution concepts in games; and, a set of file formats for storing and communicating games to external tools. A brief history of Gambit -------------------------- +========================= The Gambit Project was founded in the mid-1980s by Richard McKelvey at the California Institute of Technology. The original implementation @@ -57,7 +58,7 @@ of Stephen Kunath and Alessandro Andrioni. Key features of Gambit ----------------------- +====================== Gambit has a number of features useful both for the researcher and the instructor: @@ -91,7 +92,7 @@ econometric analysis on games. Limitations of Gambit ---------------------- +===================== Gambit has a few limitations that may be important in some applications. We outline them here. @@ -135,7 +136,7 @@ well as make the resulting methods available to both students and practitioners. Developers ----------- +========== The principal developers of Gambit are: @@ -179,19 +180,18 @@ include: .. _section-downloading: Downloading Gambit ------------------- +================== Gambit source code and built binaries can be downloaded from the project `GitHub repository releases section`. Older versions of Gambit can be downloaded from `http://sourceforge.net/projects/gambit/files -`_. Support for older -versions is limited. +`_. Bug reports ------------ +=========== In the first instance, bug reports or feature requests should be posted to the Gambit issue tracker, located at diff --git a/doc/poker.efg b/doc/poker.efg new file mode 100644 index 000000000..6d09a1a1d --- /dev/null +++ b/doc/poker.efg @@ -0,0 +1,14 @@ +EFG 2 R "One card poker game, after Myerson (1991)" { "Alice" "Bob" } +"" + +c "" 1 "" { "King" 1/2 "Queen" 1/2 } 0 +p "" 1 1 "" { "Raise" "Fold" } 0 +p "" 2 1 "" { "Meet" "Pass" } 0 +t "" 1 "Alice wins big" { 2, -2 } +t "" 2 "Alice wins" { 1, -1 } +t "" 4 "Bob wins" { -1, 1 } +p "" 1 2 "" { "Raise" "Fold" } 0 +p "" 2 1 "" { "Meet" "Pass" } 0 +t "" 3 "Bob wins big" { -2, 2 } +t "" 2 "Alice wins" { 1, -1 } +t "" 4 "Bob wins" { -1, 1 } diff --git a/doc/pygambit.api.rst b/doc/pygambit.api.rst index 79dab85fc..cb00415c7 100644 --- a/doc/pygambit.api.rst +++ b/doc/pygambit.api.rst @@ -1,3 +1,4 @@ +.. _pygambit-api: API documentation ----------------- diff --git a/doc/pygambit.user.rst b/doc/pygambit.user.rst index bc9e0e052..4ed5a6255 100644 --- a/doc/pygambit.user.rst +++ b/doc/pygambit.user.rst @@ -1,3 +1,5 @@ +.. _pygambit-user: + User guide ---------- @@ -18,46 +20,57 @@ If the Seller chooses **Honor**, both players receive payoffs of 1; if the Seller chooses **Abuse**, the Buyer receives a payoff of -1 and the Seller receives a payoff of 2. -We create a game with an extensive representation using :py:meth:`.Game.new_tree`:: +We create a game with an extensive representation using :py:meth:`.Game.new_tree`: + +.. ipython:: python + + import pygambit as gbt + g = gbt.Game.new_tree(players=["Buyer", "Seller"], + title="One-shot trust game, after Kreps (1990)") + - In [1]: import pygambit as gbt +The tree of the game contains just a root node, with no children: - In [2]: g = gbt.Game.new_tree(players=["Buyer", "Seller"], title="One-shot trust game, after Kreps (1990)") +.. ipython:: python -The tree of the game contains just a root node, with no children:: + g.root + g.root.children - In [3]: g.root - Out[3]: Out[3]: - In [4]: g.root.children - Out[4]: [] +To extend a game from an existing terminal node, use :py:meth:`.Game.append_move`: -To extend a game from an existing terminal node, use :py:meth:`.Game.append_move`:: +.. ipython:: python - In [5]: g.append_move(g.root, "Buyer", ["Trust", "Not trust"]) + g.append_move(g.root, "Buyer", ["Trust", "Not trust"]) + g.root.children - In [6]: g.root.children - Out[6]: [, ] +We can then also add the Seller's move in the situation after the Buyer chooses Trust: -We can then also add the Seller's move in the situation after the Buyer chooses Trust:: +.. ipython:: python - In [7]: g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"]) + g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"]) Now that we have the moves of the game defined, we add payoffs. Payoffs are associated with -:py:class:`.Outcome`s; each :py:class:`Outcome` has a vector of payoffs, one for each player, +an :py:class:`.Outcome`; each :py:class:`Outcome` has a vector of payoffs, one for each player, and optionally an identifying text label. First we add the outcome associated with the -Seller proving themselves trustworthy:: +Seller proving themselves trustworthy: - In [8]: g.set_outcome(g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy")) +.. ipython:: python + + g.set_outcome(g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy")) Next, the outcome associated with the scenario where the Buyer trusts but the Seller does -not return the trust:: +not return the trust: + +.. ipython:: python + + g.set_outcome(g.root.children[0].children[1], g.add_outcome([-1, 2], label="Untrustworthy")) - In [9]: g.set_outcome(g.root.children[0].children[1], g.add_outcome([-1, 2], label="Untrustworthy")) +And, finally the outcome associated with the Buyer opting out of the interaction: -And, finally the outcome associated with the Buyer opting out of the interaction:: +.. ipython:: python - In [10]: g.set_outcome(g.root.children[1], g.add_outcome([0, 0], label="Opt-out")) + g.set_outcome(g.root.children[1], g.add_outcome([0, 0], label="Opt-out")) Nodes without an outcome attached are assumed to have payoffs of zero for all players. Therefore, adding the outcome to this latter terminal node is not strictly necessary in Gambit, @@ -156,30 +169,14 @@ so, for example, NumPy arrays can be used directly. For example, to create a standard prisoner's dilemma game in which the cooperative payoff is 8, the betrayal payoff is 10, the sucker payoff is 2, and the noncooperative -payoff is 5:: +payoff is 5: - In [1]: import numpy as np +.. ipython:: python - In [2]: m = np.array([[8, 2], [10, 5]]) - - In [3]: g = gbt.Game.from_arrays(m, np.transpose(m)) - - In [4]: g - Out[4]: - NFG 1 R "Untitled strategic game" { "1" "2" } - - { { "1" "2" } - { "1" "2" } - } - "" - - { - { "" 8, 8 } - { "" 10, 2 } - { "" 2, 10 } - { "" 5, 5 } - } - 1 2 3 4 + import numpy as np + m = np.array([[8, 2], [10, 5]]) + g = gbt.Game.from_arrays(m, np.transpose(m)) + g The arrays passed to :py:meth:`.Game.from_arrays` are all indexed in the same sense, that is, the top level index is the choice of the first player, the second level index of the second player, @@ -196,35 +193,33 @@ Payoffs to players and probabilities of actions at chance information sets are s as numbers. Gambit represents the numerical values in a game in exact precision, using either decimal or rational representations. -To illustrate, we consider a trivial game which just has one move for the chance player:: - - In [1]: import pygambit as gbt - - In [2]: g = gbt.Game.new_tree() +To illustrate, we consider a trivial game which just has one move for the chance player: - In [3]: g.append_move(g.root, g.players.chance, 3) +.. ipython:: python - In [4]: [act.prob for act in g.root.infoset.actions] - Out [4]: [Rational(1, 3), Rational(1, 3), Rational(1, 3)] + import pygambit as gbt + g = gbt.Game.new_tree() + g.append_move(g.root, g.players.chance, ["a", "b", "c"]) + [act.prob for act in g.root.infoset.actions] The default when creating a new move for chance is that all actions are chosen with equal probability. These probabilities are represented as rational numbers, using ``pygambit``'s :py:class:`.Rational` class, which is derived from Python's -`fractions.Fraction`. Numerical data can be set as rational numbers:: +`fractions.Fraction`. Numerical data can be set as rational numbers: - In [5]: g.set_chance_probs(g.root.infoset, - ...: [gbt.Rational(1, 4), gbt.Rational(1, 2), gbt.Rational(1, 4)]) +.. ipython:: python - In [6]: [act.prob for act in g.root.infoset.actions] - Out [6]: [Rational(1, 4), Rational(1, 2), Rational(1, 4)] + g.set_chance_probs(g.root.infoset, + [gbt.Rational(1, 4), gbt.Rational(1, 2), gbt.Rational(1, 4)]) + [act.prob for act in g.root.infoset.actions] -They can also be explicitly specified as decimal numbers:: +They can also be explicitly specified as decimal numbers: - In [7]: g.set_chance_probs(g.root.infoset, - ...: [gbt.Decimal(".25"), gbt.Decimal(".50"), gbt.Decimal(".25")] +.. ipython:: python - In [8]: [act.prob for act in g.root.infoset.actions] - Out [8]: [Decimal('0.25'), Decimal('0.50'), Decimal('0.25')] + g.set_chance_probs(g.root.infoset, + [gbt.Decimal(".25"), gbt.Decimal(".50"), gbt.Decimal(".25")]) + [act.prob for act in g.root.infoset.actions] Although the two representations above are mathematically equivalent, ``pygambit`` remembers the format in which the values were specified. @@ -233,51 +228,54 @@ Expressing rational or decimal numbers as above is verbose and tedious. ``pygambit`` offers a more concise way to express numerical data in games: when setting numerical game data, ``pygambit`` will attempt to convert text strings to their rational or decimal representation. The above can therefore be written -more compactly using string representations:: +more compactly using string representations: - In [9]: g.set_chance_probs(g.root.infoset, ["1/4", "1/2", "1/4"]) +.. ipython:: python - In [10]: [act.prob for act in g.root.infoset.actions] - Out [10]: [Rational(1, 4), Rational(1, 2), Rational(1, 4)] + g.set_chance_probs(g.root.infoset, ["1/4", "1/2", "1/4"]) + [act.prob for act in g.root.infoset.actions] - In [11]: g.set_chance_probs(g.root.infoset, [".25", ".50", ".25"]) - - In [12]: [act.prob for act in g.root.infoset.actions] - Out [12]: [Decimal('0.25'), Decimal('0.50'), Decimal('0.25')] + g.set_chance_probs(g.root.infoset, [".25", ".50", ".25"]) + [act.prob for act in g.root.infoset.actions] As a further convenience, ``pygambit`` will accept Python ``int`` and ``float`` values. ``int`` values are always interpreted as :py:class:`.Rational` values. ``pygambit`` attempts to render `float` values in an appropriate :py:class:`.Decimal` equivalent. In the majority of cases, this creates no problems. -For example,:: +For example, - In [13]: g.set_chance_probs(g.root.infoset, [.25, .50, .25]) +.. ipython:: python - In [14]: [act.prob for act in g.root.infoset.actions] - Out [14]: [Decimal('0.25'), Decimal('0.5'), Decimal('0.25')] + g.set_chance_probs(g.root.infoset, [.25, .50, .25]) + [act.prob for act in g.root.infoset.actions] However, rounding can cause difficulties when attempting to use `float` values to -represent values which do not have an exact decimal representation:: +represent values which do not have an exact decimal representation + +.. ipython:: python + :okexcept: - In [15]: g.set_chance_probs(g.root.infoset, [1/3, 1/3, 1/3]) - ValueError: set_chance_probs(): must specify non-negative probabilities that sum to one + g.set_chance_probs(g.root.infoset, [1/3, 1/3, 1/3]) This behavior can be slightly surprising, especially in light of the fact that -in Python,:: +in Python, - In [16]: 1/3 + 1/3 + 1/3 - Out [16]: 1.0 +.. ipython:: python + + 1/3 + 1/3 + 1/3 In checking whether these probabilities sum to one, ``pygambit`` first converts each -of the probabilities to a :py:class:`.Decimal` representation, via the following method:: +of the probabilitiesto a :py:class:`.Decimal` representation, via the following method + +.. ipython:: python - In [17]: gbt.Decimal(str(1/3)) - Out [17]: Decimal('0.3333333333333333') + gbt.Decimal(str(1/3)) -and the sum-to-one check then fails because:: +and the sum-to-one check then fails because - In [18]: gbt.Decimal(str(1/3)) + gbt.Decimal(str(1/3)) + gbt.Decimal(str(1/3)) - Out [18]: Decimal('0.9999999999999999') +.. ipython:: python + + gbt.Decimal(str(1/3)) + gbt.Decimal(str(1/3)) + gbt.Decimal(str(1/3)) Setting payoffs for players also follows the same rules. Representing probabilities and payoffs exactly is essential, because ``pygambit`` offers (in particular for two-player @@ -294,29 +292,23 @@ the values are recorded as intended. Reading a game from a file ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Games stored in existing Gambit savefiles in either the .efg or .nfg -formats can be loaded using :meth:`.Game.read_game`:: +Games stored in existing Gambit savefiles can be loaded using :meth:`.Game.read_game`: + +.. ipython:: python + :suppress: + + cd ../contrib/games - In [1]: g = gbt.Game.read_game("e02.nfg") - In [2]: g - Out[2]: - NFG 1 R "Selten (IJGT, 75), Figure 2, normal form" { "Player 1" "Player 2" } +.. ipython:: python - { { "1" "2" "3" } - { "1" "2" } - } - "" + g = gbt.Game.read_game("e02.nfg") + g - { - { "" 1, 1 } - { "" 0, 2 } - { "" 0, 2 } - { "" 1, 1 } - { "" 0, 3 } - { "" 2, 0 } - } - 1 2 3 4 5 6 +.. ipython:: python + :suppress: + + cd ../../doc @@ -343,31 +335,36 @@ We take as an example the :ref:`one-card poker game `. Thi constant sum game, and so all of the equilibrium-finding methods can be applied to it. For two-player games, :py:func:`.lcp_solve` can compute Nash equilibria directly using -the extensive representation. Assuming that ``g`` refers to the game:: +the extensive representation. Assuming that ``g`` refers to the game + +.. ipython:: python + :suppress: - In [1]: eqa = gbt.nash.lcp_solve(g) + g = gbt.Game.read_game("poker.efg") - In [2]: eqa - Out[2]: [[[[Rational(1, 1), Rational(0, 1)], [Rational(1, 3), Rational(2, 3)]], [[Rational(2, 3), Rational(1, 3)]]]] +.. ipython:: python - In [3]: len(eqa) - Out[3]: 1 + eqa = gbt.nash.lcp_solve(g) + eqa + len(eqa) The result of the calculation is a list of :py:class:`~pygambit.gambit.MixedBehaviorProfile`. Such a profile specifies action probabilities for each information set. The profile represents these hierarchically, with information sets grouped by player. We can just focus on the strategy of one player by indexing the profile by that -player:: +player, + +.. ipython:: python - In [4]: eqa[0]["Alice"] - Out[4]: [[Rational(1, 1), Rational(0, 1)], [Rational(1, 3), Rational(2, 3)]] + eqa[0]["Alice"] In this case, at Alice's first information set, where she has the King, she always raises. At her second information set, where she has the Queen, she sometimes bluffs, raising with -probability one-third. Looking at Bob's strategy:: +probability one-third. Looking at Bob's strategy, - In [5]: eqa[0]["Bob"] - Out[5]: [[Rational(2, 3), Rational(1, 3)]] +.. ipython:: python + + eqa[0]["Bob"] Bob meets Alice's raise two-thirds of the time. @@ -379,19 +376,20 @@ profiles. This is enabled by default for these methods. When a game has an extensive representation, equilibrium finding methods default to computing on that representation. It is also possible to compute using the strategic representation. -``pygambit`` transparently computes the reduced strategic form representation of an extensive game:: +``pygambit`` transparently computes the reduced strategic form representation of an extensive game + +.. ipython:: python - In [6]: [s.label for s in g.players["Alice"].strategies] - Out[6]: ['11', '12', '21', '22'] + [s.label for s in g.players["Alice"].strategies] In the strategic form of this game, Alice has four strategies. The generated strategy labels list the action numbers taken at each information set. We can therefore apply a method which -operates on a strategic game to any game with an extensive representation:: +operates on a strategic game to any game with an extensive representation - In [7]: eqa = gbt.nash.gnm_solve(g) +.. ipython:: python - In [8]: eqa - Out[8]: [[[0.33333333333866655, 0.6666666666613332, 0.0, 0.0], [0.6666666666559998, 0.33333333334400017]]] + eqa = gbt.nash.gnm_solve(g) + eqa :py:func:`.gnm_solve` can be applied to any game with any number of players, and uses a path-following process in floating-point arithmetic, so it returns profiles with probabilities expressed as @@ -399,8 +397,9 @@ floating-point numbers. This method operates on the strategic representation of the returned results are of type :py:class:`~pygambit.gambit.MixedStrategyProfile`, and specify, for each player, a probability distribution over that player's strategies. We can convert freely between :py:class:`~pygambit.gambit.MixedStrategyProfile` and -:py:class:`~pygambit.gambit.MixedBehaviorProfile` representations:: +:py:class:`~pygambit.gambit.MixedBehaviorProfile` representations + +.. ipython:: python - In [9]: eqa[0].as_behavior() - Out[9]: [[[1.0, 0.0], [0.3333333333386666, 0.6666666666613333]], [[0.6666666666559998, 0.33333333334400017]]] + eqa[0].as_behavior() diff --git a/doc/requirements.txt b/doc/requirements.txt index 11a6bb8ea..a359e0d22 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -2,3 +2,5 @@ Cython numpy scipy pydata-sphinx-theme +sphinx_design +ipython diff --git a/doc/tools.convert.rst b/doc/tools.convert.rst new file mode 100644 index 000000000..a74380232 --- /dev/null +++ b/doc/tools.convert.rst @@ -0,0 +1,66 @@ +:program:`gambit-convert`: Convert games among various representations +====================================================================== + +:program:`gambit-convert` reads a game on standard input in any supported format +and converts it to another text representation. Currently, this tool supports +outputting the strategic form of the game in one of these formats: + +* A standard HTML table. +* A LaTeX fragment in the format of Martin Osborne's `sgame` macros + (see http://www.economics.utoronto.ca/osborne/latex/index.html). + + +.. program:: gambit-convert + +.. cmdoption:: -O FORMAT + + Required. Specifies the output format. Supported options for + `FORMAT` are `html` or `sgame`. + +.. cmdoption:: -r PLAYER + + Specifies the player number to place on the rows of the tables. + The default if not specified is to place player 1 on the rows. + +.. cmdoption:: -c PLAYER + + Specifies the player number to place on the columns of the tables. + The default if not specified is to place player 2 on the columns. + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + + +Example invocation for HTML output:: + + $ gambit-convert -O html 2x2.nfg + Convert games among various file formats + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + +

Two person 2 x 2 game with unique mixed equilibrium

+
12
12,00,1
20,11,0
+ + +Example invocation for LaTeX output:: + + $ gambit-convert -O sgame 2x2.nfg + Convert games among various file formats + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + \begin{game}{2}{2}[Player 1][Player 2] + &1 & 2\\ + 1 & $2,0$ & $0,1$ \\ + 2 & $0,1$ & $1,0$ + \end{game} + diff --git a/doc/tools.enummixed.rst b/doc/tools.enummixed.rst new file mode 100644 index 000000000..dbdb445e7 --- /dev/null +++ b/doc/tools.enummixed.rst @@ -0,0 +1,95 @@ +.. _gambit-enummixed: + +:program:`gambit-enummixed`: Enumerate equilibria in a two-player game +====================================================================== + +:program:`gambit-enummixed` reads a two-player game on standard input and +computes Nash equilibria using extreme point enumeration. + +In a two-player strategic game, the set of Nash equilibria can be expressed +as the union of convex sets. This program generates all the extreme +points of those convex sets. (Mangasarian [Man64]_) +This is a superset of the points generated by the path-following +procedure of Lemke and Howson (see :ref:`gambit-lcp`). It was +shown by Shapley [Sha74]_ that there are equilibria not accessible via +the method in :ref:`gambit-lcp`, whereas the output of +:program:`gambit-enummixed` is guaranteed to return all the extreme +points. + +.. program:: gambit-enummixed + +.. cmdoption:: -d + + By default, this program computes using exact + rational arithmetic. Since the extreme points computed by this method + are guaranteed to be rational when the payoffs in the game are + rational, this permits exact computation of the equilibrium set. + Computation using rational arithmetic is in general slow, however. For + most games, acceptable results can be obtained by computing using the + computer's native floating-point arithmetic. Using this flag enables + computation in floating-point, and expresses all output using decimal + representations with the specified number of digits. + +.. cmdoption:: -D + + Since all Nash equilibria involve only strategies which survive + iterative elimination of strictly dominated strategies, the program + carries out the elimination automatically prior to computation. + This is recommended, since it almost always results in superior + performance. + Specifying `-D` skips the elimination step and performs the + enumeration on the full game. + +.. cmdoption:: -c + + The program outputs the extreme equilibria as it finds them, + prefixed by the tag NE . If this option is specified, once all extreme + equilbria are identified, the program computes the convex sets which + make up the set of equilibria. The program then additionally outputs + each convex set, prefixed by convex-N , where N indexes the set. The + set of all equilibria, then, is the union of these convex sets. + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + +.. cmdoption:: -L + + Use `lrslib `_ by David Avis + to carry out the enumeration process. This is an experimental + feature that has not been widely tested. + +Computing the equilibria, in mixed strategies, of :download:`e02.nfg +<../contrib/games/e02.nfg>`, the reduced strategic form of the example +in Figure 2 of Selten (International Journal of Game Theory, +1975):: + + $ gambit-enummixed e02.nfg + Compute Nash equilibria by enumerating extreme points + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + Enumeration code based on lrslib 4.2b, + Copyright (C) 1995-2005 by David Avis (avis@cs.mcgill.ca) + This is free software, distributed under the GNU GPL + + NE,1,0,0,1,0 + NE,1,0,0,1/2,1/2 + +In fact, the game e02.nfg has a one-dimensional continuum of +equilibria. This fact can be observed by examining the connectedness +information using the `-c` switch:: + + $ gambit-enummixed -c e02.nfg + Compute Nash equilibria by enumerating extreme points + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + Enumeration code based on lrslib 4.2b, + Copyright (C) 1995-2005 by David Avis (avis@cs.mcgill.ca) + This is free software, distributed under the GNU GPL + + NE,1,0,0,1,0 + NE,1,0,0,1/2,1/2 + convex-1,1,0,0,1/2,1/2 + convex-1,1,0,0,1,0 diff --git a/doc/tools.enumpoly.rst b/doc/tools.enumpoly.rst new file mode 100644 index 000000000..27445e11d --- /dev/null +++ b/doc/tools.enumpoly.rst @@ -0,0 +1,86 @@ +:program:`gambit-enumpoly`: Compute equilibria of a game using polynomial systems of equations +============================================================================================== + +:program:`gambit-enumpoly` reads a game on standard input and +computes Nash equilibria by solving systems of polynomial equations +and inequalities. + +This program searches for all Nash equilibria in a strategic game +using a support enumeration approach. This approach computes all the +supports which could, in principle, be the support of a Nash +equilibrium, and then searches for a totally mixed equilibrium on that +support by solving a system of polynomial equalities and inequalities +formed by the Nash equilibrium conditions. The ordering of the +supports is done in such a way as to maximize use of previously +computed information, making it suited to the computation of all Nash +equilibria. + +When the verbose switch `-v` is used, the program outputs each support +as it is considered. The supports are presented as a comma-separated +list of binary strings, where each entry represents one player. The +digit 1 represents a strategy which is present in the support, and the +digit 0 represents a strategy which is not present. Each candidate +support is printed with the label "candidate,". + +Note that the subroutine to compute a solution to the system of +polynomial equations and inequalities will fail in degenerate cases. +When the verbose switch `-v` is used, these supports are identified on +standard output with the label "singular,". It is possible that there +exist equilibria, often a connected component of equilibria, on these +singular supports. + + +.. program:: gambit-enumpoly + +.. cmdoption:: -d + + Express all output using decimal representations with the specified + number of digits. + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -H + + By default, the program uses an enumeration method designed to + visit as few supports as possible in searching for all equilibria. + With this switch, the program uses a heuristic search method based on + Porter, Nudelman, and Shoham [PNS04]_, which is designed to minimize the + time until the first equilibrium is found. This switch only has an + effect when solving strategic games. + +.. cmdoption:: -S + + By default, the program uses behavior strategies for extensive + games; this switch instructs the program to use reduced strategic game + strategies for extensive games. (This has no effect for strategic + games, since a strategic game is its own reduced strategic game.) + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + +.. cmdoption:: -v + + Sets verbose mode. In verbose mode, supports are printed on + standard output with the label "candidate" as they are considered, and + singular supports are identified with the label "singular." By + default, no information about supports is printed. + +Computing equilibria of the extensive game :download:`e01.efg +<../contrib/games/e01.efg>`, the example in Figure 1 of Selten +(International Journal of Game Theory, 1975) sometimes called +"Selten's horse":: + + $ gambit-enumpoly e01.efg + Compute Nash equilibria by solving polynomial systems + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + Heuristic search implementation Copyright (C) 2006, Litao Wei + This is free software, distributed under the GNU GPL + + NE,0.000000,1.000000,0.333333,0.666667,1.000000,0.000000 + NE,1.000000,0.000000,1.000000,0.000000,0.250000,0.750000 + NE,1.000000,0.000000,1.000000,0.000000,0.000000,0.000000 + NE,0.000000,1.000000,0.000000,0.000000,1.000000,0.000000 + diff --git a/doc/tools.enumpure.rst b/doc/tools.enumpure.rst new file mode 100644 index 000000000..1e947b900 --- /dev/null +++ b/doc/tools.enumpure.rst @@ -0,0 +1,97 @@ +.. _gambit-enumpure: + +:program:`gambit-enumpure`: Enumerate pure-strategy equilibria of a game +======================================================================== + +:program:`gambit-enumpure` reads a game on standard input and searches for +pure-strategy Nash equilibria. + +.. versionchanged:: 14.0.2 + The effect of the `-S` switch is now purely cosmetic, determining + how the equilibria computed are represented in the + output. Previously, `-S` computed using the strategic game; if this + was not specified for an extensive game, the agent form equilibria + were returned. + +.. program:: gambit-enumpure + +.. cmdoption:: -S + + Report equilibria in reduced strategic form strategies, even if the + game is an extensive game. By default, if passed an extensive + game, the output will be in behavior strategies. Specifying this switch + does not imply any change in operation internally, as pure-strategy + equilibria are defined in terms of reduced strategic form + strategies. + +.. cmdoption:: -D + + .. versionadded:: 14.0.2 + + The default output format for computed equilibria is a + comma-separated list of strategy or action probabilities, suitable + for postprocessing by automated tools. Specifying `-D` instead + causes the program to output greater detail on each equilbrium + profile computed. + +.. cmdoption:: -A + + .. versionadded:: 14.0.2 + + Report agent form equilibria, that is, equilibria which consider + only deviations at one information set. Only has an effect for + extensive games, as strategic games have only one information set + per player. + +.. cmdoption:: -P + + By default, the program computes all pure-strategy Nash + equilibria in an extensive game. This switch instructs the program to + find only pure-strategy Nash equilibria which are subgame perfect. + (This has no effect for strategic games, since there are no proper + subgames of a strategic game.) + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + + +Computing the pure-strategy equilibria of extensive game :download:`e02.efg +<../contrib/games/e02.efg>`, the example in Figure 2 of Selten +(International Journal of Game Theory, 1975):: + + $ gambit-enumpure e02.efg + Search for Nash equilibria in pure strategies + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1,0,0,0,1,0 + +With the `-S` switch, the set of equilibria returned is the same, +except expressed in strategic game strategies rather than behavior +strategies:: + + $ gambit-enumpure -S e02.efg + Search for Nash equilibria in pure strategies + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1,0,0,1,0 + +The `-A` switch considers only behavior strategy profiles where there +is no way for a player to improve his payoff by changing action at +only one information set; therefore the set of solutions is larger:: + + $ gambit-enumpure -A e02.efg + Search for Nash equilibria in pure strategies + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1,0,1,0,1,0 + NE,1,0,1,0,0,1 + NE,1,0,0,1,1,0 + diff --git a/doc/tools.gnm.rst b/doc/tools.gnm.rst new file mode 100644 index 000000000..8b4a489cd --- /dev/null +++ b/doc/tools.gnm.rst @@ -0,0 +1,62 @@ +.. _gambit-gnm: + +:program:`gambit-gnm`: Compute Nash equilibria in a strategic game using a global Newton method +=============================================================================================== + +:program:`gambit-gnm` reads a game on standard input and computes Nash +equilibria using a global Newton method approach developed by Govindan +and Wilson [GovWil03]_. This program is a wrapper around the +`Gametracer 0.2 `_ +implementation by Ben Blum and Christian Shelton. + +.. program:: gambit-gnm + +.. cmdoption:: -d + + Express all output using decimal representations + with the specified number of digits. + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -n + + Randomly generate the specified number of perturbation vectors. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + +.. cmdoption:: -s + + Specifies a file containing a list of starting points + for the algorithm. The format of the file is comma-separated values, + one mixed strategy profile per line, in the same format used for + output of equilibria (excluding the initial NE tag). + +.. cmdoption:: -v + + Show intermediate output of the algorithm. If this option is + not specified, only the equilibria found are reported. + +Computing an equilibrium of :download:`e02.nfg <../contrib/games/e02.nfg>`, +the reduced strategic form of the example in Figure 2 of Selten +(International Journal of Game Theory, 1975):: + + $ gambit-gnm e02.nfg + Compute Nash equilibria using a global Newton method + Gametracer version 0.2, Copyright (C) 2002, Ben Blum and Christian Shelton + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1,0,2.99905e-12,0.5,0.5 + + +.. note:: + + This is an experimental program and has not been extensively tested. + +.. seealso:: + + :ref:`gambit-ipa`. diff --git a/doc/tools.ipa.rst b/doc/tools.ipa.rst new file mode 100644 index 000000000..8e5ce8e95 --- /dev/null +++ b/doc/tools.ipa.rst @@ -0,0 +1,49 @@ +.. _gambit-ipa: + +:program:`gambit-ipa`: Compute Nash equilibria in a strategic game using iterated polymatrix approximation +========================================================================================================== + +:program:`gambit-ipa` reads a game on standard input and computes Nash +equilibria using an iterated polymatrix approximation approach +developed by Govindan and Wilson [GovWil04]_. +This program is a wrapper around the +`Gametracer 0.2 `_ +implementation by Ben Blum and Christian Shelton. + +.. program:: gambit-ipa + +.. cmdoption:: -d + + Express all output using decimal representations + with the specified number of digits. + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + + +Computing an equilibrium of :download:`e02.nfg <../contrib/games/e02.nfg>`, +the reduced strategic form of the example in Figure 2 of Selten +(International Journal of Game Theory, 1975):: + + $ gambit-ipa e02.nfg + Compute Nash equilibria using iterated polymatrix approximation + Gametracer version 0.2, Copyright (C) 2002, Ben Blum and Christian Shelton + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1.000000,0.000000,0.000000,1.000000,0.000000 + + + +.. note:: + + This is an experimental program and has not been extensively tested. + +.. seealso:: + + :ref:`gambit-gnm`. diff --git a/doc/tools.lcp.rst b/doc/tools.lcp.rst new file mode 100644 index 000000000..93c1c8e42 --- /dev/null +++ b/doc/tools.lcp.rst @@ -0,0 +1,79 @@ +.. _gambit-lcp: + +:program:`gambit-lcp`: Compute equilibria in a two-player game via linear complementarity +========================================================================================= + + +:program:`gambit-lcp` reads a two-player game on standard input and +computes Nash equilibria by finding solutions to a linear +complementarity problem. For extensive games, the program uses the +sequence form representation of the extensive game, as defined by +Koller, Megiddo, and von Stengel [KolMegSte94]_, and applies the +algorithm developed by Lemke. For strategic games, the program using +the method of Lemke and Howson [LemHow64]_. There exist strategic +games for which some equilibria cannot be located by this method; see +Shapley [Sha74]_. + +In a two-player strategic game, the set of Nash equilibria can be expressed +as the union of convex sets. This program will find extreme points +of those convex sets. See :ref:`gambit-enummixed` for a method +which is guaranteed to find all the extreme points for a strategic +game. + +.. program:: gambit-lcp + +.. cmdoption:: -d + + By default, this program computes using exact + rational arithmetic. Since the extreme points computed by this method + are guaranteed to be rational when the payoffs in the game are + rational, this permits exact computation of the equilibrium set. + Computation using rational arithmetic is in general slow, however. For + most games, acceptable results can be obtained by computing using the + computer's native floating-point arithmetic. Using this flag enables + computation in floating-point, and expresses all output using decimal + representations with the specified number of digits. + +.. cmdoption:: -S + + By default, the program uses behavior strategies for extensive + games; this switch instructs the program to use reduced strategic game + strategies for extensive games. (This has no effect for strategic + games, since a strategic game is its own reduced strategic game.) + +.. cmdoption:: -D + + .. versionadded:: 14.0.2 + + The default output format for computed equilibria is a + comma-separated list of strategy or action probabilities, suitable + for postprocessing by automated tools. Specifying `-D` instead + causes the program to output greater detail on each equilbrium + profile computed. + +.. cmdoption:: -P + + By default, the program computes Nash equilibria in an extensive + game. This switch instructs the program to find only equilibria + which are subgame perfect. (This has no effect for strategic + games, since there are no proper subgames of a strategic game.) + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + + +Computing an equilibrium of extensive game :download:`e02.efg +<../contrib/games/e02.efg>`, the example in Figure 2 of Selten +(International Journal of Game Theory, 1975):: + + $ gambit-lcp e02.efg + Compute Nash equilibria by solving a linear complementarity program + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1,0,1/2,1/2,1/2,1/2 diff --git a/doc/tools.liap.rst b/doc/tools.liap.rst new file mode 100644 index 000000000..deeaf5ddd --- /dev/null +++ b/doc/tools.liap.rst @@ -0,0 +1,73 @@ +.. _gambit-liap: + +:program:`gambit-liap`: Compute Nash equilibria using function minimization +=========================================================================== + +:program:`gambit-liap` reads a game on standard input and computes +approximate Nash equilibria using a function minimization approach. + +This procedure searches for equilibria by generating random starting +points and using conjugate gradient descent to minimize the Lyapunov +function of the game. This function is a nonnegative function which is +zero exactly at strategy profiles which are Nash equilibria. + +Note that this procedure is not globally convergent. That is, it is +not guaranteed to find all, or even any, Nash equilibria. + + +.. program:: gambit-liap + +.. cmdoption:: -d + + Express all output using decimal representations with the + specified number of digits. + +.. cmdoption:: -n + + Specify the number of starting points to randomly generate. + +.. cmdoption:: -i + + .. versionadded:: 16.1.0 + + Specify the maximum number of iterations in function minimization (default is 100). + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + +.. cmdoption:: -s + + Specifies a file containing a list of starting points + for the algorithm. The format of the file is comma-separated values, + one mixed strategy profile per line, in the same format used for + output of equilibria (excluding the initial NE tag). + +.. cmdoption:: -S + + By default, the program uses behavior strategies for extensive + games; this switch instructs the program to use reduced strategic game + strategies for extensive games. (This has no effect for strategic + games, since a strategic game is its own reduced strategic game.) + +.. cmdoption:: -v + + Sets verbose mode. In verbose mode, initial points, as well as + points at which the minimization fails at a constrained local minimum + that is not a Nash equilibrium, are all output, in addition to any + equilibria found. + +Computing an equilibrium in mixed strategies of :download:`e02.efg +<../contrib/games/e02.efg>`, the example in Figure 2 of Selten +(International Journal of Game Theory, 1975):: + + $ gambit-liap e02.nfg + Compute Nash equilibria by minimizing the Lyapunov function + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE, 0.998701, 0.000229, 0.001070, 0.618833, 0.381167 diff --git a/doc/tools.logit.rst b/doc/tools.logit.rst new file mode 100644 index 000000000..347eeb81f --- /dev/null +++ b/doc/tools.logit.rst @@ -0,0 +1,114 @@ +.. _gambit-logit: + +:program:`gambit-logit`: Compute quantal response equilbria +=========================================================== + +:program:`gambit-logit` reads a game on standard input and computes the +principal branch of the (logit) quantal response correspondence. + +The method is based on the procedure described in Turocy [Tur05]_ for +strategic games and Turocy [Tur10]_ for extensive games. +It uses standard path-following methods (as +described in Allgower and Georg's "Numerical Continuation Methods") to +adaptively trace the principal branch of the correspondence +efficiently and securely. + +The method used is a predictor-corrector method, which first generates +a prediction using the differential equations describing the branch of +the correspondence, followed by a corrector step which refines the +prediction using Newton's method for finding a zero of a function. Two +parameters control the operation of this tracing. The option `-s` sets +the initial step size for the predictor phase of the tracing. This +step size is then dynamically adjusted based on the rate of +convergence of Newton's method in the corrector step. If the +convergence is fast, the step size is adjusted upward (accelerated); +if it is slow, the step size is decreased (decelerated). The option +`-a` sets the maximum acceleration (or deceleration). As described in +Turocy [Tur05]_, this acceleration helps to +efficiently trace the correspondence when it reaches its asymptotic +phase for large values of the precision parameter lambda. + +.. program:: gambit-logit + +.. cmdoption:: -d + + Express all output using decimal representations with the specified + number of digits. The default is `-d 6`. + +.. cmdoption:: -s + + Sets the initial step size for the predictor phase of + the tracing procedure. The default value is .03. The step size is + specified in terms of the arclength along the branch of the + correspondence, and not the size of the step measured in terms of + lambda. So, for example, if the step size is currently .03, but the + position of the strategy profile on the branch is changing rapidly + with lambda, then lambda will change by much less then .03 between + points reported by the program. + +.. cmdoption:: -a + + Sets the maximum acceleration of the step size during + the tracing procedure. This is interpreted as a multiplier. The + default is 1.1, which means the step size is increased or decreased by + no more than ten percent of its current value at every step. A value + close to one would keep the step size (almost) constant at every step. + +.. cmdoption:: -m + + Stop when reaching the specified value of the + parameter lambda. By default, the tracing stops when lambda reaches + 1,000,000, which is usually suitable for computing a good + approximation to a Nash equilibrium. For applications, such as to + laboratory experiments, where the behavior of the correspondence for + small values of lambda is of interest and the asymptotic behavior is + not relevant, setting MAXLAMBDA to a much smaller value may be + indicated. + +.. cmdoption:: -l + + While tracing, compute the logit equilibrium points + with parameter LAMBDA accurately. + +.. cmdoption:: -S + + By default, the program uses behavior strategies for extensive + games; this switch instructs the program to use reduced strategic game + strategies for extensive games. (This has no effect for strategic + games, since a strategic game is its own reduced strategic game.) + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -e + + By default, all points computed are output by the program. If + this switch is specified, only the approximation to the Nash + equilibrium at the end of the branch is output. + +Computing the principal branch, in mixed strategies, of :download:`e02.nfg +<../contrib/games/e02.nfg>`, the reduced strategic form of the example +in Figure 2 of Selten (International Journal of Game Theory, +1975):: + + $ gambit-logit e02.nfg + Compute a branch of the logit equilibrium correspondence + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + 0.000000,0.333333,0.333333,0.333333,0.5,0.5 + 0.022853,0.335873,0.328284,0.335843,0.501962,0.498038 + 0.047978,0.338668,0.322803,0.33853,0.504249,0.495751 + 0.075600,0.341747,0.316863,0.34139,0.506915,0.493085 + 0.105965,0.345145,0.310443,0.344413,0.510023,0.489977 + 0.139346,0.348902,0.303519,0.347578,0.51364,0.48636 + + ... + + 735614.794714,1,0,4.40659e-11,0.500016,0.499984 + 809176.283787,1,0,3.66976e-11,0.500015,0.499985 + 890093.921767,1,0,3.05596e-11,0.500014,0.499986 + 979103.323545,1,0,2.54469e-11,0.500012,0.499988 + 1077013.665501,1,0,2.11883e-11,0.500011,0.499989 + diff --git a/doc/tools.lp.rst b/doc/tools.lp.rst new file mode 100644 index 000000000..557d7c401 --- /dev/null +++ b/doc/tools.lp.rst @@ -0,0 +1,72 @@ +.. _gambit-lp: + +:program:`gambit-lp`: Compute equilibria in a two-player constant-sum game via linear programming +================================================================================================= + +:program:`gambit-lp` reads a two-player constant-sum game on standard input +and computes a Nash equilibrium by solving a linear program. The +program uses the sequence form formulation of Koller, Megiddo, and von +Stengel [KolMegSte94]_ for extensive games. + +While the set of equilibria in a two-player constant-sum strategic +game is convex, this method will only identify one of the extreme +points of that set. + + +.. program:: gambit-lp + +.. cmdoption:: -d + + By default, this program computes using exact + rational arithmetic. Since the extreme points computed by this method + are guaranteed to be rational when the payoffs in the game are + rational, this permits exact computation of an equilibrium. + Computation using rational arithmetic is in general slow, however. For + most games, acceptable results can be obtained by computing using the + computer's native floating-point arithmetic. Using this flag enables + computation in floating-point, and expresses all output using decimal + representations with the specified number of digits. + +.. cmdoption:: -S + + By default, the program uses behavior strategies for extensive + games; this switch instructs the program to use reduced strategic game + strategies for extensive games. (This has no effect for strategic + games, since a strategic game is its own reduced strategic game.) + +.. cmdoption:: -D + + .. versionadded:: 14.0.3 + + The default output format for computed equilibria is a + comma-separated list of strategy or action probabilities, suitable + for postprocessing by automated tools. Specifying `-D` instead + causes the program to output greater detail on each equilbrium + profile computed. + +.. cmdoption:: -P + + By default, the program computes Nash equilibria in an extensive + game. This switch instructs the program to find only equilibria + which are subgame perfect. (This has no effect for strategic + games, since there are no proper subgames of a strategic game.) + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + +Computing an equilibrium of the game :download:`2x2const.nfg +<../contrib/games/2x2const.nfg>`, a game with two players with two +strategies each, with a unique equilibrium in mixed strategies:: + + $ gambit-lp 2x2const.nfg + Compute Nash equilibria by solving a linear program + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1/3,2/3,1/3,2/3 + diff --git a/doc/tools.rst b/doc/tools.rst index 46d6c246f..07ee320f8 100644 --- a/doc/tools.rst +++ b/doc/tools.rst @@ -1,7 +1,8 @@ .. _command-line: +****************** Command-line tools -================== +****************** Gambit provides command-line interfaces for each method for computing Nash equilibria. These are suitable for scripting or calling from @@ -19,7 +20,7 @@ a list of equilibria computed. The default output format is to present equilibria computed as a list of comma-separated probabilities, preceded by the tag `NE`. For mixed strategy profiles, the probabilities are sorted lexicographically by player, then by -strategy. For behavior strategy profiles, the probabilites are sorted +strategy. For behavior strategy profiles, the probabilities are sorted by player, then information set, then action number, where the information sets for a player are sorted by the order in which they are encountered in a depth-first traversal of the game tree. @@ -33,894 +34,18 @@ program-specific tags, described in the individual program documentation. -.. _gambit-enumpure: - -:program:`gambit-enumpure`: Enumerate pure-strategy equilibria of a game ------------------------------------------------------------------------- - -:program:`gambit-enumpure` reads a game on standard input and searches for -pure-strategy Nash equilibria. - -.. versionchanged:: 14.0.2 - The effect of the `-S` switch is now purely cosmetic, determining - how the equilibria computed are represented in the - output. Previously, `-S` computed using the strategic game; if this - was not specified for an extensive game, the agent form equilibria - were returned. - -.. program:: gambit-enumpure - -.. cmdoption:: -S - - Report equilibria in reduced strategic form strategies, even if the - game is an extensive game. By default, if passed an extensive - game, the output will be in behavior strategies. Specifying this switch - does not imply any change in operation internally, as pure-strategy - equilibria are defined in terms of reduced strategic form - strategies. - -.. cmdoption:: -D - - .. versionadded:: 14.0.2 - - The default output format for computed equilibria is a - comma-separated list of strategy or action probabilities, suitable - for postprocessing by automated tools. Specifying `-D` instead - causes the program to output greater detail on each equilbrium - profile computed. - -.. cmdoption:: -A - - .. versionadded:: 14.0.2 - - Report agent form equilibria, that is, equilibria which consider - only deviations at one information set. Only has an effect for - extensive games, as strategic games have only one information set - per player. - -.. cmdoption:: -P - - By default, the program computes all pure-strategy Nash - equilibria in an extensive game. This switch instructs the program to - find only pure-strategy Nash equilibria which are subgame perfect. - (This has no effect for strategic games, since there are no proper - subgames of a strategic game.) - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - - -Computing the pure-strategy equilibria of extensive game :download:`e02.efg -<../contrib/games/e02.efg>`, the example in Figure 2 of Selten -(International Journal of Game Theory, 1975):: - - $ gambit-enumpure e02.efg - Search for Nash equilibria in pure strategies - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1,0,0,0,1,0 - -With the `-S` switch, the set of equilibria returned is the same, -except expressed in strategic game strategies rather than behavior -strategies:: - - $ gambit-enumpure -S e02.efg - Search for Nash equilibria in pure strategies - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1,0,0,1,0 - -The `-A` switch considers only behavior strategy profiles where there -is no way for a player to improve his payoff by changing action at -only one information set; therefore the set of solutions is larger:: - - $ gambit-enumpure -A e02.efg - Search for Nash equilibria in pure strategies - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1,0,1,0,1,0 - NE,1,0,1,0,0,1 - NE,1,0,0,1,1,0 - - - -:program:`gambit-enumpoly`: Compute equilibria of a game using polynomial systems of equations ----------------------------------------------------------------------------------------------- - -:program:`gambit-enumpoly` reads a game on standard input and -computes Nash equilibria by solving systems of polynomial equations -and inequalities. - -This program searches for all Nash equilibria in a strategic game -using a support enumeration approach. This approach computes all the -supports which could, in principle, be the support of a Nash -equilibrium, and then searches for a totally mixed equilibrium on that -support by solving a system of polynomial equalities and inequalities -formed by the Nash equilibrium conditions. The ordering of the -supports is done in such a way as to maximize use of previously -computed information, making it suited to the computation of all Nash -equilibria. - -When the verbose switch `-v` is used, the program outputs each support -as it is considered. The supports are presented as a comma-separated -list of binary strings, where each entry represents one player. The -digit 1 represents a strategy which is present in the support, and the -digit 0 represents a strategy which is not present. Each candidate -support is printed with the label "candidate,". - -Note that the subroutine to compute a solution to the system of -polynomial equations and inequalities will fail in degenerate cases. -When the verbose switch `-v` is used, these supports are identified on -standard output with the label "singular,". It is possible that there -exist equilibria, often a connected component of equilibria, on these -singular supports. - - -.. program:: gambit-enumpoly - -.. cmdoption:: -d - - Express all output using decimal representations with the specified - number of digits. - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -H - - By default, the program uses an enumeration method designed to - visit as few supports as possible in searching for all equilibria. - With this switch, the program uses a heuristic search method based on - Porter, Nudelman, and Shoham [PNS04]_, which is designed to minimize the - time until the first equilibrium is found. This switch only has an - effect when solving strategic games. - -.. cmdoption:: -S - - By default, the program uses behavior strategies for extensive - games; this switch instructs the program to use reduced strategic game - strategies for extensive games. (This has no effect for strategic - games, since a strategic game is its own reduced strategic game.) - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - -.. cmdoption:: -v - - Sets verbose mode. In verbose mode, supports are printed on - standard output with the label "candidate" as they are considered, and - singular supports are identified with the label "singular." By - default, no information about supports is printed. - -Computing equilbria of the extensive game :download:`e01.efg -<../contrib/games/e01.efg>`, the example in Figure 1 of Selten -(International Journal of Game Theory, 1975) sometimes called -"Selten's horse":: - - $ gambit-enumpoly e01.efg - Compute Nash equilibria by solving polynomial systems - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - Heuristic search implementation Copyright (C) 2006, Litao Wei - This is free software, distributed under the GNU GPL - - NE,0.000000,1.000000,0.333333,0.666667,1.000000,0.000000 - NE,1.000000,0.000000,1.000000,0.000000,0.250000,0.750000 - NE,1.000000,0.000000,1.000000,0.000000,0.000000,0.000000 - NE,0.000000,1.000000,0.000000,0.000000,1.000000,0.000000 - - -.. _gambit-enummixed: - -:program:`gambit-enummixed`: Enumerate equilibria in a two-player game ----------------------------------------------------------------------- - -:program:`gambit-enummixed` reads a two-player game on standard input and -computes Nash equilibria using extreme point enumeration. - -In a two-player strategic game, the set of Nash equilibria can be expressed -as the union of convex sets. This program generates all the extreme -points of those convex sets. (Mangasarian [Man64]_) -This is a superset of the points generated by the path-following -procedure of Lemke and Howson (see :ref:`gambit-lcp`). It was -shown by Shapley [Sha74]_ that there are equilibria not accessible via -the method in :ref:`gambit-lcp`, whereas the output of -:program:`gambit-enummixed` is guaranteed to return all the extreme -points. - -.. program:: gambit-enummixed - -.. cmdoption:: -d - - By default, this program computes using exact - rational arithmetic. Since the extreme points computed by this method - are guaranteed to be rational when the payoffs in the game are - rational, this permits exact computation of the equilibrium set. - Computation using rational arithmetic is in general slow, however. For - most games, acceptable results can be obtained by computing using the - computer's native floating-point arithmetic. Using this flag enables - computation in floating-point, and expresses all output using decimal - representations with the specified number of digits. - -.. cmdoption:: -D - - Since all Nash equilibria involve only strategies which survive - iterative elimination of strictly dominated strategies, the program - carries out the elimination automatically prior to computation. - This is recommended, since it almost always results in superior - performance. - Specifying `-D` skips the elimination step and performs the - enumeration on the full game. - -.. cmdoption:: -c - - The program outputs the extreme equilibria as it finds them, - prefixed by the tag NE . If this option is specified, once all extreme - equilbria are identified, the program computes the convex sets which - make up the set of equilibria. The program then additionally outputs - each convex set, prefixed by convex-N , where N indexes the set. The - set of all equilibria, then, is the union of these convex sets. - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - -.. cmdoption:: -L - - Use `lrslib `_ by David Avis - to carry out the enumeration process. This is an experimental - feature that has not been widely tested. - -Computing the equilibria, in mixed strategies, of :download:`e02.nfg -<../contrib/games/e02.nfg>`, the reduced strategic form of the example -in Figure 2 of Selten (International Journal of Game Theory, -1975):: - - $ gambit-enummixed e02.nfg - Compute Nash equilibria by enumerating extreme points - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - Enumeration code based on lrslib 4.2b, - Copyright (C) 1995-2005 by David Avis (avis@cs.mcgill.ca) - This is free software, distributed under the GNU GPL - - NE,1,0,0,1,0 - NE,1,0,0,1/2,1/2 - -In fact, the game e02.nfg has a one-dimensional continuum of -equilibria. This fact can be observed by examining the connectedness -information using the `-c` switch:: - - $ gambit-enummixed -c e02.nfg - Compute Nash equilibria by enumerating extreme points - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - Enumeration code based on lrslib 4.2b, - Copyright (C) 1995-2005 by David Avis (avis@cs.mcgill.ca) - This is free software, distributed under the GNU GPL - - NE,1,0,0,1,0 - NE,1,0,0,1/2,1/2 - convex-1,1,0,0,1/2,1/2 - convex-1,1,0,0,1,0 - - -.. _gambit-gnm: - -:program:`gambit-gnm`: Compute Nash equilibria in a strategic game using a global Newton method ------------------------------------------------------------------------------------------------ - -:program:`gambit-gnm` reads a game on standard input and computes Nash -equilibria using a global Newton method approach developed by Govindan -and Wilson [GovWil03]_. This program is a wrapper around the -`Gametracer 0.2 `_ -implementation by Ben Blum and Christian Shelton. - -.. program:: gambit-gnm - -.. cmdoption:: -d - - Express all output using decimal representations - with the specified number of digits. - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -n - - Randomly generate the specified number of perturbation vectors. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - -.. cmdoption:: -s - - Specifies a file containing a list of starting points - for the algorithm. The format of the file is comma-separated values, - one mixed strategy profile per line, in the same format used for - output of equilibria (excluding the initial NE tag). - -.. cmdoption:: -v - - Show intermediate output of the algorithm. If this option is - not specified, only the equilibria found are reported. - -Computing an equilibrium of :download:`e02.nfg <../contrib/games/e02.nfg>`, -the reduced strategic form of the example in Figure 2 of Selten -(International Journal of Game Theory, 1975):: - - $ gambit-gnm e02.nfg - Compute Nash equilibria using a global Newton method - Gametracer version 0.2, Copyright (C) 2002, Ben Blum and Christian Shelton - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1,0,2.99905e-12,0.5,0.5 - - -.. note:: - - This is an experimental program and has not been extensively tested. - -.. seealso:: - - :ref:`gambit-ipa`. - - -.. _gambit-ipa: - -:program:`gambit-ipa`: Compute Nash equilibria in a strategic game using iterated polymatrix approximation ----------------------------------------------------------------------------------------------------------- - -:program:`gambit-ipa` reads a game on standard input and computes Nash -equilibria using an iterated polymatrix approximation approach -developed by Govindan and Wilson [GovWil04]_. -This program is a wrapper around the -`Gametracer 0.2 `_ -implementation by Ben Blum and Christian Shelton. - -.. program:: gambit-ipa - -.. cmdoption:: -d - - Express all output using decimal representations - with the specified number of digits. - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - - -Computing an equilibrium of :download:`e02.nfg <../contrib/games/e02.nfg>`, -the reduced strategic form of the example in Figure 2 of Selten -(International Journal of Game Theory, 1975):: - - $ gambit-ipa e02.nfg - Compute Nash equilibria using iterated polymatrix approximation - Gametracer version 0.2, Copyright (C) 2002, Ben Blum and Christian Shelton - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1.000000,0.000000,0.000000,1.000000,0.000000 - - - -.. note:: - - This is an experimental program and has not been extensively tested. - -.. seealso:: - - :ref:`gambit-gnm`. - - -.. _gambit-lcp: - -:program:`gambit-lcp`: Compute equilibria in a two-player game via linear complementarity ------------------------------------------------------------------------------------------ - - -:program:`gambit-lcp` reads a two-player game on standard input and -computes Nash equilibria by finding solutions to a linear -complementarity problem. For extensive games, the program uses the -sequence form representation of the extensive game, as defined by -Koller, Megiddo, and von Stengel [KolMegSte94]_, and applies the -algorithm developed by Lemke. For strategic games, the program using -the method of Lemke and Howson [LemHow64]_. There exist strategic -games for which some equilibria cannot be located by this method; see -Shapley [Sha74]_. - -In a two-player strategic game, the set of Nash equilibria can be expressed -as the union of convex sets. This program will find extreme points -of those convex sets. See :ref:`gambit-enummixed` for a method -which is guaranteed to find all the extreme points for a strategic -game. - -.. program:: gambit-lcp - -.. cmdoption:: -d - - By default, this program computes using exact - rational arithmetic. Since the extreme points computed by this method - are guaranteed to be rational when the payoffs in the game are - rational, this permits exact computation of the equilibrium set. - Computation using rational arithmetic is in general slow, however. For - most games, acceptable results can be obtained by computing using the - computer's native floating-point arithmetic. Using this flag enables - computation in floating-point, and expresses all output using decimal - representations with the specified number of digits. - -.. cmdoption:: -S - - By default, the program uses behavior strategies for extensive - games; this switch instructs the program to use reduced strategic game - strategies for extensive games. (This has no effect for strategic - games, since a strategic game is its own reduced strategic game.) - -.. cmdoption:: -D - - .. versionadded:: 14.0.2 - - The default output format for computed equilibria is a - comma-separated list of strategy or action probabilities, suitable - for postprocessing by automated tools. Specifying `-D` instead - causes the program to output greater detail on each equilbrium - profile computed. - -.. cmdoption:: -P - - By default, the program computes Nash equilibria in an extensive - game. This switch instructs the program to find only equilibria - which are subgame perfect. (This has no effect for strategic - games, since there are no proper subgames of a strategic game.) - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - - -Computing an equilibrium of extensive game :download:`e02.efg -<../contrib/games/e02.efg>`, the example in Figure 2 of Selten -(International Journal of Game Theory, 1975):: - - $ gambit-lcp e02.efg - Compute Nash equilibria by solving a linear complementarity program - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1,0,1/2,1/2,1/2,1/2 - - -.. _gambit-lp: - -:program:`gambit-lp`: Compute equilibria in a two-player constant-sum game via linear programming -------------------------------------------------------------------------------------------------- - -:program:`gambit-lp` reads a two-player constant-sum game on standard input -and computes a Nash equilibrium by solving a linear program. The -program uses the sequence form formulation of Koller, Megiddo, and von -Stengel [KolMegSte94]_ for extensive games. - -While the set of equilibria in a two-player constant-sum strategic -game is convex, this method will only identify one of the extreme -points of that set. - - -.. program:: gambit-lp - -.. cmdoption:: -d - - By default, this program computes using exact - rational arithmetic. Since the extreme points computed by this method - are guaranteed to be rational when the payoffs in the game are - rational, this permits exact computation of an equilibrium. - Computation using rational arithmetic is in general slow, however. For - most games, acceptable results can be obtained by computing using the - computer's native floating-point arithmetic. Using this flag enables - computation in floating-point, and expresses all output using decimal - representations with the specified number of digits. - -.. cmdoption:: -S - - By default, the program uses behavior strategies for extensive - games; this switch instructs the program to use reduced strategic game - strategies for extensive games. (This has no effect for strategic - games, since a strategic game is its own reduced strategic game.) - -.. cmdoption:: -D - - .. versionadded:: 14.0.3 - - The default output format for computed equilibria is a - comma-separated list of strategy or action probabilities, suitable - for postprocessing by automated tools. Specifying `-D` instead - causes the program to output greater detail on each equilbrium - profile computed. - -.. cmdoption:: -P - - By default, the program computes Nash equilibria in an extensive - game. This switch instructs the program to find only equilibria - which are subgame perfect. (This has no effect for strategic - games, since there are no proper subgames of a strategic game.) - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - -Computing an equilibrium of the game :download:`2x2const.nfg -<../contrib/games/2x2const.nfg>`, a game with two players with two -strategies each, with a unique equilibrium in mixed strategies:: - - $ gambit-lp 2x2const.nfg - Compute Nash equilibria by solving a linear program - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1/3,2/3,1/3,2/3 - - -.. _gambit-liap: - -:program:`gambit-liap`: Compute Nash equilibria using function minimization ---------------------------------------------------------------------------- - -:program:`gambit-liap` reads a game on standard input and computes -approximate Nash equilibria using a function minimization approach. - -This procedure searches for equilibria by generating random starting -points and using conjugate gradient descent to minimize the Lyapunov -function of the game. This function is a nonnegative function which is -zero exactly at strategy profiles which are Nash equilibria. - -Note that this procedure is not globally convergent. That is, it is -not guaranteed to find all, or even any, Nash equilibria. - - -.. program:: gambit-liap - -.. cmdoption:: -d - - Express all output using decimal representations with the - specified number of digits. - -.. cmdoption:: -n - - Specify the number of starting points to randomly generate. - -.. cmdoption:: -i - - .. versionadded:: 16.1.0 - - Specify the maximum number of iterations in function minimization (default is 100). - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - -.. cmdoption:: -s - - Specifies a file containing a list of starting points - for the algorithm. The format of the file is comma-separated values, - one mixed strategy profile per line, in the same format used for - output of equilibria (excluding the initial NE tag). - -.. cmdoption:: -S - - By default, the program uses behavior strategies for extensive - games; this switch instructs the program to use reduced strategic game - strategies for extensive games. (This has no effect for strategic - games, since a strategic game is its own reduced strategic game.) - -.. cmdoption:: -v - - Sets verbose mode. In verbose mode, initial points, as well as - points at which the minimization fails at a constrained local minimum - that is not a Nash equilibrium, are all output, in addition to any - equilibria found. - -Computing an equilibrium in mixed strategies of :download:`e02.efg -<../contrib/games/e02.efg>`, the example in Figure 2 of Selten -(International Journal of Game Theory, 1975):: - - $ gambit-liap e02.nfg - Compute Nash equilibria by minimizing the Lyapunov function - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE, 0.998701, 0.000229, 0.001070, 0.618833, 0.381167 - - - -.. _gambit-simpdiv: - -:program:`gambit-simpdiv`: Compute equilibria via simplicial subdivision ------------------------------------------------------------------------- - -:program:`gambit-simpdiv` reads a game on standard input and computes -approximations to Nash equilibria using a simplicial subdivision -approach. - -This program implements the algorithm of van der Laan, Talman, and van -Der Heyden [VTH87]_. The algorithm proceeds by constructing a triangulated grid -over the space of mixed strategy profiles, and uses a path-following -method to compute an approximate fixed point. This approximate fixed -point can then be used as a starting point on a refinement of the -grid. The program continues this process with finer and finer grids -until locating a mixed strategy profile at which the maximum regret is -small. - -The algorithm begins with any mixed strategy profile consisting of -rational numbers as probabilities. Without any options, the algorithm -begins with the centroid, and computes one Nash equilibrium. To -attempt to compute other equilibria that may exist, use the -:option:`gambit-simpdiv -r` or :option:`gambit-simpdiv -s` -options to specify additional starting points for the algorithm. - -.. program:: gambit-simpdiv - -.. cmdoption:: -g - - Sets the granularity of the grid refinement. By - default, when the grid is refined, the stepsize is cut in half, which - corresponds to specifying `-g 2`. If this parameter is specified, the - grid is refined at each step by a multiple of MULT . - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -n - - Randomly generate COUNT starting points. Only - applicable if option :option:`gambit-simpdiv -r` is also specified. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - -.. cmdoption:: -r - - Generate random starting points with denominator DENOM. - Since this algorithm operates on a grid, by its nature the - probabilities it works with are always rational numbers. If this - parameter is specified, starting points for the procedure are - generated randomly using the uniform distribution over strategy - profiles with probabilities having denominator DENOM. - -.. cmdoption:: -s - - Specifies a file containing a list of starting points - for the algorithm. The format of the file is comma-separated values, - one mixed strategy profile per line, in the same format used for - output of equilibria (excluding the initial NE tag). - -.. cmdoption:: -v - - Sets verbose mode. In verbose mode, initial points, as well as - the approximations computed at each grid refinement, are all output, - in addition to the approximate equilibrium profile found. - - -Computing an equilibrium in mixed strategies of :download:`e02.efg -<../contrib/games/e02.efg>`, the example in Figure 2 of Selten -(International Journal of Game Theory, 1975):: - - $ gambit-simpdiv e02.nfg - Compute Nash equilibria using simplicial subdivision - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - NE,1,0,0,1,0 - - -.. _gambit-logit: - -:program:`gambit-logit`: Compute quantal response equilbria ------------------------------------------------------------ - -:program:`gambit-logit` reads a game on standard input and computes the -principal branch of the (logit) quantal response correspondence. - -The method is based on the procedure described in Turocy [Tur05]_ for -strategic games and Turocy [Tur10]_ for extensive games. -It uses standard path-following methods (as -described in Allgower and Georg's "Numerical Continuation Methods") to -adaptively trace the principal branch of the correspondence -efficiently and securely. - -The method used is a predictor-corrector method, which first generates -a prediction using the differential equations describing the branch of -the correspondence, followed by a corrector step which refines the -prediction using Newton's method for finding a zero of a function. Two -parameters control the operation of this tracing. The option `-s` sets -the initial step size for the predictor phase of the tracing. This -step size is then dynamically adjusted based on the rate of -convergence of Newton's method in the corrector step. If the -convergence is fast, the step size is adjusted upward (accelerated); -if it is slow, the step size is decreased (decelerated). The option -`-a` sets the maximum acceleration (or deceleration). As described in -Turocy [Tur05]_, this acceleration helps to -efficiently trace the correspondence when it reaches its asymptotic -phase for large values of the precision parameter lambda. - -.. program:: gambit-logit - -.. cmdoption:: -d - - Express all output using decimal representations with the specified - number of digits. The default is `-d 6`. - -.. cmdoption:: -s - - Sets the initial step size for the predictor phase of - the tracing procedure. The default value is .03. The step size is - specified in terms of the arclength along the branch of the - correspondence, and not the size of the step measured in terms of - lambda. So, for example, if the step size is currently .03, but the - position of the strategy profile on the branch is changing rapidly - with lambda, then lambda will change by much less then .03 between - points reported by the program. - -.. cmdoption:: -a - - Sets the maximum acceleration of the step size during - the tracing procedure. This is interpreted as a multiplier. The - default is 1.1, which means the step size is increased or decreased by - no more than ten percent of its current value at every step. A value - close to one would keep the step size (almost) constant at every step. - -.. cmdoption:: -m - - Stop when reaching the specified value of the - parameter lambda. By default, the tracing stops when lambda reaches - 1,000,000, which is usually suitable for computing a good - approximation to a Nash equilibrium. For applications, such as to - laboratory experiments, where the behavior of the correspondence for - small values of lambda is of interest and the asymptotic behavior is - not relevant, setting MAXLAMBDA to a much smaller value may be - indicated. - -.. cmdoption:: -l - - While tracing, compute the logit equilibrium points - with parameter LAMBDA accurately. - -.. cmdoption:: -S - - By default, the program uses behavior strategies for extensive - games; this switch instructs the program to use reduced strategic game - strategies for extensive games. (This has no effect for strategic - games, since a strategic game is its own reduced strategic game.) - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -e - - By default, all points computed are output by the program. If - this switch is specified, only the approximation to the Nash - equilibrium at the end of the branch is output. - -Computing the principal branch, in mixed strategies, of :download:`e02.nfg -<../contrib/games/e02.nfg>`, the reduced strategic form of the example -in Figure 2 of Selten (International Journal of Game Theory, -1975):: - - $ gambit-logit e02.nfg - Compute a branch of the logit equilibrium correspondence - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - 0.000000,0.333333,0.333333,0.333333,0.5,0.5 - 0.022853,0.335873,0.328284,0.335843,0.501962,0.498038 - 0.047978,0.338668,0.322803,0.33853,0.504249,0.495751 - 0.075600,0.341747,0.316863,0.34139,0.506915,0.493085 - 0.105965,0.345145,0.310443,0.344413,0.510023,0.489977 - 0.139346,0.348902,0.303519,0.347578,0.51364,0.48636 - - ... - - 735614.794714,1,0,4.40659e-11,0.500016,0.499984 - 809176.283787,1,0,3.66976e-11,0.500015,0.499985 - 890093.921767,1,0,3.05596e-11,0.500014,0.499986 - 979103.323545,1,0,2.54469e-11,0.500012,0.499988 - 1077013.665501,1,0,2.11883e-11,0.500011,0.499989 - - -:program:`gambit-convert`: Convert games among various representations ----------------------------------------------------------------------- - -:program:`gambit-convert` reads a game on standard input in any supported format -and converts it to another text representation. Currently, this tool supports -outputting the strategic form of the game in one of these formats: - -* A standard HTML table. -* A LaTeX fragment in the format of Martin Osborne's `sgame` macros - (see http://www.economics.utoronto.ca/osborne/latex/index.html). - - -.. program:: gambit-convert - -.. cmdoption:: -O FORMAT - - Required. Specifies the output format. Supported options for - `FORMAT` are `html` or `sgame`. - -.. cmdoption:: -r PLAYER - - Specifies the player number to place on the rows of the tables. - The default if not specified is to place player 1 on the rows. - -.. cmdoption:: -c PLAYER - - Specifies the player number to place on the columns of the tables. - The default if not specified is to place player 2 on the columns. - -.. cmdoption:: -h - - Prints a help message listing the available options. - -.. cmdoption:: -q - - Suppresses printing of the banner at program launch. - - -Example invocation for HTML output:: - - $ gambit-convert -O html 2x2.nfg - Convert games among various file formats - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - -

Two person 2 x 2 game with unique mixed equilibrium

-
12
12,00,1
20,11,0
- - -Example invocation for LaTeX output:: - - $ gambit-convert -O sgame 2x2.nfg - Convert games among various file formats - Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project - This is free software, distributed under the GNU GPL - - \begin{game}{2}{2}[Player 1][Player 2] - &1 & 2\\ - 1 & $2,0$ & $0,1$ \\ - 2 & $0,1$ & $1,0$ - \end{game} +.. toctree:: + :maxdepth: 2 + + tools.enumpure + tools.enummixed + tools.enumpoly + tools.lcp + tools.lp + tools.liap + tools.simpdiv + tools.logit + tools.gnm + tools.ipa + tools.convert diff --git a/doc/tools.simpdiv.rst b/doc/tools.simpdiv.rst new file mode 100644 index 000000000..16d7c7acf --- /dev/null +++ b/doc/tools.simpdiv.rst @@ -0,0 +1,80 @@ +.. _gambit-simpdiv: + +:program:`gambit-simpdiv`: Compute equilibria via simplicial subdivision +======================================================================== + +:program:`gambit-simpdiv` reads a game on standard input and computes +approximations to Nash equilibria using a simplicial subdivision +approach. + +This program implements the algorithm of van der Laan, Talman, and van +Der Heyden [VTH87]_. The algorithm proceeds by constructing a triangulated grid +over the space of mixed strategy profiles, and uses a path-following +method to compute an approximate fixed point. This approximate fixed +point can then be used as a starting point on a refinement of the +grid. The program continues this process with finer and finer grids +until locating a mixed strategy profile at which the maximum regret is +small. + +The algorithm begins with any mixed strategy profile consisting of +rational numbers as probabilities. Without any options, the algorithm +begins with the centroid, and computes one Nash equilibrium. To +attempt to compute other equilibria that may exist, use the +:option:`gambit-simpdiv -r` or :option:`gambit-simpdiv -s` +options to specify additional starting points for the algorithm. + +.. program:: gambit-simpdiv + +.. cmdoption:: -g + + Sets the granularity of the grid refinement. By + default, when the grid is refined, the stepsize is cut in half, which + corresponds to specifying `-g 2`. If this parameter is specified, the + grid is refined at each step by a multiple of MULT . + +.. cmdoption:: -h + + Prints a help message listing the available options. + +.. cmdoption:: -n + + Randomly generate COUNT starting points. Only + applicable if option :option:`gambit-simpdiv -r` is also specified. + +.. cmdoption:: -q + + Suppresses printing of the banner at program launch. + +.. cmdoption:: -r + + Generate random starting points with denominator DENOM. + Since this algorithm operates on a grid, by its nature the + probabilities it works with are always rational numbers. If this + parameter is specified, starting points for the procedure are + generated randomly using the uniform distribution over strategy + profiles with probabilities having denominator DENOM. + +.. cmdoption:: -s + + Specifies a file containing a list of starting points + for the algorithm. The format of the file is comma-separated values, + one mixed strategy profile per line, in the same format used for + output of equilibria (excluding the initial NE tag). + +.. cmdoption:: -v + + Sets verbose mode. In verbose mode, initial points, as well as + the approximations computed at each grid refinement, are all output, + in addition to the approximate equilibrium profile found. + + +Computing an equilibrium in mixed strategies of :download:`e02.efg +<../contrib/games/e02.efg>`, the example in Figure 2 of Selten +(International Journal of Game Theory, 1975):: + + $ gambit-simpdiv e02.nfg + Compute Nash equilibria using simplicial subdivision + Gambit version 16.1.0a3, Copyright (C) 1994-2023, The Gambit Project + This is free software, distributed under the GNU GPL + + NE,1,0,0,1,0 From 9169283fe923d47ad351928888e47ea6e0a9e61a Mon Sep 17 00:00:00 2001 From: Ted Turocy Date: Thu, 5 Oct 2023 12:09:19 +0100 Subject: [PATCH 2/6] Remove wxWidgets 2.4 compatibility shim. There was some remaining backwards-compatibility code for supporting wxWidgets 2.4 hanging around. As we now require wxWidgets 3 this is obsolete. This closes #283. --- ChangeLog | 6 ++ Makefile.am | 1 - src/labenski/include/wx/sheet/sheetdef.h | 5 +- src/labenski/include/wx/sheet/wx24defs.h | 72 ------------------------ src/labenski/src/sheetedt.cpp | 12 ++-- 5 files changed, 13 insertions(+), 83 deletions(-) delete mode 100644 src/labenski/include/wx/sheet/wx24defs.h diff --git a/ChangeLog b/ChangeLog index ee82a09a6..816bdb521 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ # Changelog +## [16.1.0a4] - unreleased + +### Changed +- Remaining compatibility code for wxWidgets 2.x removed from graphical interface. + + ## [16.1.0a3] - 2023-09-29 ### Changed diff --git a/Makefile.am b/Makefile.am index 3b54a3541..29a5fb821 100644 --- a/Makefile.am +++ b/Makefile.am @@ -682,7 +682,6 @@ gambit_SOURCES = \ src/labenski/include/wx/sheet/sheetsel.h \ src/labenski/include/wx/sheet/sheetspt.h \ src/labenski/include/wx/sheet/sheettbl.h \ - src/labenski/include/wx/sheet/wx24defs.h \ src/labenski/include/wx/wxthings/block.h \ src/labenski/include/wx/wxthings/bmpcombo.h \ src/labenski/include/wx/wxthings/dropdown.h \ diff --git a/src/labenski/include/wx/sheet/sheetdef.h b/src/labenski/include/wx/sheet/sheetdef.h index 5c4bc7ce8..bd1902c6e 100644 --- a/src/labenski/include/wx/sheet/sheetdef.h +++ b/src/labenski/include/wx/sheet/sheetdef.h @@ -14,11 +14,8 @@ #include "wx/defs.h" #include "wx/datetime.h" -#include "wx/sheet/wx24defs.h" // wx2.4 backwards compatibility +#include "wx/event.h" #include "wx/dynarray.h" -#if wxMINOR_VERSION > 4 - #include "wx/arrstr.h" -#endif #include "wx/sheet/pairarr.h" // ---------------------------------------------------------------------------- diff --git a/src/labenski/include/wx/sheet/wx24defs.h b/src/labenski/include/wx/sheet/wx24defs.h deleted file mode 100644 index 291727176..000000000 --- a/src/labenski/include/wx/sheet/wx24defs.h +++ /dev/null @@ -1,72 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Name: wx24defs.h -// Purpose: Definitions to make a program using wxWidgets >= 2.5 work in 2.4 -// Author: John Labenski -// Modified by: -// Created: 3/10/04 -// RCS-ID: -// Copyright: (c) John Labenski -// Licence: wxWidgets licence -/////////////////////////////////////////////////////////////////////////////// - -#ifndef __WX_WX24DEFS_H__ -#define __WX_WX24DEFS_H__ - -// Include any headers that we need to redefine macros for -#include "wx/defs.h" -#include "wx/object.h" -#include "wx/event.h" -#include "wx/dynarray.h" - -//----------------------------------------------------------------------------- -// wxWidgets 2.4 compatibility with code from >= 2.5 - -#ifndef WXDLLIMPEXP_ADV - #define WXDLLIMPEXP_ADV WXDLLEXPORT -#endif - -#ifndef DECLARE_NO_ASSIGN_CLASS - #define DECLARE_NO_ASSIGN_CLASS(classname) \ - private: \ - classname& operator=(const classname&); -#endif - -#ifndef WX_DECLARE_OBJARRAY_WITH_DECL - #define WX_DECLARE_OBJARRAY_WITH_DECL(T, name, expmode) WX_DECLARE_USER_EXPORTED_OBJARRAY(T, name, WXDLLEXPORT) -#endif - -#ifndef WX_DEFINE_ARRAY_WITH_DECL_PTR - #define WX_DEFINE_ARRAY_WITH_DECL_PTR(T, name, decl) WX_DEFINE_ARRAY(T, name) -#endif - -#ifndef WX_DEFINE_ARRAY_PTR - #define WX_DEFINE_ARRAY_PTR(T, name) WX_DEFINE_ARRAY(T, name) -#endif - -#ifndef DECLARE_EXPORTED_EVENT_TYPE - #define DECLARE_EXPORTED_EVENT_TYPE(expdecl, name, value) DECLARE_LOCAL_EVENT_TYPE(name, value) -#endif - -#ifndef wxStaticCastEvent - #define wxStaticCastEvent(type, val) (type) val -#endif - -#ifndef wxFULL_REPAINT_ON_RESIZE - #define wxFULL_REPAINT_ON_RESIZE 0 -#endif - -// ---------------------------------------------------------------------------- -#if !wxCHECK_VERSION(2,5,0) - - // wxMenuItemList::compatibility_iterator was wxMenuItemList::Node* - #define compatibility_iterator Node* - -#endif - -/* -#ifndef wxSL_INVERSE // !wxCHECK_VERSION(2,5,3) // FIXME temp fix until 2.5.4 - #define wxDefaultDateTimeFormat wxT("%c") -#endif // wxInvalidDateTime -*/ - -#endif //__WX_WX24DEFS_H__ diff --git a/src/labenski/src/sheetedt.cpp b/src/labenski/src/sheetedt.cpp index 61183f527..f5ed57c76 100644 --- a/src/labenski/src/sheetedt.cpp +++ b/src/labenski/src/sheetedt.cpp @@ -137,7 +137,7 @@ void wxSheetCellEditorEvtHandler::OnDestroy(wxWindowDestroyEvent& event) // when parent window is destroyed pop event handler and NULL the control if (m_editor->m_control && (m_editor->m_control->GetEventHandler() == this)) { - // see if anyone else want's to process the event before we delete ourself + // see if anyone else wants to process the event before we delete ourselves if (!GetNextHandler()->ProcessEvent(event)) { wxWindow *win = m_editor->m_control; @@ -595,7 +595,7 @@ bool wxSheetCellTextEditorRefData::OnChar( wxKeyEvent& WXUNUSED(event) ) wxRect rect(GetTextCtrl()->GetRect()); int clientWidth = GetTextCtrl()->GetParent()->GetClientSize().x; - // already at edge of window, maybe the win should should scroll? + // already at edge of window, maybe the win should scroll? if (rect.GetRight() >= clientWidth) return true; @@ -1339,12 +1339,12 @@ void wxSheetCellChoiceEditorRefData::CreateEditor(wxWindow* parent, wxEvtHandler* evtHandler, wxSheet* sheet) { -#if wxMINOR_VERSION > 4 +#if wxCHECK_VERSION(2, 5, 0) SetControl( new wxComboBox(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, m_choices, m_allowOthers ? 0 : wxCB_READONLY) ); -#else +#else const size_t count = m_choices.GetCount(); wxString *choices = new wxString[count]; for ( size_t n = 0; n < count; n++ ) @@ -1356,7 +1356,7 @@ void wxSheetCellChoiceEditorRefData::CreateEditor(wxWindow* parent, m_allowOthers ? 0 : wxCB_READONLY) ); delete []choices; -#endif // wxMINOR_VERSION > 4 +#endif // wxCHECK_VERSION wxSheetCellEditorRefData::CreateEditor(parent, id, evtHandler, sheet); } @@ -1440,7 +1440,7 @@ wxString wxSheetCellChoiceEditorRefData::GetValue() const // ---------------------------------------------------------------------------- #if wxUSE_COMBOBOX -// A cell editor which displays an enum number as a textual equivalent. eg +// A cell editor which displays an enum number as a textual equivalent. e.g. // data in cell is 0,1,2 ... n the cell could be displayed as // "John","Fred"..."Bob" in the combo choice box From 0c11edb99c0c1f2c095ab2a5c60526ee2c59c1a7 Mon Sep 17 00:00:00 2001 From: Rahul Savani Date: Wed, 4 Oct 2023 17:29:32 +0100 Subject: [PATCH 3/6] Game element resolution methods no longer accept empty string for labels. Closes #356 --- ChangeLog | 1 + src/pygambit/game.pxi | 54 ++++++--- .../tests/test_game_resolve_functions.py | 114 ++++++++++++++++++ 3 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 src/pygambit/tests/test_game_resolve_functions.py diff --git a/ChangeLog b/ChangeLog index 816bdb521..1ef0e1d25 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ ## [16.1.0a4] - unreleased ### Changed +- Empty or all-whitespace strings cannot be used to access members of games in pygambit. - Remaining compatibility code for wxWidgets 2.x removed from graphical interface. diff --git a/src/pygambit/game.pxi b/src/pygambit/game.pxi index afbf83b10..5d43b1d8f 100644 --- a/src/pygambit/game.pxi +++ b/src/pygambit/game.pxi @@ -164,7 +164,7 @@ class Game: with one node, which is both root and terminal. .. versionchanged:: 16.1.0 - Added the `players` and `title` parameters + Added the `players` and `title` parameters Parameters ---------- @@ -761,17 +761,17 @@ class Game: on). * `html`: A rendering of the strategic form of the game as a - collection of HTML tables. The first player is the row - chooser; the second player the column chooser. For games with - more than two players, a collection of tables is generated, - one for each possible strategy combination of players 3 and higher. + collection of HTML tables. The first player is the row + chooser; the second player the column chooser. For games with + more than two players, a collection of tables is generated, + one for each possible strategy combination of players 3 and higher. * `sgame`: A rendering of the strategic form of the game in - LaTeX, suitable for use with `Martin Osborne's sgame style - `_. - The first player is the row - chooser; the second player the column chooser. For games with - more than two players, a collection of tables is generated, - one for each possible strategy combination of players 3 and higher. + LaTeX, suitable for use with `Martin Osborne's sgame style + `_. + The first player is the row + chooser; the second player the column chooser. For games with + more than two players, a collection of tables is generated, + one for each possible strategy combination of players 3 and higher. """ if format == 'gte': return pygambit.gte.write_game(self) @@ -798,12 +798,16 @@ class Game: If `player` is a string and no player in the game has that label. TypeError If `player` is not a `Player` or a `str` + ValueError + If `player` is an empty `str` or all spaces """ if isinstance(player, Player): if player.game != self: raise MismatchError(f"{funcname}(): {argname} must be part of the same game") return player elif isinstance(player, str): + if not player.strip(): + raise ValueError(f"{funcname}(): {argname} cannot be an empty string or all spaces") try: return self.players[player] except IndexError: @@ -830,12 +834,16 @@ class Game: If `outcome` is a string and no outcome in the game has that label. TypeError If `outcome` is not an `Outcome` or a `str` + ValueError + If `outcome` is an empty `str` or all spaces """ if isinstance(outcome, Outcome): if outcome.game != self: raise MismatchError(f"{funcname}(): {argname} must be part of the same game") return outcome elif isinstance(outcome, str): + if not outcome.strip(): + raise ValueError(f"{funcname}(): {argname} cannot be an empty string or all spaces") try: return self.outcomes[outcome] except IndexError: @@ -862,12 +870,16 @@ class Game: If `strategy` is a string and no strategy in the game has that label. TypeError If `strategy` is not a `Strategy` or a `str` + ValueError + If `strategy` is an empty `str` or all spaces """ if isinstance(strategy, Strategy): if strategy.game != self: raise MismatchError(f"{funcname}(): {argname} must be part of the same game") return strategy elif isinstance(strategy, str): + if not strategy.strip(): + raise ValueError(f"{funcname}(): {argname} cannot be an empty string or all spaces") try: return self.strategies[strategy] except IndexError: @@ -894,16 +906,20 @@ class Game: If `node` is a string and no node in the game has that label. TypeError If `node` is not a `Node` or a `str` + ValueError + If `node` is an empty `str` or all spaces """ if isinstance(node, Node): if node.game != self: raise MismatchError(f"{funcname}(): {argname} must be part of the same game") return node elif isinstance(node, str): - try: - return self.nodes[node] - except IndexError: - raise KeyError(f"{funcname}(): no node with label '{node}'") + if not node.strip(): + raise ValueError(f"{funcname}(): {argname} cannot be an empty string or all spaces") + for n in self.nodes(): + if n.label == node: + return n + raise KeyError(f"{funcname}(): no node with label '{node}'") raise TypeError(f"{funcname}(): {argname} must be Node or str, not {node.__class__.__name__}") def _resolve_infoset(self, infoset: typing.Any, funcname: str, argname: str = "infoset") -> Infoset: @@ -926,12 +942,16 @@ class Game: If `infoset` is a string and no information set in the game has that label. TypeError If `infoset` is not an `Infoset` or a `str` + ValueError + If `infoset` is an empty `str` or all spaces """ if isinstance(infoset, Infoset): if infoset.game != self: raise MismatchError(f"{funcname}(): {argname} must be part of the same game") return infoset elif isinstance(infoset, str): + if not infoset.strip(): + raise ValueError(f"{funcname}(): {argname} cannot be an empty string or all spaces") try: return self.infosets[infoset] except IndexError: @@ -958,12 +978,16 @@ class Game: If `action` is a string and no action in the game has that label. TypeError If `action` is not an `Action` or a `str` + ValueError + If `action` is an empty `str` or all spaces """ if isinstance(action, Action): if action.infoset.game != self: raise MismatchError(f"{funcname}(): {argname} must be part of the same game") return action elif isinstance(action, str): + if not action.strip(): + raise ValueError(f"{funcname}(): {argname} cannot be an empty string or all spaces") try: return self.actions[action] except IndexError: diff --git a/src/pygambit/tests/test_game_resolve_functions.py b/src/pygambit/tests/test_game_resolve_functions.py new file mode 100644 index 000000000..503ab6cf9 --- /dev/null +++ b/src/pygambit/tests/test_game_resolve_functions.py @@ -0,0 +1,114 @@ +import unittest +import pygambit as gbt + + +class TestGambitResolveFunctions(unittest.TestCase): + def setUp(self): + self.game1 = gbt.Game.read_game( + "./test_games/sample_extensive_game.efg" + ) + + # has named outcomes + self.game2 = gbt.Game.read_game( + "./test_games/basic_extensive_game.efg" + ) + + # has named infosets + self.game3 = gbt.Game.read_game( + "./test_games/mixed_behavior_game.efg" + ) + + def tearDown(self): + del self.game1 + del self.game2 + del self.game3 + + def test_resolve_player_empty_strings(self): + """Test _resolve_player with the empty string or strings of all spaces""" + self.assertRaises(ValueError, self.game1._resolve_player, player='', funcname='test') + self.assertRaises(ValueError, self.game1._resolve_player, player=' ', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_player, player='', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_player, player=' ', funcname='test') + + def test_resolve_player_nonempty_strings(self): + """Test _resolve_player with non-empty strings, some that resolve some that don't""" + assert self.game1._resolve_player(player='Player 1', funcname='test') + assert self.game1._resolve_player(player='Player 2', funcname='test') + self.assertRaises(KeyError, self.game1._resolve_player, player='Player 3', + funcname='test') + + def test_resolve_outcome_empty_strings(self): + """Test _resolve_outcome with empty string or strings of all spaces""" + self.assertRaises(ValueError, self.game1._resolve_outcome, outcome='', funcname='test') + self.assertRaises(ValueError, self.game1._resolve_outcome, outcome=' ', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_outcome, outcome='', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_outcome, outcome=' ', funcname='test') + + def test_resolve_outcome_nonempty_strings(self): + """Test _resolve_outcome with non-empty strings, some that resolve some that don't""" + assert self.game2._resolve_outcome(outcome='Outcome 1', funcname='test') + assert self.game2._resolve_outcome(outcome='Outcome 2', funcname='test') + self.assertRaises(KeyError, self.game2._resolve_outcome, outcome='Outcome 5', + funcname='test') + + def test_resolve_strategy_empty_strings(self): + """Test _resolve_strategy with empty string or strings of all spaces""" + self.assertRaises(ValueError, self.game1._resolve_strategy, strategy='', funcname='test') + self.assertRaises(ValueError, self.game1._resolve_strategy, strategy=' ', + funcname='test') + self.assertRaises(ValueError, self.game2._resolve_strategy, strategy='', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_strategy, strategy=' ', + funcname='test') + + def test_resolve_strategy_nonempty_strings(self): + """Test _resolve_strategy with non-empty strings, some that resolve some that don't""" + assert self.game1._resolve_strategy(strategy='11', funcname='test') + assert self.game1._resolve_strategy(strategy='12', funcname='test') + self.assertRaises(KeyError, self.game1._resolve_strategy, strategy='13', funcname='test') + assert self.game2._resolve_strategy(strategy='1', funcname='test') + assert self.game2._resolve_strategy(strategy='2', funcname='test') + self.assertRaises(KeyError, self.game2._resolve_strategy, strategy='3', funcname='test') + + def test_resolve_node_empty_strings(self): + """Test _resolve_node with empty string or strings of all spaces""" + self.assertRaises(ValueError, self.game1._resolve_node, node='', funcname='test') + self.assertRaises(ValueError, self.game1._resolve_node, node=' ', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_node, node='', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_node, node=' ', funcname='test') + + def test_resolve_node_nonempty_strings(self): + """Test _resolve_node with non-empty strings, some that resolve some that don't""" + assert self.game1._resolve_node(node='1', funcname='test') + assert self.game1._resolve_node(node='2', funcname='test') + self.assertRaises(KeyError, self.game1._resolve_node, node='4', funcname='test') + self.assertRaises(KeyError, self.game2._resolve_node, node='1', funcname='test') + + def test_resolve_infoset_empty_strings(self): + """Test _resolve_infoset with empty string or strings of all spaces""" + self.assertRaises(ValueError, self.game1._resolve_infoset, infoset='', funcname='test') + self.assertRaises(ValueError, self.game1._resolve_infoset, infoset=' ', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_infoset, infoset='', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_infoset, infoset=' ', funcname='test') + + def test_resolve_infoset_nonempty_strings(self): + """Test _resolve_node with non-empty strings, some that resolve some that don't""" + assert self.game3._resolve_infoset(infoset='Infoset 1:1', funcname='test') + assert self.game3._resolve_infoset(infoset='Infoset 2:1', funcname='test') + self.assertRaises(KeyError, self.game3._resolve_infoset, infoset='Infoset 4:1', + funcname='test') + + def test_resolve_action_empty_strings(self): + """Test _resolve_action with empty string or strings of all spaces""" + self.assertRaises(ValueError, self.game1._resolve_action, action='', funcname='test') + self.assertRaises(ValueError, self.game1._resolve_action, action=' ', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_action, action='', funcname='test') + self.assertRaises(ValueError, self.game2._resolve_action, action=' ', funcname='test') + + def test_resolve_action_nonempty_strings(self): + """Test _resolve_action with non-empty strings, some that resolve some that don't""" + assert self.game1._resolve_action(action='1', funcname='test') + assert self.game1._resolve_action(action='2', funcname='test') + self.assertRaises(KeyError, self.game1._resolve_action, action='3', funcname='test') + assert self.game2._resolve_action(action='U1', funcname='test') + assert self.game2._resolve_action(action='D1', funcname='test') + self.assertRaises(KeyError, self.game2._resolve_action, action='D4', funcname='test') From 72920fe986f27b5dbb30becf5889126d34ac0f6b Mon Sep 17 00:00:00 2001 From: Ted Turocy Date: Fri, 6 Oct 2023 10:43:52 +0100 Subject: [PATCH 4/6] Update GitHub actions to use Python 3.12. --- .github/workflows/lint.yml | 2 +- .github/workflows/python.yml | 4 ++-- src/pygambit/games/meanstat.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 316f783df..f08577622 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11'] + python-version: ['3.12'] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 0fba4158a..e1b653172 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.11', '3.12-dev'] + python-version: ['3.8', '3.12'] steps: - uses: actions/checkout@v4 @@ -33,7 +33,7 @@ jobs: runs-on: windows-latest strategy: matrix: - python-version: ['3.11'] + python-version: ['3.12'] steps: - uses: actions/checkout@v4 diff --git a/src/pygambit/games/meanstat.py b/src/pygambit/games/meanstat.py index 1b6cc2fe2..2c22467ac 100644 --- a/src/pygambit/games/meanstat.py +++ b/src/pygambit/games/meanstat.py @@ -247,7 +247,7 @@ def __str__(self): def __repr__(self): return ( f"Mixed strategy profile on '{self.game.title}': " - f"{', '.join([ str(self[i]) for i in range(len(self)) ])}" + f"{', '.join([str(self[i]) for i in range(len(self))])}" ) def __rmul__(self, fac): From f6dec0c10a6f076362c6528272da9e311931a441 Mon Sep 17 00:00:00 2001 From: Ted Turocy Date: Fri, 6 Oct 2023 11:18:01 +0100 Subject: [PATCH 5/6] Migrate out-of-date games packages from pygambit to contrib. --- contrib/scripts/games/README.md | 6 ++++++ .../scripts}/games/contest.py | 0 .../scripts}/games/meanstat.py | 0 .../scripts}/games/public.py | 0 setup.py | 2 +- src/pygambit/games/__init__.py | 21 ------------------- 6 files changed, 7 insertions(+), 22 deletions(-) create mode 100644 contrib/scripts/games/README.md rename {src/pygambit => contrib/scripts}/games/contest.py (100%) rename {src/pygambit => contrib/scripts}/games/meanstat.py (100%) rename {src/pygambit => contrib/scripts}/games/public.py (100%) delete mode 100644 src/pygambit/games/__init__.py diff --git a/contrib/scripts/games/README.md b/contrib/scripts/games/README.md new file mode 100644 index 000000000..fd4f3f491 --- /dev/null +++ b/contrib/scripts/games/README.md @@ -0,0 +1,6 @@ +These are some sketches of Python codes that implement certain common symmetric games +in pure Python rather than using the underlying C++ representation. + +They are not currently maintained vis-a-vis the current pygambit implementation. +However, they are intended to be suggestive of how to provide codes to generate/implement +common games, with an aim to eventual integration as a pygambit feature. diff --git a/src/pygambit/games/contest.py b/contrib/scripts/games/contest.py similarity index 100% rename from src/pygambit/games/contest.py rename to contrib/scripts/games/contest.py diff --git a/src/pygambit/games/meanstat.py b/contrib/scripts/games/meanstat.py similarity index 100% rename from src/pygambit/games/meanstat.py rename to contrib/scripts/games/meanstat.py diff --git a/src/pygambit/games/public.py b/contrib/scripts/games/public.py similarity index 100% rename from src/pygambit/games/public.py rename to contrib/scripts/games/public.py diff --git a/setup.py b/setup.py index 5e5f27ac6..1b455eaca 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def readme(): ], libraries=[cppgambit, lrslib], package_dir={'': 'src'}, - packages=['pygambit', 'pygambit.games'], + packages=['pygambit'], ext_modules=Cython.Build.cythonize(libgambit, language_level="3str", compiler_directives={'binding': True}) diff --git a/src/pygambit/games/__init__.py b/src/pygambit/games/__init__.py deleted file mode 100644 index 505a29305..000000000 --- a/src/pygambit/games/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -# This file is part of Gambit -# Copyright (c) 1994-2023, The Gambit Project (http://www.gambit-project.org) -# -# FILE: src/python/gambit/games/__init__.py -# Module definition file for special games submodule of Gambit -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# From 2150a7315c8869f2c91bc19418369a89fe565f0f Mon Sep 17 00:00:00 2001 From: Ted Turocy Date: Fri, 6 Oct 2023 11:34:33 +0100 Subject: [PATCH 6/6] Migrate from using nose2 to pytest. With this we have moved from using nose2 to pytest for running the pygambit test suite. Many, though not all, of the tests have been migrated from unittest to pytest style. At present the remaining tests in unittest style are supported by pytest, but it will be desirable to rewrite those as well. --- .github/workflows/python.yml | 12 +- ChangeLog | 1 + Makefile.am | 12 +- src/pygambit/tests/test_actions.py | 192 ++++++------ src/pygambit/tests/test_behav.py | 4 +- src/pygambit/tests/test_extensive.py | 133 ++++++--- src/pygambit/tests/test_extensive_outcomes.py | 50 ---- src/pygambit/tests/test_game.py | 275 +++++------------- .../tests/test_game_resolve_functions.py | 1 + src/pygambit/tests/test_infosets.py | 131 ++++----- src/pygambit/tests/test_mixed.py | 232 +++++++-------- src/pygambit/tests/test_outcomes.py | 178 ++++++------ src/pygambit/tests/test_players.py | 261 ++++++++--------- src/pygambit/tests/test_strategic.py | 131 ++++----- src/pygambit/tests/test_strategies.py | 68 ----- src/pygambit/tests/test_userguide.py | 71 +++-- 16 files changed, 739 insertions(+), 1013 deletions(-) delete mode 100644 src/pygambit/tests/test_extensive_outcomes.py delete mode 100644 src/pygambit/tests/test_strategies.py diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e1b653172..6cc2cc00a 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -20,14 +20,14 @@ jobs: - name: Set up dependencies run: | python -m pip install --upgrade pip - pip install cython nose2 wheel lxml numpy scipy + pip install cython pytest wheel lxml numpy scipy - name: Build extension run: | python -m pip install -v . - - name: Run nosetests + - name: Run tests run: | cd src/pygambit/tests - nose2 + pytest windows: runs-on: windows-latest @@ -44,11 +44,11 @@ jobs: - name: Set up dependencies run: | python -m pip install --upgrade pip - pip install cython nose2 wheel lxml numpy scipy + pip install cython pytest wheel lxml numpy scipy - name: Build extension run: | python -m pip install -v . - - name: Run nosetests + - name: Run tests run: | cd src/pygambit/tests - nose2 + pytest diff --git a/ChangeLog b/ChangeLog index 1ef0e1d25..dabba37e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ ### Changed - Empty or all-whitespace strings cannot be used to access members of games in pygambit. - Remaining compatibility code for wxWidgets 2.x removed from graphical interface. +- Migrated to pytest for testing of pygambit. ## [16.1.0a3] - 2023-09-29 diff --git a/Makefile.am b/Makefile.am index 29a5fb821..6a3871756 100644 --- a/Makefile.am +++ b/Makefile.am @@ -256,10 +256,6 @@ EXTRA_DIST = \ src/pygambit/profiles.py \ src/pygambit/qre.py \ src/pygambit/supports.py \ - src/pygambit/games/__init__.py \ - src/pygambit/games/contest.py \ - src/pygambit/games/meanstat.py \ - src/pygambit/games/public.py \ src/pygambit/__init__.py \ src/pygambit/action.pxi \ src/pygambit/behav.pxi \ @@ -281,14 +277,16 @@ EXTRA_DIST = \ src/pygambit/tests/test_behav.py \ src/pygambit/tests/test_extensive.py \ src/pygambit/tests/test_game.py \ + src/pygambit/tests/test_game_resolve_functions.py \ src/pygambit/tests/test_infosets.py \ src/pygambit/tests/test_mixed.py \ + src/pygambit/tests/test_nash.py \ src/pygambit/tests/test_node.py \ src/pygambit/tests/test_outcomes.py \ src/pygambit/tests/test_players.py \ src/pygambit/tests/test_strategic.py \ src/pygambit/tests/test_stratprofiles.py \ - src/pygambit/tests/test_strategies.py \ + src/pygambit/tests/test_userguide.py \ src/pygambit/tests/test_games/basic_extensive_game.efg \ src/pygambit/tests/test_games/complicated_extensive_game.efg \ src/pygambit/tests/test_games/const_sum_game.nfg \ @@ -337,12 +335,12 @@ agg_SOURCES = \ src/games/gamebagg.h \ src/games/agg/gray.h \ src/games/agg/agg.cc \ - src/games/agg/agg.h \ + src/games/agg/agg.h \ src/games/agg/bagg.cc \ src/games/agg/bagg.h \ src/games/agg/proj_func.h \ src/games/agg/trie_map.h \ - src/games/agg/trie_map.imp + src/games/agg/trie_map.imp game_SOURCES = \ src/gambit.h \ diff --git a/src/pygambit/tests/test_actions.py b/src/pygambit/tests/test_actions.py index 74a464d4d..50070db3b 100644 --- a/src/pygambit/tests/test_actions.py +++ b/src/pygambit/tests/test_actions.py @@ -1,94 +1,98 @@ -import pygambit -import fractions -import unittest - - -class TestGambitActions(unittest.TestCase): - def setUp(self): - self.extensive_game = pygambit.Game.read_game( - "test_games/complicated_extensive_game.efg" - ) - - def tearDown(self): - del self.extensive_game - - def test_action_set_label(self): - """Test to ensure action labels work""" - assert self.extensive_game.root.infoset.actions[0].label == "RED" - self.extensive_game.root.infoset.actions[0].label = "action label" - assert ( - self.extensive_game.root.infoset.actions[0].label == - "action label" - ) - - def test_action_probability_chance(self): - """Test setting action probabilities at chance information sets""" - assert (self.extensive_game.root.infoset.actions[0].prob == 0.5) - - self.extensive_game.set_chance_probs(self.extensive_game.root.infoset, [0.75, 0.25]) - assert (self.extensive_game.root.infoset.actions[0].prob == 0.75) - assert (self.extensive_game.root.infoset.actions[1].prob == 0.25) - - self.extensive_game.set_chance_probs(self.extensive_game.root.infoset, ["1/17", "16/17"]) - assert (self.extensive_game.root.infoset.actions[0].prob == fractions.Fraction("1/17")) - assert (self.extensive_game.root.infoset.actions[1].prob == fractions.Fraction("16/17")) - - self.assertRaises( - ValueError, self.extensive_game.set_chance_probs, - self.extensive_game.root.infoset, [0.75, 0.10] - ) - self.assertRaises( - ValueError, self.extensive_game.set_chance_probs, - self.extensive_game.root.infoset, [1.1, -0.1] - ) - self.assertRaises( - IndexError, self.extensive_game.set_chance_probs, - self.extensive_game.root.infoset, [0.75, 0.10, 0.15] - ) - self.assertRaises( - ValueError, setattr, self.extensive_game.root.infoset.actions[0], "prob", "test" - ) - - self.extensive_game.set_chance_probs(self.extensive_game.root.infoset, [0.50, 0.50]) - - def test_action_probability_nonchance(self): - """Test attempts to set action probabilities at non-chance information sets.""" - self.assertRaises( - pygambit.UndefinedOperationError, self.extensive_game.set_chance_probs, - self.extensive_game.players[0].infosets[0], [0.75, 0.25] - ) - - def test_action_precedes(self): - """Test to ensure precedes is working""" - assert not self.extensive_game.actions[0].precedes( - self.extensive_game.root - ) - assert self.extensive_game.actions[0].precedes( - self.extensive_game.root.children[0].children[0] - ) - - def test_action_precedes_error(self): - """Test to ensure a TypeError is raised when precedes is called - without a node - """ - self.assertRaises( - TypeError, self.extensive_game.actions[0].precedes, 0 - ) - - def test_action_delete(self): - """Test to ensure it is possible to delete an action""" - assert len(self.extensive_game.actions) == 6 - self.extensive_game.delete_action(self.extensive_game.actions[0]) - assert len(self.extensive_game.actions) == 5 - - def test_action_delete_error(self): - """Test to ensure deleting the last action of an infoset - raises an error - """ - assert len(self.extensive_game.infosets[0].actions) == 2 - self.extensive_game.delete_action(self.extensive_game.actions[0]) - self.assertRaises( - pygambit.UndefinedOperationError, - self.extensive_game.delete_action, - self.extensive_game.actions[0] - ) +import pytest + +import pygambit as gbt + + +@pytest.mark.parametrize( + "game,label", + [(gbt.Game.read_game("test_games/complicated_extensive_game.efg"), "random label")] +) +def test_set_action_label(game: gbt.Game, label: str): + game.root.infoset.actions[0].label = label + assert game.root.infoset.actions[0].label == label + + +@pytest.mark.parametrize( + "game,inprobs,outprobs", + [(gbt.Game.read_game("test_games/complicated_extensive_game.efg"), + [0.75, 0.25], [0.75, 0.25]), + (gbt.Game.read_game("test_games/complicated_extensive_game.efg"), + ["16/17", "1/17"], [gbt.Rational("16/17"), gbt.Rational("1/17")])] +) +def test_set_chance_valid_probability(game: gbt.Game, inprobs: list, outprobs: list): + game.set_chance_probs(game.root.infoset, inprobs) + for (action, prob) in zip(game.root.infoset.actions, outprobs): + assert action.prob == prob + + +@pytest.mark.parametrize( + "game,inprobs", + [(gbt.Game.read_game("test_games/complicated_extensive_game.efg"), [0.75, -0.10]), + (gbt.Game.read_game("test_games/complicated_extensive_game.efg"), [0.75, 0.40]), + (gbt.Game.read_game("test_games/complicated_extensive_game.efg"), ["foo", "bar"])] +) +def test_set_chance_improper_probability(game: gbt.Game, inprobs: list): + with pytest.raises(ValueError): + game.set_chance_probs(game.root.infoset, inprobs) + + +@pytest.mark.parametrize( + "game,inprobs", + [(gbt.Game.read_game("test_games/complicated_extensive_game.efg"), [0.25, 0.75, 0.25]), + (gbt.Game.read_game("test_games/complicated_extensive_game.efg"), [1.00])] +) +def test_set_chance_bad_dimension(game: gbt.Game, inprobs: list): + with pytest.raises(IndexError): + game.set_chance_probs(game.root.infoset, inprobs) + + +@pytest.mark.parametrize( + "game", + [gbt.Game.read_game("test_games/complicated_extensive_game.efg")] +) +def test_set_chance_personal(game: gbt.Game): + with pytest.raises(gbt.UndefinedOperationError): + game.set_chance_probs(game.players[0].infosets[0], [0.75, 0.25]) + + +@pytest.mark.parametrize( + "game", + [gbt.Game.read_game("test_games/complicated_extensive_game.efg")] +) +def test_action_precedes(game: gbt.Game): + child = game.root.children[0] + assert game.root.infoset.actions[0].precedes(child) + assert not game.root.infoset.actions[1].precedes(child) + + +@pytest.mark.parametrize( + "game", + [gbt.Game.read_game("test_games/complicated_extensive_game.efg")] +) +def test_action_precedes_nonnode(game: gbt.Game): + with pytest.raises(TypeError): + game.root.infoset.actions[0].precedes(game) + + +@pytest.mark.parametrize( + "game", + [gbt.Game.read_game("test_games/complicated_extensive_game.efg")] +) +def test_action_delete_personal(game: gbt.Game): + node = game.players[0].infosets[0].members[0] + action_count = len(node.infoset.actions) + game.delete_action(node.infoset.actions[0]) + assert len(node.infoset.actions) == action_count - 1 + assert len(node.children) == action_count - 1 + + +@pytest.mark.parametrize( + "game", + [gbt.Game.read_game("test_games/complicated_extensive_game.efg")] +) +def test_action_delete_last(game: gbt.Game): + node = game.players[0].infosets[0].members[0] + while len(node.infoset.actions) > 1: + game.delete_action(node.infoset.actions[0]) + with pytest.raises(gbt.UndefinedOperationError): + game.delete_action(node.infoset.actions[0]) diff --git a/src/pygambit/tests/test_behav.py b/src/pygambit/tests/test_behav.py index 45170de63..faff4520e 100644 --- a/src/pygambit/tests/test_behav.py +++ b/src/pygambit/tests/test_behav.py @@ -1,7 +1,9 @@ +import unittest + import pygambit as gbt -class TestGambitMixedBehavGame: +class TestGambitMixedBehavGame(unittest.TestCase): def setUp(self): self.game = gbt.Game.read_game( "test_games/mixed_behavior_game.efg" diff --git a/src/pygambit/tests/test_extensive.py b/src/pygambit/tests/test_extensive.py index eb5b6572b..714fd6e63 100644 --- a/src/pygambit/tests/test_extensive.py +++ b/src/pygambit/tests/test_extensive.py @@ -1,33 +1,100 @@ -import pygambit - - -class TestGambitExtensiveGame: - def setUp(self): - self.game = pygambit.Game.new_tree() - - def tearDown(self): - del self.game - - def test_initial_player_count(self): - "Test to ensure 0 initial players" - assert len(self.game.players) == 0 - - def test_initial_game_title(self): - "Test to ensure correct basic title" - assert str(self.game) == "" - - def test_game_title_assignment(self): - "Test to check title assignment" - self.game.title = "A simple poker example" - assert str(self.game) == "" - - def test_game_add_players(self): - "Test to add player" - self.game.title = "A simple poker example" - p = self.game.add_player("Alice") - assert len(self.game.players) == 1 - assert ( - str(self.game.players[0]) == - "" - ) - assert str(p.label) == "Alice" +import typing + +import pytest + +import pygambit as gbt + + +@pytest.mark.parametrize( + "players,title", + [([], "New game"), + (["Alice", "Bob"], "A poker game")] +) +def test_new_tree(players: list, title: typing.Optional[str]): + game = gbt.Game.new_tree(players=players, title=title) + assert len(game.players) == len(players) + for (player, label) in zip(game.players, players): + assert player.label == label + assert game.title == title + + +@pytest.mark.parametrize( + "title", ["My game's new title"] +) +def test_game_title(title: str): + game = gbt.Game.new_tree() + game.title = title + assert game.title == title + + +@pytest.mark.parametrize( + "comment", ["This is a comment describing the game in more detail"] +) +def test_game_comment(comment: str): + game = gbt.Game.new_tree() + game.comment = comment + assert game.comment == comment + + +@pytest.mark.parametrize( + "players", + [["Alice"], ["Oscar", "Felix"]] +) +def test_game_add_players_label(players: list): + game = gbt.Game.new_tree() + for player in players: + game.add_player(player) + for (player, label) in zip(game.players, players): + assert player.label == label + + +def test_game_add_players_nolabel(): + game = gbt.Game.new_tree() + game.add_player() + + +def test_game_num_nodes(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + assert len(game.nodes()) == 15 + + +def test_game_is_perfect_recall(): + game = gbt.Game.read_game("test_games/perfect_recall.efg") + assert game.is_perfect_recall + + +def test_game_is_not_perfect_recall(): + game = gbt.Game.read_game("test_games/not_perfect_recall.efg") + assert not game.is_perfect_recall + + +def test_getting_payoff_by_label_string(): + game = gbt.Game.read_game("test_games/sample_extensive_game.efg") + assert game[[0, 0]]['Player 1'] == 2 + assert game[[0, 1]]['Player 1'] == 2 + assert game[[1, 0]]['Player 1'] == 4 + assert game[[1, 1]]['Player 1'] == 6 + assert game[[0, 0]]['Player 2'] == 3 + assert game[[0, 1]]['Player 2'] == 3 + assert game[[1, 0]]['Player 2'] == 5 + assert game[[1, 1]]['Player 2'] == 7 + + +def test_getting_payoff_by_player(): + game = gbt.Game.read_game("test_games/sample_extensive_game.efg") + player1 = game.players[0] + player2 = game.players[1] + assert game[[0, 0]][player1] == 2 + assert game[[0, 1]][player1] == 2 + assert game[[1, 0]][player1] == 4 + assert game[[1, 1]][player1] == 6 + assert game[[0, 0]][player2] == 3 + assert game[[0, 1]][player2] == 3 + assert game[[1, 0]][player2] == 5 + assert game[[1, 1]][player2] == 7 + + +def test_outcome_index_exception_label(): + game = gbt.Game.read_game("test_games/sample_extensive_game.efg") + with pytest.raises(KeyError): + _ = game[[0, 0]]["Not a player"] diff --git a/src/pygambit/tests/test_extensive_outcomes.py b/src/pygambit/tests/test_extensive_outcomes.py deleted file mode 100644 index ee4c724ec..000000000 --- a/src/pygambit/tests/test_extensive_outcomes.py +++ /dev/null @@ -1,50 +0,0 @@ -import unittest - -import pygambit - - -class TestGambitOutcomes(unittest.TestCase): - def setUp(self): - self.game = pygambit.Game.read_game( - "./test_games/sample_extensive_game.efg" - ) - - def tearDown(self): - del self.game - - def test_player1_outcomes(self): - assert self.game[[0, 0]]["Player 1"] == 2 - assert self.game[[0, 1]]["Player 1"] == 2 - assert self.game[[1, 0]]["Player 1"] == 4 - assert self.game[[1, 1]]["Player 1"] == 6 - - def test_player2_outcomes(self): - assert self.game[[0, 0]]["Player 2"] == 3 - assert self.game[[0, 1]]["Player 2"] == 3 - assert self.game[[1, 0]]["Player 2"] == 5 - assert self.game[[1, 1]]["Player 2"] == 7 - - def test_getting_payoff_by_label_string(self): - assert self.game[[0, 0]]['Player 1'] == 2 - assert self.game[[0, 1]]['Player 1'] == 2 - assert self.game[[1, 0]]['Player 1'] == 4 - assert self.game[[1, 1]]['Player 1'] == 6 - assert self.game[[0, 0]]['Player 2'] == 3 - assert self.game[[0, 1]]['Player 2'] == 3 - assert self.game[[1, 0]]['Player 2'] == 5 - assert self.game[[1, 1]]['Player 2'] == 7 - - def test_getting_payoff_by_player(self): - player1 = self.game.players[0] - player2 = self.game.players[1] - assert self.game[[0, 0]][player1] == 2 - assert self.game[[0, 1]][player1] == 2 - assert self.game[[1, 0]][player1] == 4 - assert self.game[[1, 1]][player1] == 6 - assert self.game[[0, 0]][player2] == 3 - assert self.game[[0, 1]][player2] == 3 - assert self.game[[1, 0]][player2] == 5 - assert self.game[[1, 1]][player2] == 7 - - def test_outcome_index_exception_int(self): - self.assertRaises(KeyError, self.game[[0, 0]].__getitem__, "Not a player") diff --git a/src/pygambit/tests/test_game.py b/src/pygambit/tests/test_game.py index 58d103c10..e89521d41 100644 --- a/src/pygambit/tests/test_game.py +++ b/src/pygambit/tests/test_game.py @@ -1,206 +1,79 @@ -import fractions -import unittest - +import pytest import numpy as np import pygambit as gbt -class TestGambitGame(unittest.TestCase): - def setUp(self): - self.game = gbt.Game.new_table([2, 2]) - self.extensive_game = gbt.Game.read_game("test_games/basic_extensive_game.efg") - - def tearDown(self): - del self.game - del self.extensive_game - - def test_from_arrays(self): - """Test creating a two-player game from numpy arrays.""" - m = np.array([[8, 2], [10, 5]]) - g = gbt.Game.from_arrays(m, m.transpose()) - assert len(g.players) == 2 - assert len(g.players[0].strategies) == 2 - assert len(g.players[1].strategies) == 2 - - def test_game_get_outcome_with_ints(self): - "To test getting the first outcome" - assert self.game.outcomes[0] == self.game[0, 0] - - def test_game_get_outcome_with_incorrect_tuple_size(self): - "To test getting an outcome with a bad tuple size" - self.assertRaises(KeyError, self.game.__getitem__, (0, 0, 0)) - - def test_game_get_outcome_with_bad_ints(self): - "To test getting an index error with ints" - self.assertRaises(IndexError, self.game.__getitem__, (0, 3)) - - def test_game_get_outcome_with_strings(self): - "To test getting the first outcome with strategy labels" - self.game.players[0].strategies[0].label = "defect" - self.game.players[1].strategies[0].label = "cooperate" - assert self.game.outcomes[0] == self.game["defect", "cooperate"] - - def test_game_get_outcome_with_bad_strings(self): - "To test getting the first outcome with strategy labels" - self.game.players[0].strategies[0].label = "defect" - self.game.players[1].strategies[0].label = "cooperate" - self.assertRaises(IndexError, - self.game.__getitem__, ("defect", "defect")) - - def test_game_get_outcome_with_strategies(self): - "To test getting the first outcome with strategies" - assert ( - self.game.outcomes[0] == - self.game[self.game.players[0].strategies[0], - self.game.players[1].strategies[0]] - ) - - def test_game_get_outcome_with_bad_strategies(self): - "To test getting the first outcome with incorrect strategies" - self.assertRaises( - IndexError, self.game.__getitem__, - (self.game.players[0].strategies[0], - self.game.players[0].strategies[0]) - ) - - def test_game_outcomes_non_tuple(self): - "To test when attempting to find outcome with a non-tuple-like object" - self.assertRaises(TypeError, self.game.__getitem__, 42) - - def test_game_outcomes_type_exception(self): - "To test when attempting to find outcome with invalid input" - self.assertRaises(TypeError, self.game.__getitem__, (1.23, 1)) - - def test_game_is_const_sum(self): - "To test checking if the game is constant sum" - game = gbt.Game.read_game("test_games/const_sum_game.nfg") - assert game.is_const_sum - - def test_game_is_not_const_sum(self): - "To test checking if the game is not constant sum" - game = gbt.Game.read_game("test_games/non_const_sum_game.nfg") - assert not game.is_const_sum - - def test_game_get_min_payoff(self): - "To test getting the minimum payoff of the game" - game = gbt.Game.read_game("test_games/payoff_game.nfg") - assert game.min_payoff == fractions.Fraction(1, 1) - - def test_game_get_max_payoff(self): - "To test getting the maximum payoff of the game" - game = gbt.Game.read_game("test_games/payoff_game.nfg") - assert game.max_payoff == fractions.Fraction(10, 1) - - def test_game_is_perfect_recall(self): - "To test checking if the game is of perfect recall" - game = gbt.Game.read_game("test_games/perfect_recall.efg") - assert game.is_perfect_recall - - def test_game_is_not_perfect_recall(self): - "To test checking if the game is not of perfect recall" - game = gbt.Game.read_game("test_games/not_perfect_recall.efg") - assert not game.is_perfect_recall - - def test_game_behav_profile_error(self): - """To test raising an error when trying to create a - MixedBehavProfile from a game without a tree representation - """ - self.assertRaises( - gbt.UndefinedOperationError, self.game.mixed_behavior_profile, True - ) - - def test_game_title(self): - assert self.game.title == "Untitled strategic game" - self.game.title = "Test Title" - assert self.game.title == "Test Title" - - def test_game_comment(self): - assert self.game.comment == "" - self.game.comment = "Test Comment" - assert self.game.comment == "Test Comment" - - def test_game_actions(self): - assert ( - self.extensive_game.actions[0] == - self.extensive_game.players[0].infosets[0].actions[0] - ) - assert ( - self.extensive_game.actions[1] == - self.extensive_game.players[0].infosets[0].actions[1] - ) - assert ( - self.extensive_game.actions[2] == - self.extensive_game.players[1].infosets[0].actions[0] - ) - assert ( - self.extensive_game.actions[3] == - self.extensive_game.players[1].infosets[0].actions[1] - ) - assert ( - self.extensive_game.actions[4] == - self.extensive_game.players[2].infosets[0].actions[0] - ) - assert ( - self.extensive_game.actions[5] == - self.extensive_game.players[2].infosets[0].actions[1] - ) - - def test_strategic_game_actions_error(self): - """Test to ensure an error is raised when trying to access actions - of a game without a tree representation - """ - self.assertRaises(gbt.UndefinedOperationError, lambda: self.game.actions) - - def test_game_infosets(self): - assert ( - self.extensive_game.infosets[0] == - self.extensive_game.players[0].infosets[0] - ) - assert ( - self.extensive_game.infosets[1] == - self.extensive_game.players[1].infosets[0] - ) - assert ( - self.extensive_game.infosets[2] == - self.extensive_game.players[2].infosets[0] - ) - - def test_strategic_game_infosets_error(self): - """Test to ensure an error is raised when trying to access infosets - of a game without a tree representation - """ - self.assertRaises(gbt.UndefinedOperationError, lambda: self.game.infosets) - - def test_game_strategies(self): - assert self.game.strategies[0] == self.game.players[0].strategies[0] - assert self.game.strategies[1] == self.game.players[0].strategies[1] - assert self.game.strategies[2] == self.game.players[1].strategies[0] - assert self.game.strategies[3] == self.game.players[1].strategies[1] - - def test_game_get_root(self): - "Test retrieving the root node of a game" - root = self.extensive_game.root - root.label = "Test" - assert self.extensive_game.root.label == root.label - - def test_game_get_root_error(self): - """Test to ensure an error is raised when trying to get the root - node of a game without a tree representation - """ - self.assertRaises(gbt.UndefinedOperationError, lambda: self.game.root) - - def test_game_num_nodes(self): - "Test retrieving the number of nodes of a game" - assert len(self.extensive_game.nodes()) == 15 - assert len(self.game.nodes()) == 0 - - def test_game_dereference_invalid(self): - "Test referencing an invalid game member object" - def foo(): - g = gbt.Game.new_tree() - g.add_player("One") - s = g.players[0].strategies[0] - g.append_move(g.root, g.players[0], ["a", "b"]) - s.number - self.assertRaises(RuntimeError, foo) +def test_from_arrays(): + m = np.array([[8, 2], [10, 5]]) + game = gbt.Game.from_arrays(m, m.transpose()) + assert len(game.players) == 2 + assert len(game.players[0].strategies) == 2 + assert len(game.players[1].strategies) == 2 + + +def test_game_get_outcome_by_index(): + game = gbt.Game.new_table([2, 2]) + assert game[0, 0] == game.outcomes[0] + + +def test_game_get_outcome_by_label(): + game = gbt.Game.new_table([2, 2]) + game.players[0].strategies[0].label = "defect" + game.players[1].strategies[0].label = "cooperate" + assert game["defect", "cooperate"] == game.outcomes[0] + + +def test_game_get_outcome_invalid_tuple_size(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(KeyError): + _ = game[0, 0, 0] + + +def test_game_outcomes_non_tuple(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(TypeError): + _ = game[42] + + +def test_game_outcomes_type_exception(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(TypeError): + _ = game[1.23, 1] + + +def test_game_get_outcome_index_out_of_range(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(IndexError): + _ = game[0, 3] + + +def test_game_get_outcome_unmatched_label(): + game = gbt.Game.new_table([2, 2]) + game.players[0].strategies[0].label = "defect" + game.players[1].strategies[0].label = "cooperate" + with pytest.raises(IndexError): + _ = game["defect", "defect"] + + +def test_game_get_outcome_with_strategies(): + game = gbt.Game.new_table([2, 2]) + assert ( + game[game.players[0].strategies[0], game.players[1].strategies[0]] == + game.outcomes[0] + ) + + +def test_game_get_outcome_with_bad_strategies(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(IndexError): + _ = game[game.players[0].strategies[0], game.players[0].strategies[0]] + + +def test_game_dereference_invalid(): + game = gbt.Game.new_tree() + game.add_player("One") + strategy = game.players[0].strategies[0] + game.append_move(game.root, game.players[0], ["a", "b"]) + with pytest.raises(RuntimeError): + _ = strategy.label diff --git a/src/pygambit/tests/test_game_resolve_functions.py b/src/pygambit/tests/test_game_resolve_functions.py index 503ab6cf9..3d51dc312 100644 --- a/src/pygambit/tests/test_game_resolve_functions.py +++ b/src/pygambit/tests/test_game_resolve_functions.py @@ -1,4 +1,5 @@ import unittest + import pygambit as gbt diff --git a/src/pygambit/tests/test_infosets.py b/src/pygambit/tests/test_infosets.py index 0048388cb..69df5f391 100644 --- a/src/pygambit/tests/test_infosets.py +++ b/src/pygambit/tests/test_infosets.py @@ -1,78 +1,53 @@ -import unittest - -import pygambit - - -class TestGambitInfosets(unittest.TestCase): - def setUp(self): - self.extensive_game = pygambit.Game.read_game( - "test_games/basic_extensive_game.efg" - ) - self.complicated_game = pygambit.Game.read_game( - "test_games/complicated_extensive_game.efg" - ) - - def tearDown(self): - del self.extensive_game - del self.complicated_game - - def test_infoset_set_label(self): - "Test to ensure infoset labels work" - assert self.extensive_game.players[0].infosets[0].label == "" - self.extensive_game.players[0].infosets[0].label = "infoset 1" - assert self.extensive_game.players[0].infosets[0].label == "infoset 1" - - def test_infoset_player_retrieval(self): - "Test to ensure infoset returns correct player" - assert ( - self.extensive_game.players[0] == - self.extensive_game.players[0].infosets[0].player - ) - - def test_infoset_player_change(self): - "Test to ensure infoset player transfer works" - self.extensive_game.set_player(self.extensive_game.root.infoset, - self.extensive_game.players[1]) - assert ( - self.extensive_game.root.infoset.player == - self.extensive_game.players[1] - ) - - def test_infoset_player_mismatch(self): - """Test to ensure exception raised on setting player - from different game. - """ - def foo(): - g2 = pygambit.Game.new_tree() - p = g2.add_player() - self.extensive_game.set_player(self.extensive_game.root.infoset, p) - self.assertRaises(pygambit.MismatchError, foo) - - def test_infoset_node_precedes(self): - "Test to check if the infoset preceding check works" - assert not self.extensive_game.players[0].infosets[0].precedes( - self.extensive_game.root - ) - assert ( - self.extensive_game.players[1].infosets[0].precedes( - self.extensive_game.root.children[0] - ) - ) - - def test_infoset_add_action(self): - assert len(self.extensive_game.infosets[0].actions) == 2 - self.extensive_game.add_action(self.extensive_game.infosets[0]) - assert len(self.extensive_game.infosets[0].actions) == 3 - self.extensive_game.add_action( - self.extensive_game.infosets[0], - self.extensive_game.actions[2] - ) - assert len(self.extensive_game.infosets[0].actions) == 4 - - def test_infoset_add_action_error(self): - self.assertRaises( - pygambit.MismatchError, - self.extensive_game.add_action, - self.extensive_game.infosets[0], - self.extensive_game.actions[3] - ) +import pytest + +import pygambit as gbt + + +def test_infoset_set_label(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + game.players[0].infosets[0].label = "infoset 1" + assert game.players[0].infosets[0].label == "infoset 1" + + +def test_infoset_player_retrieval(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + assert game.players[0] == game.players[0].infosets[0].player + + +def test_infoset_node_precedes(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + assert not game.players[0].infosets[0].precedes(game.root) + assert game.players[1].infosets[0].precedes(game.root.children[0]) + + +def test_infoset_set_player(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + game.set_player(game.root.infoset, game.players[1]) + assert game.root.infoset.player == game.players[1] + + +def test_infoset_set_player_mismatch(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + game2 = gbt.Game.new_tree(["Frank"]) + with pytest.raises(gbt.MismatchError): + game.set_player(game.root.infoset, game2.players[0]) + + +def test_infoset_add_action_end(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + actions = list(game.players[0].infosets[0].actions) + game.add_action(game.players[0].infosets[0]) + assert list(game.players[0].infosets[0].actions)[:-1] == actions + + +def test_infoset_add_action_before(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + actions = list(game.players[0].infosets[0].actions) + game.add_action(game.players[0].infosets[0], actions[0]) + assert list(game.players[0].infosets[0].actions)[1:] == actions + + +def test_infoset_add_action_error(): + game = gbt.Game.read_game("test_games/basic_extensive_game.efg") + with pytest.raises(gbt.MismatchError): + game.add_action(game.players[0].infosets[0], game.players[1].infosets[0].actions[0]) diff --git a/src/pygambit/tests/test_mixed.py b/src/pygambit/tests/test_mixed.py index 91e3e6b74..b628b0189 100644 --- a/src/pygambit/tests/test_mixed.py +++ b/src/pygambit/tests/test_mixed.py @@ -1,138 +1,102 @@ -import fractions -import unittest +import pytest import pygambit +import pygambit as gbt -class TestGambitMixedStrategyGame(unittest.TestCase): - def setUp(self): - self.game = pygambit.Game.new_table([2, 2]) - self.game.players[0].label = "joe" - self.game.players["joe"].strategies[0].label = "cooperate" - self.game.players[1].label = "dan" - self.game.players["dan"].strategies[1].label = "defect" - - self.profile_double = self.game.mixed_strategy_profile() - self.profile_rational = self.game.mixed_strategy_profile(rational=True) - - self.tree_game = ( - pygambit.Game.read_game("test_games/mixed_behavior_game.efg") - ) - - self.tree_profile_double = self.tree_game.mixed_strategy_profile() - self.tree_profile_rational = ( - self.tree_game.mixed_strategy_profile(rational=True) - ) - - def tearDown(self): - del self.game - del self.tree_game - del self.profile_double - del self.profile_rational - del self.tree_profile_double - del self.tree_profile_rational - - def test_payoffs(self): - "Test to ensure that payoffs are returned correctly" - assert self.profile_double.payoff(self.game.players[0]) == 0.0 - assert self.profile_rational.payoff(self.game.players[0]) == 0.0 - - def test_finding_payoffs_by_string(self): - "Test to find payoffs by string values" - assert self.profile_double.payoff("joe") == 0.0 - assert self.profile_rational.payoff("joe") == 0.0 - - def test_strategy_value(self): - "Test to retrieve strategy value based on given strategy" - assert ( - self.profile_double.strategy_value( - self.game.players[0].strategies[1] - ) == 0.0 - ) - assert ( - self.profile_rational.strategy_value( - self.game.players[0].strategies[1] - ) == 0.0 - ) - - def test_strategy_value_by_string(self): - "Test expected payoff based on given strategy" - assert self.profile_double.strategy_value("defect") == 0.0 - assert self.profile_rational.strategy_value("defect") == 0.0 - - def test_get_probabilities_strategy(self): - """Test retrieving probabilities""" - assert self.profile_double[self.game.strategies[0]] == 0.5 - assert self.profile_rational[self.game.strategies[0]] == fractions.Fraction("1/2") - - def test_get_probabilities_player(self): - "Test retrieving probabilities" - assert self.profile_double[self.game.players[0]] == [0.5, 0.5] - assert self.profile_rational[self.game.players[0]] == ( - [fractions.Fraction("1/2"), fractions.Fraction("1/2")] - ) - - assert self.profile_double[self.game.players[0].strategies[0]] == 0.5 - assert self.profile_rational[self.game.players[0].strategies[0]] == ( - fractions.Fraction("1/2") - ) - - assert self.profile_double[self.game.players[0]]["cooperate"] == 0.5 - assert self.profile_rational[self.game.players[0]]["cooperate"] == ( - fractions.Fraction("1/2") - ) - - def test_set_probabilities(self): - """Test setting probabilities""" - self.profile_double[self.game.strategies[0]] = 0.72 - assert self.profile_double[self.game.strategies[0]] == 0.72 - self.profile_rational[self.game.strategies[0]] = fractions.Fraction("2/9") - assert self.profile_rational[self.game.strategies[0]] == fractions.Fraction("2/9") - - self.profile_double[self.game.players[0].strategies[1]] = 0.72 - assert self.profile_double[self.game.players[0].strategies[1]] == 0.72 - - self.profile_double[self.game.players[0]] = [0.72, 0.28] - assert self.profile_double[self.game.players[0]] == [0.72, 0.28] - - self.profile_rational[self.game.players[0].strategies[1]] = fractions.Fraction("2/9") - assert ( - self.profile_rational[self.game.players[0].strategies[1]] == - fractions.Fraction("2/9") - ) - self.profile_rational[self.game.players[0]] = ( - [fractions.Fraction("2/9"), fractions.Fraction("7/9")] - ) - assert ( - self.profile_rational[self.game.players[0]] == - [fractions.Fraction("2/9"), fractions.Fraction("7/9")] - ) - - def test_liap_values(self): - "Test retrieving Lyapunov values" - assert self.profile_double.liap_value() == 0.0 - assert self.profile_rational.liap_value() == fractions.Fraction("0") - - def test_as_behav_tree(self): - "Test converting the profile to a behavior one" - behav_double = self.tree_profile_double.as_behavior() - behav_rational = self.tree_profile_rational.as_behavior() - assert ( - [behav_double[action] for action in self.tree_game.actions] == - [self.tree_profile_double[strategy] for strategy in self.tree_game.strategies] - ) - assert ( - [behav_rational[action] for action in self.tree_game.actions] == - [self.tree_profile_rational[strategy] for strategy in self.tree_game.strategies] - ) - - def test_as_behav_error(self): - """Test raising an error when trying to convert a profile - from a strategic game - """ - self.assertRaises( - pygambit.UndefinedOperationError, self.profile_double.as_behavior - ) - self.assertRaises( - pygambit.UndefinedOperationError, self.profile_rational.as_behavior - ) +def _create_strategic_game() -> gbt.Game: + game = pygambit.Game.new_table([2, 2]) + game.players[0].label = "Joe" + game.players["Joe"].strategies[0].label = "cooperate" + game.players[1].label = "Dan" + game.players["Dan"].strategies[1].label = "defect" + return game + + +def test_get_probabilities_strategy(): + game = _create_strategic_game() + strategy = game.players[0].strategies[0] + assert game.mixed_strategy_profile(rational=False)[strategy] == 0.5 + assert game.mixed_strategy_profile(rational=True)[strategy] == gbt.Rational("1/2") + + +def test_get_probabilities_player(): + game = _create_strategic_game() + assert game.mixed_strategy_profile(rational=False)[game.players[0]] == [0.5, 0.5] + assert ( + game.mixed_strategy_profile(rational=True)[game.players[0]] == + [gbt.Rational("1/2"), gbt.Rational("1/2")] + ) + + +def test_set_probability_strategy(): + game = _create_strategic_game() + profile = game.mixed_strategy_profile(rational=False) + profile[game.players[0].strategies[0]] = 0.72 + assert profile[game.players[0].strategies[0]] == 0.72 + + profile = game.mixed_strategy_profile(rational=True) + profile[game.players[0].strategies[0]] = gbt.Rational("2/9") + assert profile[game.players[0].strategies[0]] == gbt.Rational("2/9") + + +def test_set_probability_player(): + game = _create_strategic_game() + profile = game.mixed_strategy_profile(rational=False) + profile[game.players[0]] = [0.72, 0.28] + assert profile[game.players[0]] == [0.72, 0.28] + + profile = game.mixed_strategy_profile(rational=True) + profile[game.players[0]] = [gbt.Rational("7/9"), gbt.Rational("2/9")] + assert profile[game.players[0]] == [gbt.Rational("7/9"), gbt.Rational("2/9")] + + +def test_payoffs(): + game = _create_strategic_game() + assert game.mixed_strategy_profile(rational=False).payoff(game.players[0]) == 0 + assert game.mixed_strategy_profile(rational=True).payoff(game.players[0]) == 0 + + +def test_payoffs_by_label(): + game = _create_strategic_game() + assert game.mixed_strategy_profile(rational=False).payoff("Joe") == 0 + assert game.mixed_strategy_profile(rational=True).payoff("Joe") == 0 + + +def test_strategy_value(): + game = _create_strategic_game() + strategy = game.players[0].strategies[1] + assert game.mixed_strategy_profile(rational=False).strategy_value(strategy) == 0 + assert game.mixed_strategy_profile(rational=True).strategy_value(strategy) == 0 + + +def test_strategy_value_by_label(): + game = _create_strategic_game() + assert game.mixed_strategy_profile(rational=False).strategy_value("defect") == 0 + assert game.mixed_strategy_profile(rational=True).strategy_value("defect") == 0 + + +def test_liap_value(): + game = _create_strategic_game() + assert game.mixed_strategy_profile(rational=False).liap_value() == 0 + assert game.mixed_strategy_profile(rational=True).liap_value() == 0 + + +def test_as_behavior_roundtrip(): + game = gbt.Game.read_game("test_games/mixed_behavior_game.efg") + assert ( + game.mixed_strategy_profile(rational=False).as_behavior().as_strategy() == + game.mixed_strategy_profile(rational=False) + ) + assert ( + game.mixed_strategy_profile(rational=True).as_behavior().as_strategy() == + game.mixed_strategy_profile(rational=True) + ) + + +def test_as_behavior_error(): + game = _create_strategic_game() + with pytest.raises(gbt.UndefinedOperationError): + _ = game.mixed_strategy_profile(rational=False).as_behavior() + with pytest.raises(gbt.UndefinedOperationError): + _ = game.mixed_strategy_profile(rational=True).as_behavior() diff --git a/src/pygambit/tests/test_outcomes.py b/src/pygambit/tests/test_outcomes.py index 00cbdc578..68a07d9b8 100644 --- a/src/pygambit/tests/test_outcomes.py +++ b/src/pygambit/tests/test_outcomes.py @@ -1,84 +1,94 @@ -import unittest -import warnings - -import pygambit - - -class TestGambitOutcomes(unittest.TestCase): - def setUp(self): - self.game = pygambit.Game.new_table([2, 2]) - self.game.players[0].label = "joe" - self.game.players[1].label = "dan" - self.game.outcomes[0]["joe"] = 1 - self.game.outcomes[0]["dan"] = 2 - self.game.outcomes[1]["joe"] = 3 - self.game.outcomes[1]["dan"] = 4 - - def tearDown(self): - del self.game - - def test_outcomes_number_index(self): - "Test to verify outcome labels and indexing" - self.game.outcomes[0].label = "trial" - self.game.outcomes[1].label = "trial 2" - - assert self.game.outcomes[0].label == "trial" - assert self.game.outcomes[1].label == "trial 2" - - def test_game_add_outcomes(self): - "Test to verify outcome indexing" - self.game.outcomes[0].label = "trial" - assert self.game.outcomes[0].label == "trial" - - def test_game_add_duplicate_outcome_names(self): - "Test to verify duplicate outcome names" - self.game.outcomes[0].label = "trial" - assert self.game.outcomes[0].label == "trial" - with warnings.catch_warnings(record=True) as w: - self.game.outcomes[1].label = "trial" - assert ( - str(w[0].message) == - "Another outcome with an identical label exists" - ) - - def test_game_outcomes_index_by_string(self): - "Test to find an outcome by providing a string" - self.game.outcomes[0].label = "trial" - o1 = self.game.outcomes["trial"] - assert o1.label == "trial" - - def test_game_outcome_index_exception_int(self): - "Test to verify when an index is out of range" - self.assertRaises(IndexError, self.game.outcomes.__getitem__, 9) - - def test_game_outcome_index_exception_string(self): - """Test to verify when an outcome label is not in the list of - game outcomes - """ - self.assertRaises(IndexError, self.game.outcomes.__getitem__, "None") - - def test_game_ouctome_index_exception_invalid_input(self): - """Test to verify when attempting to retrieve an outcome - with invalid input - """ - self.assertRaises(TypeError, self.game.outcomes.__getitem__, 1.3) - - def test_getting_payoff_by_label_string(self): - assert self.game.outcomes[0]['joe'] == 1 - assert self.game.outcomes[0]['dan'] == 2 - assert self.game.outcomes[1]['joe'] == 3 - assert self.game.outcomes[1]['dan'] == 4 - - def test_getting_payoff_by_player(self): - player1 = self.game.players[0] - player2 = self.game.players[1] - assert self.game.outcomes[0][player1] == 1 - assert self.game.outcomes[0][player2] == 2 - assert self.game.outcomes[1][player1] == 3 - assert self.game.outcomes[1][player2] == 4 - - def test_outcome_delete(self): - "Test to ensure it is possible to delete an outcome from the game" - assert len(self.game.outcomes) == 4 - self.game.delete_outcome(self.game.outcomes[0]) - assert len(self.game.outcomes) == 3 +import pytest + +import pygambit as gbt + + +@pytest.mark.parametrize( + "game", [gbt.Game.new_table([2, 2]), gbt.Game.new_tree()] +) +def test_outcome_add(game: gbt.Game): + outcome_count = len(game.outcomes) + game.add_outcome() + assert len(game.outcomes) == outcome_count + 1 + + +@pytest.mark.parametrize( + "game", [gbt.Game.new_table([2, 2])] +) +def test_outcome_delete(game: gbt.Game): + outcome_count = len(game.outcomes) + game.delete_outcome(game.outcomes[0]) + assert len(game.outcomes) == outcome_count - 1 + + +@pytest.mark.parametrize( + "game,label", + [(gbt.Game.new_table([2, 2]), "outcome label")] +) +def test_outcome_label(game: gbt.Game, label: str): + game.outcomes[0].label = label + assert game.outcomes[0].label == label + + +@pytest.mark.parametrize( + "game,label", + [(gbt.Game.new_table([2, 2]), "outcome label")] +) +def test_outcome_index_label(game: gbt.Game, label: str): + game.outcomes[0].label = label + assert game.outcomes[0] == game.outcomes[label] + assert game.outcomes[label].label == label + + +@pytest.mark.parametrize( + "game", [gbt.Game.new_table([2, 2])] +) +def test_outcome_index_int_range(game: gbt.Game): + with pytest.raises(IndexError): + _ = game.outcomes[2 * len(game.outcomes)] + + +@pytest.mark.parametrize( + "game", [gbt.Game.new_table([2, 2])] +) +def test_outcome_index_label_range(game: gbt.Game): + with pytest.raises(IndexError): + _ = game.outcomes["not an outcome"] + + +@pytest.mark.parametrize( + "game", [gbt.Game.new_table([2, 2])] +) +def test_outcome_index_invalid_type(game: gbt.Game): + with pytest.raises(TypeError): + _ = game.outcomes[1.3] + + +def test_outcome_payoff_by_player_label(): + game = gbt.Game.new_table([2, 2]) + game.players[0].label = "joe" + game.players[1].label = "dan" + game.outcomes[0]["joe"] = 1 + game.outcomes[0]["dan"] = 2 + game.outcomes[1]["joe"] = 3 + game.outcomes[1]["dan"] = 4 + assert game.outcomes[0]['joe'] == 1 + assert game.outcomes[0]['dan'] == 2 + assert game.outcomes[1]['joe'] == 3 + assert game.outcomes[1]['dan'] == 4 + + +def test_outcome_payoff_by_player(): + game = gbt.Game.new_table([2, 2]) + game.players[0].label = "joe" + game.players[1].label = "dan" + game.outcomes[0]["joe"] = 1 + game.outcomes[0]["dan"] = 2 + game.outcomes[1]["joe"] = 3 + game.outcomes[1]["dan"] = 4 + player1 = game.players[0] + player2 = game.players[1] + assert game.outcomes[0][player1] == 1 + assert game.outcomes[0][player2] == 2 + assert game.outcomes[1][player1] == 3 + assert game.outcomes[1][player2] == 4 diff --git a/src/pygambit/tests/test_players.py b/src/pygambit/tests/test_players.py index df215bbc9..20b0a3158 100644 --- a/src/pygambit/tests/test_players.py +++ b/src/pygambit/tests/test_players.py @@ -1,140 +1,121 @@ -import warnings -import fractions -import unittest - -import pygambit - - -class TestGambitPlayers(unittest.TestCase): - def setUp(self): - self.strategic_game = pygambit.Game.new_table([2, 2]) - self.strategic_game.players[0].label = "Alphonse" - self.strategic_game.players[1].label = "Gaston" - self.extensive_game = pygambit.Game.new_tree() - - def tearDown(self): - del self.strategic_game - - def test_initial_player_count(self): - "Test to ensure 0 initial players" - assert len(self.strategic_game.players) == 2 - - def test_game_add_players(self): - "Test to add player" - p1 = self.strategic_game.players[0] - assert p1.label == "Alphonse" - p2 = self.strategic_game.players[1] - assert p2.label == "Gaston" - - def test_game_add_duplicate_player_names(self): - "Test to add player with preexisting name" - with warnings.catch_warnings(record=True) as w: - self.strategic_game.players[1].label = "Alphonse" - assert ( - str(w[0].message) == - "Another player with an identical label exists" - ) - p1 = self.strategic_game.players[0] - assert p1.label == "Alphonse" - p2 = self.strategic_game.players[1] - assert p2.label == "Alphonse" - - def test_game_players_index_by_string(self): - "Test find a player by providing a label" - p1 = self.strategic_game.players["Alphonse"] - assert p1.label == "Alphonse" - p2 = self.strategic_game.players["Gaston"] - assert p2.label == "Gaston" - - def test_game_players_index_exception_int(self): - "Test to verify when an index is out of range" - self.assertRaises( - IndexError, self.strategic_game.players.__getitem__, 3 - ) - - def test_game_players_index_exception_string(self): - "Test to verify when a player label is not in the list of players" - self.assertRaises( - IndexError, self.strategic_game.players.__getitem__, "None" - ) - - def test_game_players_index_exception_player(self): - "Test to verify when a player object is not in the players" - self.strategic_game_2 = pygambit.Game.new_table([2, 2]) - self.assertRaises( - TypeError, - self.strategic_game.players.__getitem__, - self.strategic_game_2.players[0] - ) - - def test_game_players_index_exception_badindex(self): - "Test to verify when attempting to retrieve with invalid input" - self.assertRaises( - TypeError, self.strategic_game.players.__getitem__, 1.3 - ) - - def test_add_strategic_game_players(self): - """Test to ensure that players can be added to a strategic game - with a single strategy - """ - assert len(self.strategic_game.players) == 2 - self.strategic_game.add_player("new player") - assert len(self.strategic_game.players) == 3 - assert self.strategic_game.players[2].label == "new player" - assert len(self.strategic_game.players[2].strategies) == 1 - self.strategic_game.add_player() - assert len(self.strategic_game.players) == 4 - assert self.strategic_game.players[3].label == "" - - def test_add_extensive_game_players(self): - """Test to ensure that players can be added to an extensive game - with no moves - """ - assert len(self.extensive_game.players) == 0 - self.extensive_game.add_player("new player") - assert len(self.extensive_game.players) == 1 - assert len(self.extensive_game.players[0].infosets) == 0 - self.extensive_game.add_player("new player 2") - assert len(self.extensive_game.players) == 2 - assert len(self.extensive_game.players[1].infosets) == 0 - assert self.extensive_game.players[0].label == "new player" - assert self.extensive_game.players[1].label == "new player 2" - - def test_game_add_strategies_to_player_strategic_game(self): - "Test to add a strategy to a player in a strategic game" - assert len(self.strategic_game.players[0].strategies) == 2 - self.strategic_game.players[0].strategies.add("1st new strategy") - assert len(self.strategic_game.players[0].strategies) == 3 - - with warnings.catch_warnings(record=True) as w: - self.strategic_game.players[0].strategies.add("1st new strategy") - assert ( - str(w[0].message) == - "This player has another strategy with an identical label" - ) - assert len(self.strategic_game.players[0].strategies) == 4 - - def test_game_add_strategies_to_player_extensive_game(self): - """Test to ensure that an exception is raised when attempting - to add a strategy to a player in an extensive game - """ - self.extensive_game.add_player("Alice") - self.assertRaises( - TypeError, self.extensive_game.players[0].strategies.add, "Test" - ) - - def test_player_get_min_payoff(self): - "To test getting the minimum payoff for the players" - game = pygambit.Game.read_game("test_games/payoff_game.nfg") - assert game.players[0].min_payoff == fractions.Fraction(4, 1) - assert game.players["Player 1"].min_payoff == fractions.Fraction(4, 1) - assert game.players[1].min_payoff == fractions.Fraction(1, 1) - assert game.players["Player 2"].min_payoff == fractions.Fraction(1, 1) - - def test_player_get_max_payoff(self): - "To test getting the maximum payoff for the players" - game = pygambit.Game.read_game("test_games/payoff_game.nfg") - assert game.players[0].max_payoff == fractions.Fraction(10, 1) - assert game.players["Player 1"].max_payoff == fractions.Fraction(10, 1) - assert game.players[1].max_payoff == fractions.Fraction(8, 1) - assert game.players["Player 2"].max_payoff == fractions.Fraction(8, 1) +import pytest + +import pygambit as gbt + + +def _generate_strategic_game() -> gbt.Game: + game = gbt.Game.new_table([2, 2]) + game.players[0].label = "Alphonse" + game.players[1].label = "Gaston" + return game + + +def _generate_extensive_game() -> gbt.Game: + return gbt.Game.new_tree() + + +def test_player_count(): + game = gbt.Game.new_table([2, 2]) + assert len(game.players) == 2 + + +def test_player_label(): + game = gbt.Game.new_table([2, 2]) + game.players[0].label = "Alphonse" + game.players[1].label = "Gaston" + assert game.players[0].label == "Alphonse" + assert game.players[1].label == "Gaston" + + +def test_player_index_by_string(): + game = gbt.Game.new_table([2, 2]) + game.players[0].label = "Alphonse" + game.players[1].label = "Gaston" + assert game.players["Alphonse"].label == "Alphonse" + assert game.players["Gaston"].label == "Gaston" + + +def test_player_index_out_of_range(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(IndexError): + _ = game.players[3] + + +def test_player_index_invalid(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(TypeError): + _ = game.players[1.3] + + +def test_player_label_invalid(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(IndexError): + _ = game.players["Not a player"] + + +def test_strategic_game_add_player(): + game = gbt.Game.new_table([2, 2]) + game.add_player() + assert len(game.players) == 3 + assert len(game.players[2].strategies) == 1 + + +def test_extensive_game_add_player(): + game = gbt.Game.new_tree() + game.add_player() + assert len(game.players) == 1 + assert len(game.players[0].infosets) == 0 + assert len(game.players[0].strategies) == 1 + + +def test_strategic_game_add_strategy(): + game = gbt.Game.new_table([2, 2]) + game.players[0].strategies.add("new strategy") + assert len(game.players[0].strategies) == 3 + + +def test_extensive_game_add_strategy(): + game = gbt.Game.new_tree(["Alice"]) + assert len(game.players["Alice"].strategies) == 1 + with pytest.raises(TypeError): + game.players["Alice"].strategies.add("new strategy") + + +def test_player_strategy_by_label(): + game = gbt.Game.new_table([2, 2]) + game.players[0].strategies[0].label = "Cooperate" + assert game.players[0].strategies["Cooperate"].label == "Cooperate" + + +def test_player_strategy_bad_index(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(IndexError): + _ = game.players[0].strategies[42] + + +def test_player_strategy_bad_label(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(IndexError): + _ = game.players[0].strategies["Cooperate"] + + +def test_player_strategy_bad_type(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(TypeError): + _ = game.players[0].strategies[1.3] + + +def test_player_get_min_payoff(): + game = gbt.Game.read_game("test_games/payoff_game.nfg") + assert game.players[0].min_payoff == 4 + assert game.players["Player 1"].min_payoff == 4 + assert game.players[1].min_payoff == 1 + assert game.players["Player 2"].min_payoff == 1 + + +def test_player_get_max_payoff(): + game = gbt.Game.read_game("test_games/payoff_game.nfg") + assert game.players[0].max_payoff == 10 + assert game.players["Player 1"].max_payoff == 10 + assert game.players[1].max_payoff == 8 + assert game.players["Player 2"].max_payoff == 8 diff --git a/src/pygambit/tests/test_strategic.py b/src/pygambit/tests/test_strategic.py index ae0e99b77..ee952c187 100644 --- a/src/pygambit/tests/test_strategic.py +++ b/src/pygambit/tests/test_strategic.py @@ -1,79 +1,52 @@ -import pygambit -import warnings - - -class TestGambitStrategicGame: - def setUp(self): - self.game = pygambit.Game.new_table([2, 2]) - - def tearDown(self): - del self.game - - def test_initial_player_count(self): - "Test to ensure 0 initial players" - assert len(self.game.players) == 2 - - def test_initial_game_title(self): - "Test to ensure correct basic title" - assert str(self.game) == "" - - def test_game_title_assignment(self): - "Test to check title assignment" - self.game.title = "A prisoner's dilemma game" - assert str(self.game) == "" - - def test_game_add_players(self): - "Test to add player" - self.game.players[0].label = "Alphonse" - self.game.players[1].label = "Gaston" - p1 = self.game.players[0] - assert p1.label == "Alphonse" - p2 = self.game.players[1] - assert p2.label == "Gaston" - - def test_game_add_duplicate_player_names(self): - "Test to add player with preexisting name" - self.game.players[0].label = "Alphonse" - with warnings.catch_warnings(record=True) as w: - self.game.players[1].label = "Alphonse" - assert ( - str(w[0].message) == - "Another player with an identical label exists" - ) - p1 = self.game.players[0] - assert p1.label == "Alphonse" - p2 = self.game.players[1] - assert p2.label == "Alphonse" - - def test_game_add_strategies(self): - "Test to add strategies" - assert len(self.game.players[0].strategies) == 2 - assert len(self.game.players[1].strategies) == 2 - - self.game.players[0].strategies[0].label = "Cooperate" - self.game.players[0].strategies[1].label = "Defect" - assert self.game.players[0].strategies[0].label == "Cooperate" - assert self.game.players[0].strategies[1].label == "Defect" - self.game.players[1].strategies[0].label = "Cooperate" - self.game.players[1].strategies[1].label = "Defect" - assert self.game.players[1].strategies[0].label == "Cooperate" - assert self.game.players[1].strategies[1].label == "Defect" - - def test_game_add_duplicate_strategy_names(self): - "Test to add strategies with preexisting names" - assert len(self.game.players[0].strategies) == 2 - assert len(self.game.players[1].strategies) == 2 - - self.game.players[0].strategies[0].label = "Cooperate" - with warnings.catch_warnings(record=True) as w: - self.game.players[0].strategies[1].label = "Cooperate" - assert ( - str(w[0].message) == - "This player has another strategy with an identical label" - ) - assert self.game.players[0].strategies[0].label == "Cooperate" - assert self.game.players[0].strategies[1].label == "Cooperate" - self.game.players[1].strategies[0].label = "Cooperate" - self.game.players[1].strategies[1].label = "Defect" - assert self.game.players[1].strategies[0].label == "Cooperate" - assert self.game.players[1].strategies[1].label == "Defect" +import pytest + +import pygambit as gbt + + +def test_strategic_game_actions(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(gbt.UndefinedOperationError): + _ = game.actions + + +def test_strategic_game_infosets(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(gbt.UndefinedOperationError): + _ = game.infosets + + +def test_strategic_game_root(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(gbt.UndefinedOperationError): + _ = game.root + + +def test_strategic_game_nodes(): + game = gbt.Game.new_table([2, 2]) + assert game.nodes() == [] + + +def test_game_behav_profile_error(): + game = gbt.Game.new_table([2, 2]) + with pytest.raises(gbt.UndefinedOperationError): + _ = game.mixed_behavior_profile() + + +def test_game_is_const_sum(): + game = gbt.Game.read_game("test_games/const_sum_game.nfg") + assert game.is_const_sum + + +def test_game_is_not_const_sum(): + game = gbt.Game.read_game("test_games/non_const_sum_game.nfg") + assert not game.is_const_sum + + +def test_game_get_min_payoff(): + game = gbt.Game.read_game("test_games/payoff_game.nfg") + assert game.min_payoff == 1 + + +def test_game_get_max_payoff(): + game = gbt.Game.read_game("test_games/payoff_game.nfg") + assert game.max_payoff == 10 diff --git a/src/pygambit/tests/test_strategies.py b/src/pygambit/tests/test_strategies.py deleted file mode 100644 index acfd263ae..000000000 --- a/src/pygambit/tests/test_strategies.py +++ /dev/null @@ -1,68 +0,0 @@ -import unittest -import warnings - -import pygambit - - -class TestGambitStrategies(unittest.TestCase): - def setUp(self): - self.game = pygambit.Game.new_table([2, 2]) - self.game.players[0].label = "Alphonse" - self.game.players[1].label = "Gaston" - - def tearDown(self): - del self.game - - def test_game_add_strategies(self): - "Test to add strategies" - assert len(self.game.players[0].strategies) == 2 - assert len(self.game.players[1].strategies) == 2 - - def test_game_add_duplicate_strategy_names(self): - "Test to add strategies with preexisting names" - self.game.players[0].strategies[0].label = "Cooperate" - with warnings.catch_warnings(record=True) as w: - self.game.players[0].strategies[1].label = "Cooperate" - assert ( - str(w[0].message) == - "This player has another strategy with an identical label" - ) - - def test_game_strategies_index_by_string(self): - "Test to find a strategy by providing a string" - self.game.players[0].strategies[0].label = "Cooperate" - s1 = self.game.players[0].strategies["Cooperate"] - assert s1.label == "Cooperate" - - def test_game_strategies_index_exception_int(self): - "Test to verify when an index is out of range" - self.assertRaises( - IndexError, self.game.players[0].strategies.__getitem__, 3 - ) - - def test_game_strategies_index_exception_string(self): - """Test to verify when a strategy label is not in the list - of player's strategies - """ - self.assertRaises( - IndexError, self.game.players[0].strategies.__getitem__, "None" - ) - - def test_game_strategies_index_exception_player(self): - """Test to verify when a strategy object is not in a player's - list of strategies - """ - self.game_2 = pygambit.Game.new_table([2, 2]) - self.assertRaises( - TypeError, - self.game.players[0].strategies.__getitem__, - self.game_2.players[0].strategies[0] - ) - - def test_game_strategies_index_exception_badindex(self): - """Test to verify when attempting to retrieve strategy - with invalid input - """ - self.assertRaises( - TypeError, self.game.players[0].strategies.__getitem__, 1.3 - ) diff --git a/src/pygambit/tests/test_userguide.py b/src/pygambit/tests/test_userguide.py index c90dcedb8..d86547ae6 100644 --- a/src/pygambit/tests/test_userguide.py +++ b/src/pygambit/tests/test_userguide.py @@ -1,43 +1,38 @@ """Tests inspired by examples in the user guide.""" -import unittest - import pygambit as gbt -class TestUserGuide(unittest.TestCase): - def test_trust_game(self): - """Build the one-shot trust game from Kreps (1990)""" - g = gbt.Game.new_tree(players=["Buyer", "Seller"], - title="One-shot trust game, after Kreps (1990)") - g.root - g.root.children - g.append_move(g.root, "Buyer", ["Trust", "Not trust"]) - g.root.children - g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"]) - g.set_outcome(g.root.children[0].children[0], - g.add_outcome([1, 1], label="Trustworthy")) - g.set_outcome(g.root.children[0].children[1], - g.add_outcome([-1, 2], label="Untrustworthy")) - g.set_outcome(g.root.children[1], - g.add_outcome([0, 0], label="Opt-out")) +def test_trust_game(): + """Build the one-shot trust game from Kreps (1990)""" + g = gbt.Game.new_tree(players=["Buyer", "Seller"], + title="One-shot trust game, after Kreps (1990)") + g.append_move(g.root, "Buyer", ["Trust", "Not trust"]) + g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"]) + g.set_outcome(g.root.children[0].children[0], + g.add_outcome([1, 1], label="Trustworthy")) + g.set_outcome(g.root.children[0].children[1], + g.add_outcome([-1, 2], label="Untrustworthy")) + g.set_outcome(g.root.children[1], + g.add_outcome([0, 0], label="Opt-out")) + - def test_myerson_poker(self): - """Build the one-card poker example adapted from Myerson (1991)""" - g = gbt.Game.new_tree(players=["Alice", "Bob"], - title="One card poker game, after Myerson (1991)") - g.append_move(g.root, g.players.chance, ["King", "Queen"]) - for node in g.root.children: - g.append_move(node, "Alice", ["Raise", "Fold"]) - g.append_move(g.root.children[0].children[0], "Bob", ["Meet", "Pass"]) - g.append_infoset(g.root.children[1].children[0], - g.root.children[0].children[0].infoset) - alice_winsbig = g.add_outcome([2, -2], label="Alice wins big") - alice_wins = g.add_outcome([1, -1], label="Alice wins") - bob_winsbig = g.add_outcome([-2, 2], label="Bob wins big") - bob_wins = g.add_outcome([-1, 1], label="Bob wins") - g.set_outcome(g.root.children[0].children[0].children[0], alice_winsbig) - g.set_outcome(g.root.children[0].children[0].children[1], alice_wins) - g.set_outcome(g.root.children[0].children[1], bob_wins) - g.set_outcome(g.root.children[1].children[0].children[0], bob_winsbig) - g.set_outcome(g.root.children[1].children[0].children[1], alice_wins) - g.set_outcome(g.root.children[1].children[1], bob_wins) +def test_myerson_poker(): + """Build the one-card poker example adapted from Myerson (1991)""" + g = gbt.Game.new_tree(players=["Alice", "Bob"], + title="One card poker game, after Myerson (1991)") + g.append_move(g.root, g.players.chance, ["King", "Queen"]) + for node in g.root.children: + g.append_move(node, "Alice", ["Raise", "Fold"]) + g.append_move(g.root.children[0].children[0], "Bob", ["Meet", "Pass"]) + g.append_infoset(g.root.children[1].children[0], + g.root.children[0].children[0].infoset) + alice_winsbig = g.add_outcome([2, -2], label="Alice wins big") + alice_wins = g.add_outcome([1, -1], label="Alice wins") + bob_winsbig = g.add_outcome([-2, 2], label="Bob wins big") + bob_wins = g.add_outcome([-1, 1], label="Bob wins") + g.set_outcome(g.root.children[0].children[0].children[0], alice_winsbig) + g.set_outcome(g.root.children[0].children[0].children[1], alice_wins) + g.set_outcome(g.root.children[0].children[1], bob_wins) + g.set_outcome(g.root.children[1].children[0].children[0], bob_winsbig) + g.set_outcome(g.root.children[1].children[0].children[1], alice_wins) + g.set_outcome(g.root.children[1].children[1], bob_wins)