├── .gitignore
├── LICENSE.txt
├── README.md
├── docs
├── .gitignore
├── Makefile
├── conf.py
├── index.rst
├── installing.rst
├── make.bat
├── modeling.rst
├── simulating.rst
└── source
│ ├── psychsim.pwl.rst
│ ├── psychsim.rst
│ ├── psychsim.tools.rst
│ └── psychsim.ui.rst
├── psychsim
├── __init__.py
├── __main__.py
├── action.py
├── agent.py
├── domains
│ ├── disaster
│ │ ├── .gitignore
│ │ └── anthrax.py
│ ├── evacuation
│ │ └── Fire.py
│ ├── gameTheory
│ │ ├── justcentipede.py
│ │ └── ultimatum.py
│ ├── hri
│ │ ├── .gitignore
│ │ ├── robot.py
│ │ ├── robotWaypoints.py
│ │ ├── wp.py
│ │ ├── wpRobot.py
│ │ └── wpRobotWaypoints.py
│ ├── negotiation
│ │ ├── Full_Ntest.py
│ │ ├── Negotiation_full.py
│ │ ├── Negotiation_legal.py
│ │ ├── Negotiation_loops.py
│ │ └── Negotiation_rounds.py
│ ├── teamofrivals
│ │ ├── .gitignore
│ │ ├── asia.xml
│ │ ├── getLog.py
│ │ ├── newmap.xml
│ │ ├── risk.xml
│ │ ├── teamofrivals.py
│ │ └── torAnalysis.py
│ ├── teamwork
│ │ ├── __init__.py
│ │ ├── loader.py
│ │ ├── resources
│ │ │ ├── base.png
│ │ │ ├── grass.png
│ │ │ ├── grass_noborder.png
│ │ │ ├── heli.png
│ │ │ ├── soldier_blue.png
│ │ │ ├── soldier_red.png
│ │ │ ├── target.png
│ │ │ └── target_old.png
│ │ └── teamwork.py
│ └── wartime
│ │ ├── freedonia.py
│ │ └── index.py
├── graph.py
├── modeling.py
├── probability.py
├── pwl
│ ├── __init__.py
│ ├── keys.py
│ ├── matrix.py
│ ├── plane.py
│ ├── state.py
│ ├── tree.py
│ └── vector.py
├── reward.py
├── shell.py
├── test
│ ├── testPWL.py
│ └── tomjerry.py
├── tools
│ ├── __init__.py
│ ├── gdelt.py
│ └── graph.py
├── ui
│ ├── __init__.py
│ ├── diagram.py
│ ├── mainwindow.py
│ ├── mainwindow.ui
│ ├── mapview.py
│ ├── psychsim.gif
│ ├── psychsim.qrc
│ ├── psychsim_rc.py
│ ├── psychsim_rc2.py
│ ├── psychsim_rc3.py
│ └── worldview.py
└── world.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *~
3 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 David Pynadath
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PsychSim
2 |
3 | PsychSim is an open-source social-simulation framework using decision-theoretic agents who have a theory of mind.
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 | _static
3 | _templates
4 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SPHINXPROJ = PsychSim
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # PsychSim documentation build configuration file, created by
5 | # sphinx-quickstart on Thu Oct 11 22:38:49 2018.
6 | #
7 | # This file is execfile()d with the current directory set to its
8 | # containing dir.
9 | #
10 | # Note that not all possible configuration values are present in this
11 | # autogenerated file.
12 | #
13 | # All configuration values have a default; values that are commented out
14 | # serve to show the default.
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #
20 | import os
21 | import sys
22 | sys.path.insert(0, os.path.abspath('..'))
23 |
24 |
25 | # -- General configuration ------------------------------------------------
26 |
27 | # If your documentation needs a minimal Sphinx version, state it here.
28 | #
29 | # needs_sphinx = '1.0'
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = ['sphinx.ext.autodoc',
35 | 'sphinx.ext.todo',
36 | 'sphinx.ext.viewcode',
37 | 'sphinx.ext.mathjax',
38 | 'sphinx.ext.githubpages']
39 |
40 | # Add any paths that contain templates here, relative to this directory.
41 | templates_path = ['_templates']
42 |
43 | # The suffix(es) of source filenames.
44 | # You can specify multiple suffix as a list of string:
45 | #
46 | # source_suffix = ['.rst', '.md']
47 | source_suffix = '.rst'
48 |
49 | # The master toctree document.
50 | master_doc = 'index'
51 |
52 | # General information about the project.
53 | project = 'PsychSim'
54 | copyright = '2018, David V. Pynadath and Stacy C. Marsella'
55 | author = 'David V. Pynadath and Stacy C. Marsella'
56 |
57 | # The version info for the project you're documenting, acts as replacement for
58 | # |version| and |release|, also used in various other places throughout the
59 | # built documents.
60 | #
61 | # The short X.Y version.
62 | version = '3'
63 | # The full version, including alpha/beta/rc tags.
64 | release = '3.0'
65 |
66 | # The language for content autogenerated by Sphinx. Refer to documentation
67 | # for a list of supported languages.
68 | #
69 | # This is also used if you do content translation via gettext catalogs.
70 | # Usually you set "language" from the command line for these cases.
71 | language = None
72 |
73 | # List of patterns, relative to source directory, that match files and
74 | # directories to ignore when looking for source files.
75 | # This patterns also effect to html_static_path and html_extra_path
76 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
77 |
78 | # The name of the Pygments (syntax highlighting) style to use.
79 | pygments_style = 'sphinx'
80 |
81 | # If true, `todo` and `todoList` produce output, else they produce nothing.
82 | todo_include_todos = True
83 |
84 |
85 | # -- Options for HTML output ----------------------------------------------
86 |
87 | # The theme to use for HTML and HTML Help pages. See the documentation for
88 | # a list of builtin themes.
89 | #
90 | html_theme = 'sphinx_rtd_theme'
91 | html_theme_path = ["_themes", ]
92 |
93 | # Theme options are theme-specific and customize the look and feel of a theme
94 | # further. For a list of options available for each theme, see the
95 | # documentation.
96 | #
97 | # html_theme_options = {}
98 |
99 | # Add any paths that contain custom static files (such as style sheets) here,
100 | # relative to this directory. They are copied after the builtin static files,
101 | # so a file named "default.css" will overwrite the builtin "default.css".
102 | html_static_path = ['_static']
103 |
104 | # Custom sidebar templates, must be a dictionary that maps document names
105 | # to template names.
106 | #
107 | # This is required for the alabaster theme
108 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
109 | html_sidebars = {
110 | '**': [
111 | 'relations.html', # needs 'show_related': True theme option to display
112 | 'searchbox.html',
113 | ]
114 | }
115 |
116 |
117 | # -- Options for HTMLHelp output ------------------------------------------
118 |
119 | # Output file base name for HTML help builder.
120 | htmlhelp_basename = 'PsychSimdoc'
121 |
122 |
123 | # -- Options for LaTeX output ---------------------------------------------
124 |
125 | latex_elements = {
126 | # The paper size ('letterpaper' or 'a4paper').
127 | #
128 | # 'papersize': 'letterpaper',
129 |
130 | # The font size ('10pt', '11pt' or '12pt').
131 | #
132 | # 'pointsize': '10pt',
133 |
134 | # Additional stuff for the LaTeX preamble.
135 | #
136 | # 'preamble': '',
137 |
138 | # Latex figure (float) alignment
139 | #
140 | # 'figure_align': 'htbp',
141 | }
142 |
143 | # Grouping the document tree into LaTeX files. List of tuples
144 | # (source start file, target name, title,
145 | # author, documentclass [howto, manual, or own class]).
146 | latex_documents = [
147 | (master_doc, 'PsychSim.tex', 'PsychSim Documentation',
148 | 'David V. Pynadath and Stacy C. Marsella', 'manual'),
149 | ]
150 |
151 |
152 | # -- Options for manual page output ---------------------------------------
153 |
154 | # One entry per manual page. List of tuples
155 | # (source start file, name, description, authors, manual section).
156 | man_pages = [
157 | (master_doc, 'psychsim', 'PsychSim Documentation',
158 | [author], 1)
159 | ]
160 |
161 |
162 | # -- Options for Texinfo output -------------------------------------------
163 |
164 | # Grouping the document tree into Texinfo files. List of tuples
165 | # (source start file, target name, title, author,
166 | # dir menu entry, description, category)
167 | texinfo_documents = [
168 | (master_doc, 'PsychSim', 'PsychSim Documentation',
169 | author, 'PsychSim', 'One line description of project.',
170 | 'Miscellaneous'),
171 | ]
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. PsychSim documentation master file, created by
2 | sphinx-quickstart on Thu Oct 11 22:38:49 2018.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to PsychSim's documentation!
7 | ====================================
8 |
9 | "I never satisfy myself until I can make a mechanical model of a thing. If I can make a mechanical model, I can understand it. As long as I cannot make a mechanical model all the way through I cannot understand it."
10 |
11 | -- Lord Kelvin
12 |
13 | .. image:: ../psychsim/ui/psychsim.gif
14 | :alt: (c) Susana Maria Halpine
15 | :align: right
16 |
17 | .. toctree::
18 | :maxdepth: 2
19 | :caption: Contents:
20 |
21 | installing
22 | modeling
23 | simulating
24 |
25 |
26 | Indices and tables
27 | ==================
28 |
29 | * :ref:`genindex`
30 | * :ref:`modindex`
31 | * :ref:`search`
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docs/installing.rst:
--------------------------------------------------------------------------------
1 | Installing
2 | ==========
3 |
4 | 1. Clone the repository::
5 |
6 | git clone https://github.com/pynadath/psychsim.git
7 |
8 | 2. Checkout the HRI branch::
9 |
10 | git checkout hri
11 |
12 | 3. Profit
13 |
14 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 | set SPHINXPROJ=PsychSim
13 |
14 | if "%1" == "" goto help
15 |
16 | %SPHINXBUILD% >NUL 2>NUL
17 | if errorlevel 9009 (
18 | echo.
19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
20 | echo.installed, then set the SPHINXBUILD environment variable to point
21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
22 | echo.may add the Sphinx directory to PATH.
23 | echo.
24 | echo.If you don't have Sphinx installed, grab it from
25 | echo.http://sphinx-doc.org/
26 | exit /b 1
27 | )
28 |
29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30 | goto end
31 |
32 | :help
33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34 |
35 | :end
36 | popd
37 |
--------------------------------------------------------------------------------
/docs/simulating.rst:
--------------------------------------------------------------------------------
1 | Simulating
2 | ==========
3 |
--------------------------------------------------------------------------------
/docs/source/psychsim.pwl.rst:
--------------------------------------------------------------------------------
1 | psychsim\.pwl package
2 | =====================
3 |
4 | Submodules
5 | ----------
6 |
7 | psychsim\.pwl\.keys module
8 | --------------------------
9 |
10 | .. automodule:: psychsim.pwl.keys
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | psychsim\.pwl\.matrix module
16 | ----------------------------
17 |
18 | .. automodule:: psychsim.pwl.matrix
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | psychsim\.pwl\.plane module
24 | ---------------------------
25 |
26 | .. automodule:: psychsim.pwl.plane
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | psychsim\.pwl\.state module
32 | ---------------------------
33 |
34 | .. automodule:: psychsim.pwl.state
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | psychsim\.pwl\.tree module
40 | --------------------------
41 |
42 | .. automodule:: psychsim.pwl.tree
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | psychsim\.pwl\.vector module
48 | ----------------------------
49 |
50 | .. automodule:: psychsim.pwl.vector
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 |
56 | Module contents
57 | ---------------
58 |
59 | .. automodule:: psychsim.pwl
60 | :members:
61 | :undoc-members:
62 | :show-inheritance:
63 |
--------------------------------------------------------------------------------
/docs/source/psychsim.rst:
--------------------------------------------------------------------------------
1 | psychsim package
2 | ================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | psychsim.pwl
10 | psychsim.tools
11 | psychsim.ui
12 |
13 | Submodules
14 | ----------
15 |
16 | psychsim\.action module
17 | -----------------------
18 |
19 | .. automodule:: psychsim.action
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
24 | psychsim\.agent module
25 | ----------------------
26 |
27 | .. automodule:: psychsim.agent
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
32 | psychsim\.graph module
33 | ----------------------
34 |
35 | .. automodule:: psychsim.graph
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
40 | psychsim\.modeling module
41 | -------------------------
42 |
43 | .. automodule:: psychsim.modeling
44 | :members:
45 | :undoc-members:
46 | :show-inheritance:
47 |
48 | psychsim\.probability module
49 | ----------------------------
50 |
51 | .. automodule:: psychsim.probability
52 | :members:
53 | :undoc-members:
54 | :show-inheritance:
55 |
56 | psychsim\.reward module
57 | -----------------------
58 |
59 | .. automodule:: psychsim.reward
60 | :members:
61 | :undoc-members:
62 | :show-inheritance:
63 |
64 | psychsim\.shell module
65 | ----------------------
66 |
67 | .. automodule:: psychsim.shell
68 | :members:
69 | :undoc-members:
70 | :show-inheritance:
71 |
72 | psychsim\.world module
73 | ----------------------
74 |
75 | .. automodule:: psychsim.world
76 | :members:
77 | :undoc-members:
78 | :show-inheritance:
79 |
80 |
81 | Module contents
82 | ---------------
83 |
84 | .. automodule:: psychsim
85 | :members:
86 | :undoc-members:
87 | :show-inheritance:
88 |
--------------------------------------------------------------------------------
/docs/source/psychsim.tools.rst:
--------------------------------------------------------------------------------
1 | psychsim\.tools package
2 | =======================
3 |
4 | Submodules
5 | ----------
6 |
7 | psychsim\.tools\.gdelt module
8 | -----------------------------
9 |
10 | .. automodule:: psychsim.tools.gdelt
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | psychsim\.tools\.graph module
16 | -----------------------------
17 |
18 | .. automodule:: psychsim.tools.graph
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | psychsim\.tools\.world2tex module
24 | ---------------------------------
25 |
26 | .. automodule:: psychsim.tools.world2tex
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: psychsim.tools
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/source/psychsim.ui.rst:
--------------------------------------------------------------------------------
1 | psychsim\.ui package
2 | ====================
3 |
4 | Submodules
5 | ----------
6 |
7 | psychsim\.ui\.diagram module
8 | ----------------------------
9 |
10 | .. automodule:: psychsim.ui.diagram
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | psychsim\.ui\.mainwindow module
16 | -------------------------------
17 |
18 | .. automodule:: psychsim.ui.mainwindow
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | psychsim\.ui\.mapview module
24 | ----------------------------
25 |
26 | .. automodule:: psychsim.ui.mapview
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | psychsim\.ui\.psychsim\_rc module
32 | ---------------------------------
33 |
34 | .. automodule:: psychsim.ui.psychsim_rc
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | psychsim\.ui\.psychsim\_rc2 module
40 | ----------------------------------
41 |
42 | .. automodule:: psychsim.ui.psychsim_rc2
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | psychsim\.ui\.psychsim\_rc3 module
48 | ----------------------------------
49 |
50 | .. automodule:: psychsim.ui.psychsim_rc3
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | psychsim\.ui\.viz module
56 | ------------------------
57 |
58 | .. automodule:: psychsim.ui.viz
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
63 | psychsim\.ui\.worldview module
64 | ------------------------------
65 |
66 | .. automodule:: psychsim.ui.worldview
67 | :members:
68 | :undoc-members:
69 | :show-inheritance:
70 |
71 |
72 | Module contents
73 | ---------------
74 |
75 | .. automodule:: psychsim.ui
76 | :members:
77 | :undoc-members:
78 | :show-inheritance:
79 |
--------------------------------------------------------------------------------
/psychsim/__init__.py:
--------------------------------------------------------------------------------
1 | name = "psychsim"
2 |
--------------------------------------------------------------------------------
/psychsim/__main__.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import pickle
3 | from PyQt5.QtCore import *
4 | from PyQt5.QtGui import *
5 | from PyQt5.QtWidgets import *
6 | from pwl.keys import WORLD
7 | from ui.mainwindow import Ui_MainWindow
8 | from ui.worldview import WorldView
9 | from ui.mapview import MapView
10 | from world import World
11 |
12 | settings = QSettings('USC ICT','PsychSim')
13 |
14 | class PsychSimUI(QMainWindow, Ui_MainWindow):
15 | def __init__(self, parent=None):
16 | self.world = None
17 | super(PsychSimUI, self).__init__(parent)
18 | self.setupUi(self)
19 | self.scene = WorldView(self.graphicsView)
20 | self.map = MapView(self.graphicsView)
21 | self.graphicsView.setScene(self.scene)
22 |
23 | @pyqtSlot() # signal with no arguments
24 | def on_actionOpen_triggered(self):
25 | filename,types = QFileDialog.getOpenFileName(self,"PsychSim -- Open File")
26 | if filename:
27 | self.openScenario(str(filename))
28 |
29 | def openScenario(self,filename):
30 | if os.path.splitext(filename)[1] == '.pkl':
31 | with open(filename,'rb') as f:
32 | try:
33 | self.world = pickle.load(f)
34 | except ValueError:
35 | msg = QMessageBox()
36 | msg.setIcon(QMessageBox.Warning)
37 | msg.setText('Scenario file saved under different version of Python')
38 | msg.setInformativeText(filename)
39 | msg.setWindowTitle('Unable to open scenario')
40 | msg.setStandardButtons(QMessageBox.Ok)
41 | msg.exec_()
42 | return
43 | else:
44 | self.world = World(filename)
45 | self.scene.world = self.world
46 | self.scene.clear()
47 | settings.setValue('LastFile',os.path.abspath(filename))
48 | if settings.value('ViewCyclical') == 'yes':
49 | self.findChild(QAction,'actionGround_Truth').setChecked(True)
50 | self.on_actionGround_Truth_triggered()
51 | else:
52 | self.findChild(QAction,'actionAcyclical').setChecked(True)
53 | self.scene.displayWorld()
54 |
55 | @pyqtSlot() # signal with no arguments
56 | def on_actionSave_triggered(self):
57 | filename = settings.value('LastFile').toString()
58 | self.scene.world.save(str(filename))
59 | self.scene.unsetDirty()
60 |
61 | @pyqtSlot() # signal with no arguments
62 | def on_actionQuit_triggered(self):
63 | app.quit()
64 |
65 | @pyqtSlot() # signal with no arguments
66 | def on_actionAgent_triggered(self):
67 | self.scene.colorNodes('agent')
68 |
69 | @pyqtSlot() # signal with no arguments
70 | def on_actionLikelihood_triggered(self):
71 | self.scene.colorNodes('likelihood')
72 |
73 | @pyqtSlot() # signal with no arguments
74 | def on_actionMap_triggered(self):
75 | self.graphicsView.setScene(self.map)
76 |
77 | @pyqtSlot() # signal with no arguments
78 | def on_actionGround_Truth_triggered(self):
79 | # self.world.clearCoords()
80 | self.scene.world = self.world
81 | # self.scene.clear()
82 | self.scene.displayGroundTruth(maxRows=6)
83 | settings.setValue('ViewCyclical','yes')
84 |
85 | @pyqtSlot() # signal with no arguments
86 | def on_actionAcyclical_triggered(self):
87 | self.world.clearCoords()
88 | self.scene.world = self.world
89 | self.scene.clear()
90 | self.scene.displayWorld()
91 | settings.setValue('ViewCyclical','no')
92 |
93 | @pyqtSlot() # signal with no arguments
94 | def on_actionBeliefs_triggered(self):
95 | button = self.findChild(QAction,'actionBeliefs')
96 | if self.findChild(QAction,'actionAcyclical').isChecked():
97 | pass
98 | else:
99 | self.scene.displayGroundTruth(maxRows=6,recursive=button.isChecked())
100 |
101 | @pyqtSlot() # signal with no arguments
102 | def on_actionStep_triggered(self):
103 | self.scene.step()
104 |
105 | @pyqtSlot() # signal with no arguments
106 | def on_actionScreenshot_triggered(self):
107 | name,types = QFileDialog.getSaveFileName(self,'Save File')
108 | if name:
109 | self.scene.saveImage(name)
110 |
111 | @pyqtSlot() # signal with no arguments
112 | def on_actionSubgraphs_triggered(self):
113 | name = QFileDialog.getExistingDirectory(self,'Destination Directory',
114 | options=QFileDialog.ShowDirsOnly)
115 | if name:
116 | self.scene.saveSubgraphs(name)
117 |
118 | def wheelEvent(self,event):
119 | # factor = 1.41**(-event.delta()/240.)
120 | factor = 1.41**(-event.pixelDelta().y()/240.)
121 | self.graphicsView.scale(factor,factor)
122 |
123 | if __name__ == '__main__':
124 | import argparse
125 | import sys
126 |
127 | parser = argparse.ArgumentParser()
128 | parser.add_argument('scenario',default=None,nargs='?',
129 | help='File containing an exising PsychSim scenario')
130 | parser.add_argument('-c','--cyclical',action='store_true',help='Start with cyclical view of graph')
131 |
132 | app = QApplication(sys.argv)
133 | app.setOrganizationName('USC ICT')
134 | app.setOrganizationDomain('ict.usc.edu')
135 | app.setApplicationName('PsychSim')
136 |
137 | args = parser.parse_args(args=[str(el) for el in app.arguments()][1:])
138 | if args.cyclical:
139 | settings.setValue('ViewCyclic','yes')
140 | settings.sync()
141 | elif settings.value('ViewCylical') is None:
142 | settings.setValue('ViewCyclical','no')
143 |
144 | win = PsychSimUI()
145 | if args.scenario is None:
146 | filename = settings.value('LastFile')
147 | if filename and QFile.exists(filename):
148 | win.openScenario(filename)
149 | else:
150 | win.openScenario(args.scenario)
151 | win.showMaximized()
152 | app.exec_()
153 |
154 |
--------------------------------------------------------------------------------
/psychsim/action.py:
--------------------------------------------------------------------------------
1 | import itertools
2 | from xml.dom.minidom import Document,Element,Node,NodeList,parseString
3 |
4 | class Action(dict):
5 | """
6 | :cvar special: a list of keys that are reserved for system use
7 | :type special: list(str)
8 | """
9 | special = ['subject','verb','object']
10 |
11 | def __init__(self,arg={},description=None):
12 | if isinstance(arg,Node):
13 | dict.__init__(self)
14 | self.parse(arg)
15 | if isinstance(arg,str):
16 | values = arg.split('-')
17 | dict.__init__(self,{self.special[i]: values[i] for i in range(len(values))})
18 | else:
19 | dict.__init__(self,arg)
20 | self._string = None
21 | self.description = description
22 |
23 | def match(self,pattern):
24 | for key,value in pattern.items():
25 | if not key in self or self[key] != value:
26 | # Mismatch
27 | return False
28 | else:
29 | # Match
30 | return True
31 |
32 | def agentLess(self):
33 | """
34 | Utility method that returns a subject-independent version of this action
35 | :rtype: Action
36 | """
37 | args = dict(self)
38 | try:
39 | del args['subject']
40 | return self.__class__(args)
41 | except KeyError:
42 | return self.__class__(self)
43 |
44 | def getParameters(self):
45 | """
46 | :return: list of special parameters for this action
47 | :rtype: list(str)
48 | """
49 | return filter(lambda k: not k in self.special,self.keys())
50 |
51 | def __setitem__(self,key,value):
52 | self._string = None
53 | dict.__setitem__(self,key,value)
54 |
55 | def clear(self):
56 | self._string = None
57 | dict.clear(self)
58 |
59 | def root(self):
60 | """
61 | :return: the base action table, with only special keys "subject", "verb", and "object"
62 | :rtype: Action
63 | """
64 | root = {}
65 | for key in self.special:
66 | if key in self:
67 | root[key] = self[key]
68 | return Action(root)
69 |
70 | def __str__(self):
71 | if self._string is None:
72 | elements = []
73 | keys = list(self.keys())
74 | for special in self.special:
75 | if special in self:
76 | elements.append(self[special])
77 | keys.remove(special)
78 | keys.sort()
79 | elements += map(lambda k: self[k],keys)
80 | self._string = '-'.join(map(str,elements))
81 | return self._string
82 |
83 | def __hash__(self):
84 | return hash(str(self))
85 |
86 | def __xml__(self):
87 | doc = Document()
88 | root = doc.createElement('action')
89 | doc.appendChild(root)
90 | for key,value in self.items():
91 | node = doc.createElement('entry')
92 | node.setAttribute('key',key)
93 | node.appendChild(doc.createTextNode(str(value)))
94 | root.appendChild(node)
95 | node = doc.createElement('description')
96 | if self.description:
97 | node.appendChild(doc.createTextNode(self.description))
98 | root.appendChild(node)
99 | return doc
100 |
101 | def parse(self,element):
102 | assert element.tagName == 'action'
103 | self.clear()
104 | child = element.firstChild
105 | while child:
106 | if child.nodeType == child.ELEMENT_NODE:
107 | if child.tagName == 'entry':
108 | key = str(child.getAttribute('key'))
109 | subchild = child.firstChild
110 | while subchild.nodeType != subchild.TEXT_NODE:
111 | subchild = subchild.nextSibling
112 | value = str(subchild.data).strip()
113 | if not key in self.special:
114 | if '.' in value:
115 | value = float(value)
116 | else:
117 | value = int(value)
118 | self[key] = value
119 | elif child.tagName == 'description':
120 | while subchild.nodeType != subchild.TEXT_NODE:
121 | subchild = subchild.nextSibling
122 | self.description = str(subchild.data).strip()
123 | child = child.nextSibling
124 |
125 | class ActionSet(frozenset):
126 |
127 | def __new__(cls,elements=[]):
128 | if isinstance(elements,Element):
129 | iterable = []
130 | node = elements.firstChild
131 | while node:
132 | if node.nodeType == node.ELEMENT_NODE and node.tagName == 'action':
133 | assert node.tagName == 'action','Element has tag %s instead of action' % (node.tagName)
134 | atom = Action(node)
135 | iterable.append(atom)
136 | node = node.nextSibling
137 |
138 | elif isinstance(elements,NodeList):
139 | iterable = []
140 | for node in elements:
141 | if node.nodeType == node.ELEMENT_NODE and node.tagName == 'action':
142 | assert node.tagName == 'action','Element has tag %s instead of action' % (node.tagName)
143 | atom = Action(node)
144 | iterable.append(atom)
145 | elif isinstance(elements,Action):
146 | iterable = [elements]
147 | elif isinstance(elements,dict):
148 | iterable = set()
149 | for subset in elements.values():
150 | iterable |= subset
151 | # iterable = reduce(ActionSet.union,elements.values(),ActionSet())
152 | else:
153 | iterable = elements
154 | return frozenset.__new__(cls,iterable)
155 |
156 | def match(self,pattern):
157 | """
158 | :param pattern: a table of key-value patterns that the action must match
159 | :type pattern: dict
160 | :return: the first action that matches the given pattern, or C{None} if none
161 | :rtype: Action
162 | """
163 | for action in self:
164 | if action.match(pattern):
165 | return action
166 | else:
167 | # No matching actions
168 | return None
169 |
170 | def __getitem__(self,key):
171 | elements = list(self)
172 | result = elements[0].get(key,None)
173 | for atom in elements[1:]:
174 | if key in atom and atom[key] != result:
175 | raise ValueError('Conflicting values for key: %s' % (key))
176 | return result
177 |
178 | def get(self,key,default=None):
179 | try:
180 | return self.__getitem__(key)
181 | except KeyError:
182 | return default
183 |
184 | def __str__(self):
185 | return ','.join(map(str,self))
186 |
187 | def __hash__(self):
188 | return hash(str(self))
189 |
190 | def __lt__(self,other):
191 | return str(self) < str(other)
192 |
193 | def agentLess(self):
194 | """
195 | Utility method that returns a subject-independent version of this action set
196 | :rtype: ActionSet
197 | """
198 | return self.__class__([a.agentLess() for a in self])
199 |
200 | def __xml__(self):
201 | doc = Document()
202 | root = doc.createElement('option')
203 | doc.appendChild(root)
204 | for atom in self:
205 | root.appendChild(atom.__xml__().documentElement)
206 | return doc
207 |
208 | def filterActions(pattern,actions):
209 | """
210 | :type pattern: dict
211 | :return: the subset of given actions that match the given pattern
212 | """
213 | return filter(lambda a: a.match(pattern),actions)
214 |
215 | def powerset(iterable):
216 | """
217 | Utility function, taken from Python doc recipes
218 | powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
219 | """
220 | s = list(iterable)
221 | return itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s)+1))
222 |
223 | def makeActionSet(subject,verb,obj=None):
224 | if obj is None:
225 | return ActionSet([Action({'subject': subject,'verb': verb})])
226 | else:
227 | return ActionSet([Action({'subject': subject,'verb': verb,'object': obj})])
228 |
229 | if __name__ == '__main__':
230 | act1 = Action({'subject': 'I','verb': 'help','object': 'you'})
231 | act2 = Action({'subject': 'you','verb': 'help','object': 'I'})
232 | old = ActionSet([act1,act2])
233 | print(old)
234 | doc = parseString(old.__xml__().toprettyxml())
235 | new = ActionSet(doc.documentElement.childNodes)
236 | print(new)
237 | print(old == new)
238 |
--------------------------------------------------------------------------------
/psychsim/domains/disaster/.gitignore:
--------------------------------------------------------------------------------
1 | *.csv
2 | *.psy
3 | *.bak
4 |
--------------------------------------------------------------------------------
/psychsim/domains/evacuation/Fire.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 |
12 | if __name__ == '__main__':
13 |
14 | # Create scenario
15 | world = World()
16 | totals = {'exiter':3,'follower':4, 'avoider':3} #
17 |
18 | # there are a mix of agent types that have different reward preferences for heading towards door,
19 | # following someone who is closest or avoiding the fire
20 | rewardWeights = {'exiter':{'fire':.4,'door':.5,'follow':.1},'follower':{'fire':.2,'door':.2,'follow':.6},'avoider':{'fire':.6,'door':.3,'follow':.1}}
21 |
22 |
23 | # the fire and door are modeled as agents with no actions - they only have a fixed location
24 |
25 | me = Agent('door')
26 | world.addAgent(me)
27 | world.defineState(me.name,'x',float)
28 | world.setState(me.name,'x',5)
29 | world.defineState(me.name,'y',float)
30 | world.setState(me.name,'y',5)
31 | me.setHorizon(0)
32 |
33 | me = Agent('fire')
34 | world.addAgent(me)
35 | world.defineState(me.name,'x',float)
36 | world.setState(me.name,'x',1)
37 | world.defineState(me.name,'y',float)
38 | world.setState(me.name,'y',1)
39 | me.setHorizon(0)
40 |
41 | # Player state, actions and parameters common to both players
42 | embodiedAgts = []
43 | for base in ['exiter','follower','avoider']:
44 | num = 0
45 | print base
46 | for i in range(totals[base]):
47 | num = num + 1
48 | me = Agent(base + str(num))
49 | world.addAgent(me)
50 | # State
51 | # trying to avoid psychsim thinking in terms x,y coordinates
52 | # so smartbody will have to maintain these values
53 | world.defineState(me.name,'door_dist',float)
54 | world.setState(me.name,'door_dist',3)
55 | world.defineState(me.name,'fire_dist',float)
56 | world.setState(me.name,'fire_dist',5)
57 | world.defineState(me.name,'closest_dist',float)
58 | world.setState(me.name,'closest_dist',4)
59 |
60 | # Actions
61 | me.addAction({'verb': 'do nothing'})
62 | me.addAction({'verb': 'runAway','object': 'fire'})
63 | me.addAction({'verb': 'runTowards','object': 'door'})
64 | me.addAction({'verb': 'followClosest'})
65 | # goals
66 | goal = maximizeFeature(stateKey(me.name,'fire_dist'))
67 | me.setReward(goal,rewardWeights[base]['fire'])
68 | goal = minimizeFeature(stateKey(me.name,'door_dist'))
69 | me.setReward(goal,rewardWeights[base]['door'])
70 | goal = minimizeFeature(stateKey(me.name,'closest_dist'))
71 | me.setReward(goal,rewardWeights[base]['follow'])
72 | # Parameters
73 | me.setHorizon(1)
74 | # me.setParameter('discount',0.9)
75 | me.setParameter('discount',0.2)
76 |
77 | # Turn order: Uncomment the following if you want agents to act in parallel
78 |
79 | actors = world.agents.keys()
80 | # actors = set(actors)
81 | # print actors
82 | # actors.discard('door')
83 | # actors.discard('fire')
84 | # world.setOrder([actors])
85 | actors.remove('door')
86 | actors.remove('fire')
87 | world.setOrder([set(actors)])
88 | print actors
89 |
90 | for agt in actors:
91 | atom = Action({'subject': agt,'verb': 'runAway', 'object':'fire'})
92 | tree = makeTree(incrementMatrix(stateKey(atom['subject'],'fire_dist'),.1))
93 | world.setDynamics(agt,'fire_dist',atom,tree)
94 |
95 | atom = Action({'subject': agt,'verb': 'runTowards', 'object':'door'})
96 | tree = makeTree(incrementMatrix(stateKey(atom['subject'],'door_dist'),-.1))
97 | world.setDynamics(agt,'door_dist',atom,tree)
98 |
99 | atom = Action({'subject': agt,'verb': 'runClosest'})
100 | tree = makeTree(incrementMatrix(stateKey(atom['subject'],'closest_dist'),-.1))
101 | world.setDynamics(agt,'door_dist',atom,tree)
102 |
103 |
104 | # Save scenario to compressed XML file
105 | world.save('default.psy')
106 |
107 | # Create configuration file
108 | # config = SafeConfigParser()
109 | # f = open('default.cfg','w')
110 | # config.write(f)
111 | # f.close()
112 |
113 | # Test saved scenario
114 | world = World('default.psy')
115 | # world.printState()
116 |
117 | for t in range(7):
118 | print 'next:',world.next(world.state.expectation())
119 | world.explain(world.step(),0)
120 | # world.explain()
121 | # print world.step()
122 | world.state.select()
123 |
124 | world.printState()
125 |
126 |
--------------------------------------------------------------------------------
/psychsim/domains/gameTheory/justcentipede.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 | # Create scenario
12 | class Centipede:
13 |
14 |
15 |
16 | def __init__(self,turnOrder,maxRounds,payoff):
17 |
18 | self.maxRounds=maxRounds
19 | self.payoff = payoff
20 | print self.payoff
21 | self.world = World()
22 | stacy = Agent('Stacy')
23 | david = Agent('David')
24 | agts = [stacy, david]
25 |
26 | # Player state, actions and parameters common to both players
27 | for i in range(2):
28 | me = agts[i]
29 | other = agts[1-i]
30 | self.world.addAgent(me)
31 | # State
32 | self.world.defineState(me.name,'money',int)
33 | me.setState('money',0)
34 | mePass = me.addAction({'verb': 'pass','object': other.name})
35 | meTake = me.addAction({'verb': 'take','object': other.name})
36 | # Parameters
37 | me.setHorizon(6)
38 | me.setParameter('discount',1.)
39 | # me.setParameter('discount',0.9)
40 |
41 | # Levels of belief
42 | david.setRecursiveLevel(3)
43 | stacy.setRecursiveLevel(3)
44 |
45 | self.world.setOrder(turnOrder)
46 | # World state
47 | self.world.defineState(None,'round',int,description='The current round')
48 | self.world.setState(None,'round',0)
49 | self.world.defineState(None,'gameOver',bool,description='whether game is over')
50 | self.world.setState(None,'gameOver',False)
51 |
52 | self.world.addTermination(makeTree({'if': thresholdRow(stateKey(None,'round'),self.maxRounds),
53 | True: True,
54 | False: {'if': trueRow(stateKey(None,'gameOver')),
55 | True: True,
56 | False: False}}))
57 |
58 | # Dynamics
59 | for action in stacy.actions | david.actions:
60 | tree = makeTree(incrementMatrix(stateKey(None,'round'),1))
61 | self.world.setDynamics(None,'round',action,tree)
62 | if (action['verb'] == 'take'):
63 | tree = makeTree(setTrueMatrix(stateKey(None,'gameOver')))
64 | self.world.setDynamics(None,'gameOver',action,tree)
65 | agts = ['Stacy','David']
66 | for i in range(2):
67 | key = stateKey(agts[i],'money')
68 | tree = makeTree(self.buildPayoff(0, key, self.payoff[agts[i]]))
69 | self.world.setDynamics(agts[i],'money',action,tree)
70 | elif action['verb'] == 'pass':
71 | agts = ['Stacy','David']
72 | for i in range(2):
73 | key = stateKey(agts[i],'money')
74 | tree = makeTree({'if': equalRow(stateKey(None,'round'),self.maxRounds-1),
75 | True: setToConstantMatrix(key,self.payoff[agts[i]][self.maxRounds]),
76 | False: noChangeMatrix(key)})
77 | self.world.setDynamics(agts[i],'money',action,tree)
78 |
79 |
80 | # really need to ask david about these levels - if adding modesl with levels, can
81 | # the true model point to these but have a different level
82 |
83 | for agent in self.world.agents.values():
84 | agent.addModel('Christian',R={},level=2,rationality=10.,selection='distribution')
85 | agent.addModel('Capitalist',R={},level=2,rationality=10.,selection='distribution')
86 | # agent.addModel('Christian',R={},level=2,rationality=0.01)
87 | # agent.addModel('Capitalist',R={},level=2,rationality=0.01)
88 |
89 | def buildPayoff(self,rnd,key,payoff):
90 | if (rnd == self.maxRounds - 1):
91 | return setToConstantMatrix(key,payoff[rnd])
92 | else:
93 | return {'if': equalRow(stateKey(None,'round'),rnd),
94 | True: setToConstantMatrix(key,payoff[rnd]),
95 | False: self.buildPayoff(rnd+1,key,payoff)}
96 |
97 |
98 | def modeltest(self,trueModels,davidBeliefAboutStacy,stacyBeliefAboutDavid,strongerBelief):
99 | agts = self.world.agents.values()
100 | for i in range(2):
101 | me = agts[i]
102 | other = agts[1-i]
103 | for model in me.models.keys():
104 | if model is True:
105 | name = trueModels[me.name]
106 | else:
107 | name = model
108 | if name == 'Capitalist':
109 | me.setReward(maximizeFeature(stateKey(me.name,'money')),1.0,model)
110 | elif name == 'Christian':
111 | me.setReward(maximizeFeature(stateKey(me.name,'money')),1.0,model)
112 | me.setReward(maximizeFeature(stateKey(other.name,'money')),1.0,model)
113 |
114 | weakBelief = 1.0 - strongerBelief
115 | belief = {'Christian': weakBelief,'Capitalist': weakBelief}
116 | belief[davidBeliefAboutStacy] = strongerBelief
117 | self.world.setMentalModel('David','Stacy',belief)
118 | belief = {'Christian': weakBelief,'Capitalist': weakBelief}
119 | belief[stacyBeliefAboutDavid] = strongerBelief
120 | self.world.setMentalModel('Stacy','David',belief)
121 |
122 | def runit(self,Msg):
123 | print Msg
124 | for t in range(self.maxRounds + 1):
125 | self.world.explain(self.world.step(),level=2)
126 | # self.world.explain(self.world.step(),level=1)
127 | # print self.world.step()
128 | self.world.state.select()
129 | # self.world.printState()
130 | if self.world.terminated():
131 | break
132 |
133 | # Parameters
134 | # me.setHorizon(6)
135 | # me.setParameter('discount',0.9)
136 | # Levels of belief
137 | # david.setRecursiveLevel(3)
138 | # stacy.setRecursiveLevel(3)
139 | # level 2 models
140 | # Rounds
141 | # self.maxRounds=4
142 | #
143 | # Rationality = .01
144 |
145 | # TEST Runs Scripting
146 |
147 | for payoffDict in [{'Stacy': [2,0,3,1,3],
148 | 'David': [0,2,0,4,3]},
149 | {'Stacy': [2,4,3,1,3],
150 | 'David': [0,2,0,4,3]},
151 | {'Stacy': [2,0,3,1,3],
152 | 'David': [0,1,2,4,3]},
153 | {'Stacy': [2,0,1,3,3],
154 | 'David': [0,2,0,4,3]},
155 | {'Stacy': [2,0,1,2,3],
156 | 'David': [0,2,0,3,4]}]:
157 | trueModels = {'Stacy': 'Capitalist',
158 | 'David': 'Capitalist'}
159 | turnOrder=['Stacy','David']
160 |
161 | # The following tests the dynamics by running through every possible action sequence
162 | # for length in range(len(payoffDict['Stacy'])):
163 | # negagts = Centipede(turnOrder, len(payoffDict['Stacy'])-1, payoffDict)
164 | # world = negagts.world
165 | # state = world.state
166 | # while not world.terminated():
167 | # agent = world.agents[negagts.world.next()[0]]
168 | # if world.getState(None,'round').expectation() == length:
169 | # verb = 'take'
170 | # else:
171 | # verb = 'pass'
172 | # for action in agent.getActions(state):
173 | # if action['verb'] == verb:
174 | # break
175 | # else:
176 | # raise NameError,'Unable to find %s for %s' % (verb,agent.name)
177 | # print action
178 | # world.step({agent.name: action})
179 | # world.printState(state)
180 | # break
181 |
182 | negagts = Centipede(turnOrder, len(payoffDict['Stacy'])-1, payoffDict)
183 | negagts.modeltest(trueModels,'Capitalist','Capitalist', 1.0)
184 | negagts.runit("Capitalist and Correct beliefs")
185 |
186 |
--------------------------------------------------------------------------------
/psychsim/domains/gameTheory/ultimatum.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 | # Create scenario
12 |
13 |
14 | class Ultimatum:
15 |
16 | def __init__(self):
17 | self.world = World()
18 | stacy = Agent('Stacy')
19 | david = Agent('David')
20 | agts = [stacy, david]
21 | totalAmt = 4
22 | # Player state, actions and parameters common to both players
23 | for i in range(2):
24 | me = agts[i]
25 | other = agts[1-i]
26 | self.world.addAgent(me)
27 | self.world.defineState(me.name,'offered',int,lo=0,hi=totalAmt)
28 | self.world.defineState(me.name,'money',int,lo=0,hi=totalAmt)
29 | me.setState('offered',0)
30 | me.setState('money',0)
31 | if (me.name == 'Stacy'):
32 | for amt in range(totalAmt + 1):
33 | me.addAction({'verb': 'offer','object': other.name,'amount': amt})
34 | else:
35 | mePass = me.addAction({'verb': 'accept','object': other.name})
36 | mePass = me.addAction({'verb': 'reject','object': other.name})
37 | # Parameters
38 | me.setHorizon(2)
39 | me.setParameter('discount',0.9)
40 | # me.setParameter('discount',1.0)
41 |
42 | # Levels of belief
43 | david.setRecursiveLevel(3)
44 | stacy.setRecursiveLevel(3)
45 |
46 | self.world.setOrder(['Stacy','David'])
47 |
48 | # World state
49 | self.world.defineState(None,'gameOver',bool,description='The current round')
50 | self.world.setState(None,'gameOver',False)
51 |
52 | self.world.addTermination(makeTree({'if': trueRow(stateKey(None,'gameOver')),
53 | True: True, False: False}))
54 | # offer dynamics
55 | atom = Action({'subject': 'Stacy','verb': 'offer', 'object': 'David'})
56 | parties = [atom['subject'], atom['object']]
57 | for j in range(2):
58 | offer = stateKey(parties[j],'offered')
59 | amount = actionKey('amount') if j == 1 else '%d-%s' % (totalAmt,actionKey('amount'))
60 | tree = makeTree(setToConstantMatrix(offer,amount))
61 | self.world.setDynamics(parties[j],'offered',atom,tree)
62 | # accept dynamics
63 | atom = Action({'subject': 'David','verb': 'accept', 'object': 'Stacy'})
64 | parties = [atom['subject'], atom['object']]
65 | for j in range(2):
66 | offer = stateKey(parties[j],'offered')
67 | money = stateKey(parties[j],'money')
68 | tree = makeTree(setToFeatureMatrix(money,offer))
69 | self.world.setDynamics(parties[j],'money',atom,tree)
70 | tree=makeTree(setTrueMatrix(stateKey(None,'gameOver')))
71 | self.world.setDynamics(None,'gameOver',atom,tree)
72 | # reject dynamics
73 | atom = Action({'subject': 'David','verb': 'reject', 'object': 'Stacy'})
74 | tree=makeTree(setTrueMatrix(stateKey(None,'gameOver')))
75 | self.world.setDynamics(None,'gameOver',atom,tree)
76 |
77 | # really need to ask david about these levels - if adding modesl with levels, can
78 | # the true model point to these but have a different level
79 | for agent in self.world.agents.values():
80 | agent.addModel('Christian',R={},level=2,rationality=25.,selection='distribution')
81 | agent.addModel('Capitalist',R={},level=2,rationality=25.,selection='distribution')
82 | # agent.addModel('Christian',R={},level=2,rationality=10.,selection='distribution')
83 | # agent.addModel('Capitalist',R={},level=2,rationality=10.,selection='distribution')
84 | # agent.addModel('Christian',R={},level=2,rationality=10.,selection='distribution')
85 | # agent.addModel('Capitalist',R={},level=2,rationality=10.,selection='random')
86 |
87 |
88 |
89 | def modeltest(self,trueModels,davidBeliefAboutStacy,stacyBeliefAboutDavid,strongerBelief):
90 | agts = self.world.agents.values()
91 | for i in range(2):
92 | me = agts[i]
93 | other = agts[1-i]
94 | for model in me.models.keys():
95 | if model is True:
96 | name = trueModels[me.name]
97 | else:
98 | name = model
99 | if name == 'Capitalist':
100 | me.setReward(maximizeFeature(stateKey(me.name,'money')),1.0,model)
101 | elif name == 'Christian':
102 | me.setReward(maximizeFeature(stateKey(me.name,'money')),1.0,model)
103 | me.setReward(maximizeFeature(stateKey(other.name,'money')),1.0,model)
104 |
105 | weakBelief = 1.0 - strongerBelief
106 | print weakBelief
107 | belief = {'Christian': weakBelief,'Capitalist': weakBelief}
108 | belief[davidBeliefAboutStacy] = strongerBelief
109 | self.world.setMentalModel('David','Stacy',belief)
110 | belief = {'Christian': weakBelief,'Capitalist': weakBelief}
111 | belief[stacyBeliefAboutDavid] = strongerBelief
112 | self.world.setMentalModel('Stacy','David',belief)
113 |
114 | def runit(self,Msg):
115 | print Msg
116 | for t in range(2):
117 | self.world.explain(self.world.step(),level=2)
118 | # print self.world.step()
119 | self.world.state.select()
120 | # self.world.printState()
121 | if self.world.terminated():
122 | break
123 |
124 | # Parameters
125 | # me.setHorizon(6)
126 | # me.setParameter('discount',0.9)
127 | # Levels of belief
128 | # david.setRecursiveLevel(3)
129 | # stacy.setRecursiveLevel(3)
130 | # level 2 models
131 | # Rounds
132 | # self.maxRounds=4
133 | #
134 | # Rationality = .01
135 |
136 | # TEST Runs Scripting
137 |
138 | payoffDict= {'Stacy': [],
139 | 'David': []}
140 |
141 | trueModels = {'Stacy': 'Capitalist',
142 | 'David': 'Capitalist'}
143 | turnOrder=['Stacy','David']
144 | negagts = Ultimatum()
145 | negagts.modeltest(trueModels,'Capitalist','Capitalist', 1.0)
146 | negagts.runit("Capitalist and Correct beliefs")
147 |
148 |
--------------------------------------------------------------------------------
/psychsim/domains/hri/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | *.xml
3 | *.cfg
4 | *.csv
5 |
--------------------------------------------------------------------------------
/psychsim/domains/negotiation/Negotiation_full.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 |
12 | if __name__ == '__main__':
13 |
14 | # Create scenario
15 | world = World()
16 | totals = {'apple':3,'pear':2}
17 | # Stacy state
18 | stacy = Agent('Stacy')
19 | world.addAgent(stacy)
20 | world.defineState(stacy.name,'applesOwned',int,lo=0,hi=totals['apple'])
21 | stacy.setState('applesOwned',0)
22 | world.defineState(stacy.name,'applesOffered',int,lo=0,hi=totals['apple'])
23 | stacy.setState('applesOffered',0)
24 | world.defineState(stacy.name,'pearsOwned',int,lo=0,hi=totals['pear'])
25 | stacy.setState('pearsOwned',0)
26 | world.defineState(stacy.name,'pearsOffered',int,lo=0,hi=totals['pear'])
27 | stacy.setState('pearsOffered',0)
28 |
29 |
30 | # David state
31 | david = Agent('David')
32 | world.addAgent(david)
33 |
34 | world.defineState(david.name,'applesOwned',int,lo=0,hi=totals['apple'])
35 | david.setState('applesOwned',0)
36 | world.defineState(david.name,'applesOffered',int,lo=0,hi=totals['apple'])
37 | david.setState('applesOffered',0)
38 | world.defineState(david.name,'pearsOwned',int,lo=0,hi=totals['pear'])
39 | david.setState('pearsOwned',0)
40 | world.defineState(david.name,'pearsOffered',int,lo=0,hi=totals['pear'])
41 | david.setState('pearsOffered',0)
42 |
43 | # World state
44 | world.defineState(None,'agreement',bool)
45 | world.setState(None,'agreement',False)
46 | world.defineState(None,'applesOffer',bool)
47 | world.setState(None,'applesOffer',False)
48 | world.defineState(None,'pearsOffer',bool)
49 | world.setState(None,'pearsOffer',False)
50 |
51 | world.termination.append(makeTree({'if': trueRow(stateKey(None,'agreement')),
52 | True: True,
53 | False: False}))
54 |
55 | # Turn order: Uncomment the following if you want agents to act in parallel
56 | # world.setOrder([[stacy.name,david.name]])
57 | # Turn order: Uncomment the following if you want agents to act sequentially
58 | world.setOrder([stacy.name,david.name])
59 |
60 | # Stacy actions
61 | stacy.addAction({'verb': 'do nothing'})
62 | stacy.addAction({'verb': 'offerApple','object': david.name,'amount': 0})
63 | stacy.addAction({'verb': 'offerApple','object': david.name,'amount': 1})
64 | stacy.addAction({'verb': 'offerApple','object': david.name,'amount': 2})
65 | stacy.addAction({'verb': 'offerApple','object': david.name,'amount': 3})
66 |
67 | stacy.addAction({'verb': 'offerPear','object': david.name,'amount': 0})
68 | stacy.addAction({'verb': 'offerPear','object': david.name,'amount': 1})
69 | stacy.addAction({'verb': 'offerPear','object': david.name,'amount': 2})
70 |
71 | stacyAccept = stacy.addAction({'verb': 'accept offer','object': david.name})
72 |
73 | # David actions
74 | david.addAction({'verb': 'do nothing'})
75 | david.addAction({'verb': 'offerApple','object': stacy.name,'amount': 0})
76 | david.addAction({'verb': 'offerApple','object': stacy.name,'amount': 1})
77 | david.addAction({'verb': 'offerApple','object': stacy.name,'amount': 2})
78 | david.addAction({'verb': 'offerApple','object': stacy.name,'amount': 3})
79 |
80 | david.addAction({'verb': 'offerPear','object': stacy.name,'amount': 0})
81 | david.addAction({'verb': 'offerPear','object': stacy.name,'amount': 1})
82 | david.addAction({'verb': 'offerPear','object': stacy.name,'amount': 2})
83 |
84 | davidAccept = david.addAction({'verb': 'accept offer','object': stacy.name})
85 |
86 |
87 | david.setLegal(davidAccept,makeTree({'if': trueRow(stateKey(None, 'applesOffer')),
88 | True: {'if': trueRow(stateKey(None, 'pearsOffer')),
89 | True: True,
90 | False: False},
91 | False: False}))
92 |
93 |
94 | stacy.setLegal(stacyAccept, makeTree({'if': trueRow(stateKey(None, 'applesOffer')),
95 | True: {'if': trueRow(stateKey(None, 'pearsOffer')),
96 | True: True,
97 | False: False},
98 | False: False}))
99 |
100 | david.setHorizon(4)
101 | stacy.setHorizon(4)
102 | stacy.setParameter('discount',0.9)
103 | david.setParameter('discount',0.9)
104 | #######################
105 | # A more flexible way to specify the payoffs would be better
106 | # for example we would want to capture that a person might want
107 | # one apple but no more and as many pears as they could get
108 | #
109 | # Also a more flexbile way to specify the model of the other is needed.
110 | # We specifically need ways to specify the model of the other
111 | # that supports abstraction and perhaps easy calculation
112 | # eg "the other will accept any offer that includes at least one apple"
113 |
114 | # Here I just give a simple contrary preferences
115 | # Goals for Stacy
116 | goal = minimizeFeature(stateKey(stacy.name,'applesOwned'))
117 | stacy.setReward(goal,1.)
118 | goal = maximizeFeature(stateKey(stacy.name,'pearsOwned'))
119 | stacy.setReward(goal,2.)
120 |
121 | # Goals for David
122 | goal = maximizeFeature(stateKey(david.name,'applesOwned'))
123 | david.setReward(goal,2.)
124 | goal = minimizeFeature(stateKey(david.name,'pearsOwned'))
125 | david.setReward(goal,1.)
126 |
127 | # Dynamics of offers
128 | agents = [david.name,stacy.name]
129 | for fruit in ['apple','pear']:
130 | for i in range(2):
131 | atom = Action({'subject': agents[i],'verb': 'offer%s' % (fruit.capitalize()),
132 | 'object': agents[1-i]})
133 | offer = stateKey(atom['object'],'%ssOffered' % (fruit))
134 | amount = actionKey('amount')
135 |
136 | tree = makeTree({'if': trueRow('agreement'),
137 | True: noChangeMatrix(offer),
138 | False: setToConstantMatrix(offer,amount)})
139 | world.setDynamics(atom['object'],'%ssOffered' % (fruit),atom,tree)
140 |
141 | offer = stateKey(atom['subject'],'%ssOffered' % (fruit))
142 | tree = makeTree({'if': trueRow('agreement'),
143 | True: noChangeMatrix(offer),
144 | False: setToConstantMatrix(offer,'%d-%s' % (totals[fruit],actionKey('amount')))})
145 | world.setDynamics(atom['subject'],'%ssOffered' % (fruit),atom,tree)
146 | # Offers set offer flag in world state
147 | tree = makeTree({'if': trueRow(stateKey(None,'%ssOffer' % (fruit))),
148 | True: noChangeMatrix(stateKey(None,'%ssOffer' % (fruit))),
149 | False: setTrueMatrix(stateKey(None,'%ssOffer' % (fruit)))})
150 | world.setDynamics(None,'%ssOffer' % (fruit) ,atom,tree)
151 |
152 |
153 | # Dynamics of agreements
154 |
155 | agents = [david.name,stacy.name]
156 | for i in range(2):
157 | atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
158 |
159 | # accept offer sets agreement if both apples and pears have been offered
160 | tree = makeTree({'if': trueRow(stateKey(None,'agreement')),
161 | True: noChangeMatrix(stateKey(None,'agreement')),
162 | False: setTrueMatrix(stateKey(None,'agreement'))})
163 | world.setDynamics(None,'agreement',atom,tree)
164 | # Accepting offer sets ownership
165 | for fruit in ['apple','pear']:
166 | atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
167 | offer = stateKey(atom['object'],'%ssOffered' % (fruit))
168 | owned = stateKey(atom['object'],'%ssOwned' % (fruit))
169 | tree = makeTree({'if': trueRow('agreement'), # this test shouldn't be necessary
170 | False: setToFeatureMatrix(owned,offer),
171 | True: setToFeatureMatrix(owned,offer)})
172 | world.setDynamics(atom['object'],'%ssOwned' % (fruit),atom,tree)
173 | offer = stateKey(atom['subject'],'%ssOffered' % (fruit))
174 | owned = stateKey(atom['subject'],'%ssOwned' % (fruit))
175 | tree = makeTree({'if': trueRow('agreement'), # this test shouldn't be necessary
176 | False: setToFeatureMatrix(owned,offer),
177 | True: setToFeatureMatrix(owned,offer)})
178 | world.setDynamics(atom['subject'],'%ssOwned' % (fruit),atom,tree)
179 |
180 |
181 |
182 | # Save scenario to compressed XML file
183 | world.save('default.psy')
184 |
185 | # Create configuration file
186 | # config = SafeConfigParser()
187 | # f = open('default.cfg','w')
188 | # config.write(f)
189 | # f.close()
190 |
191 | # Test saved scenario
192 | world = World('default.psy')
193 | world.printState()
194 |
195 | for t in range(7):
196 | world.explain(world.step())
197 | world.state.select()
198 | world.printState()
199 |
200 |
201 |
--------------------------------------------------------------------------------
/psychsim/domains/negotiation/Negotiation_legal.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 |
12 | if __name__ == '__main__':
13 |
14 | # Create scenario
15 | maxRounds=8
16 | world = World()
17 | totals = {'apple':1,'pear':2}
18 | batna_prePref = totals['apple'] + totals['pear']
19 | stacy = Agent('Stacy')
20 | david = Agent('David')
21 | agts = [stacy, david]
22 |
23 | # Player state, actions and parameters common to both players
24 | for i in range(2):
25 | me = agts[i]
26 | other = agts[1-i]
27 | world.addAgent(me)
28 | # State
29 | world.defineState(me.name,'appleOwned',int,lo=0,hi=totals['apple'])
30 | me.setState('appleOwned',0)
31 | world.defineState(me.name,'appleOffered',int,lo=0,hi=totals['apple'])
32 | me.setState('appleOffered',0)
33 | world.defineState(me.name,'pearOwned',int,lo=0,hi=totals['pear'])
34 | me.setState('pearOwned',0)
35 | world.defineState(me.name,'pearOffered',int,lo=0,hi=totals['pear'])
36 | me.setState('pearOffered',0)
37 |
38 | world.defineState(me.name,'Batna',int,lo=0,hi=10)
39 | me.setState('Batna', batna_prePref)
40 | world.defineState(me.name,'BatnaOwned',int,lo=0,hi=10)
41 | me.setState('BatnaOwned',0)
42 |
43 | world.defineState(me.name,'agree',bool)
44 | me.setState('agree',False)
45 | # Actions
46 | me.addAction({'verb': 'do nothing'})
47 | for amt in range(totals['apple'] + 1):
48 | tmp = me.addAction({'verb': 'offerApple','object': other.name,'amount': amt})
49 | me.setLegal(tmp,makeTree({'if': trueRow(stateKey(None, 'agreement')),
50 | False: {'if': trueRow(stateKey(None, 'rejectedNegotiation')),
51 | True: False,
52 | False: True},
53 | True: False}))
54 |
55 |
56 | for amt in range(totals['pear'] + 1):
57 | tmp = me.addAction({'verb': 'offerPear','object': other.name,'amount': amt})
58 | me.setLegal(tmp,makeTree({'if': trueRow(stateKey(None, 'agreement')),
59 | False: {'if': trueRow(stateKey(None, 'rejectedNegotiation')),
60 | True: False,
61 | False: True},
62 | True: False}))
63 |
64 | meReject = me.addAction({'verb': 'rejectNegotiation','object': other.name})
65 | me.setLegal(meReject,makeTree({'if': trueRow(stateKey(None, 'agreement')),
66 | False: {'if': trueRow(stateKey(None, 'rejectedNegotiation')),
67 | True: False,
68 | False: True},
69 | True: False}))
70 |
71 | meAccept = me.addAction({'verb': 'accept offer','object': other.name})
72 | me.setLegal(meAccept,makeTree({'if': trueRow(stateKey(None, 'appleOffer')),
73 | True: {'if': trueRow(stateKey(None, 'pearOffer')),
74 | True: {'if': trueRow(stateKey(None, 'agreement')),
75 | False: {'if': trueRow(stateKey(None, 'rejectedNegotiation')),
76 | True: False,
77 | False: True},
78 | True: False},
79 | False: False},
80 | False: False}))
81 | # Parameters
82 | me.setHorizon(6)
83 | me.setParameter('discount',0.9)
84 |
85 | # Levels of belief
86 | david.setRecursiveLevel(3)
87 | stacy.setRecursiveLevel(3)
88 |
89 | # Turn order: Uncomment the following if you want agents to act in parallel
90 | #world.setOrder([{agts[0].name,agts[1].name}])
91 | # Turn order: Uncomment the following if you want agents to act sequentially
92 | world.setOrder([agts[0].name,agts[1].name])
93 |
94 | # World state
95 | world.defineState(None,'agreement',bool)
96 | world.setState(None,'agreement',False)
97 | world.defineState(None,'appleOffer',bool)
98 | world.setState(None,'appleOffer',False)
99 | world.defineState(None,'pearOffer',bool)
100 | world.setState(None,'pearOffer',False)
101 | world.defineState(None,'round',int,description='The current round of the negotiation')
102 | world.setState(None,'round',0)
103 | world.defineState(None,'rejectedNegotiation',bool,
104 | description='Have one of the players walked out?')
105 | world.setState(None, 'rejectedNegotiation', False)
106 |
107 |
108 | # dont terminate so agent sees benefit of early agreement
109 | # world.addTermination(makeTree({'if': trueRow(stateKey(None,'agreement')),
110 | # True: True,
111 | # False: False}))
112 |
113 | # world.addTermination(makeTree({'if': trueRow(stateKey(None,'rejectedNegotiation')),
114 | # True: True,
115 | # False: False}))
116 |
117 | world.addTermination(makeTree({'if': thresholdRow(stateKey(None,'round'),maxRounds),
118 | True: True, False: False}))
119 |
120 |
121 |
122 | #######################
123 | # A more flexible way to specify the payoffs would be better
124 | # for example we would want to capture that a person might want
125 | # one apple but no more and as many pear as they could get
126 | #
127 | # Also a more flexbile way to specify the model of the other is needed.
128 | # We specifically need ways to specify the model of the other
129 | # that supports abstraction and perhaps easy calculation
130 | # eg "the other will accept any offer that includes at least one apple"
131 |
132 | # Here I just give a simple contrary preferences
133 | # Goals for Stacy
134 | appleGoalS = maximizeFeature(stateKey(stacy.name,'appleOwned'))
135 | stacy.setReward(appleGoalS,4.0)
136 | pearGoalS = maximizeFeature(stateKey(stacy.name,'pearOwned'))
137 | stacy.setReward(pearGoalS,1.0)
138 | BatnaGoalS = maximizeFeature(stateKey(stacy.name,'BatnaOwned'))
139 | stacy.setReward(BatnaGoalS,6.0)
140 |
141 | # Goals for David
142 | appleGoalD = maximizeFeature(stateKey(david.name,'appleOwned'))
143 | david.setReward(appleGoalD,1.0)
144 | pearGoalD = maximizeFeature(stateKey(david.name,'pearOwned'))
145 | david.setReward(pearGoalD,4.0)
146 | BatnaGoalD = maximizeFeature(stateKey(david.name,'BatnaOwned'))
147 | david.setReward(BatnaGoalD,0.1)
148 |
149 |
150 | # So the following would be a tree capturing both of Stacy's current goals:
151 | # apple = stateKey(stacy.name,'appleOwned')
152 | # pear = stateKey(stacy.name,'pearOwned')
153 | # goal = makeTree(KeyedVector({apple: -1.,pear: 2.}))
154 | # stacy.setReward(goal,1.)
155 |
156 | # The following would be more complicated, saying that the badness of apple plateaus at 2
157 | # goal = makeTree({'if': thresholdRow(apple,1.5),
158 | # True: KeyedVector({CONSTANT: -2.,pear: 2.}),
159 | # False: KeyedVector({apple: -1.,pear: 2.})})
160 | # stacy.setReward(goal,1.)
161 |
162 | # Dynamics of offers
163 | agents = [david.name,stacy.name]
164 | for i in range(2):
165 | for fruit in ['apple','pear']:
166 | atom = Action({'subject': agents[i],'verb': 'offer%s' % (fruit.capitalize()),
167 | 'object': agents[1-i]})
168 | parties = [atom['subject'], atom['object']]
169 | for j in range(2):
170 | # Set offer amount
171 | offer = stateKey(parties[j],'%sOffered' % (fruit))
172 | amount = actionKey('amount') if j == 1 else '%d-%s' % (totals[fruit],actionKey('amount'))
173 | tree = makeTree(setToConstantMatrix(offer,amount))
174 | world.setDynamics(parties[j],'%sOffered' % (fruit),atom,tree)
175 | # reset agree flags whenever an offer is made
176 | agreeFlag = stateKey(parties[j],'agree')
177 | tree = makeTree(setFalseMatrix(agreeFlag))
178 | world.setDynamics(parties[j],'agree',atom,tree)
179 | # Offers set offer flag in world state
180 | tree = makeTree(setTrueMatrix(stateKey(None,'%sOffer' % (fruit))))
181 | world.setDynamics(None,'%sOffer' % (fruit) ,atom,tree)
182 |
183 |
184 | # agents = [david.name,stacy.name]
185 | # Dynamics of agreements
186 | for i in range(2):
187 | atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
188 |
189 | # accept offer sets accept
190 | tree = makeTree(setTrueMatrix(stateKey(atom['subject'],'agree')))
191 | world.setDynamics(atom['subject'],'agree',atom,tree)
192 |
193 | # accept offer sets agreement if object has accepted
194 | tree = makeTree({'if': trueRow(stateKey(atom['object'],'agree')),
195 | True: setTrueMatrix(stateKey(None,'agreement')),
196 | False: noChangeMatrix(stateKey(None,'agreement'))})
197 | world.setDynamics(None,'agreement',atom,tree)
198 |
199 | # Accepting offer sets ownership
200 | parties = [atom['subject'], atom['object']]
201 | for fruit in ['apple','pear']:
202 | # atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
203 | for j in range(2):
204 | offer = stateKey(parties[j],'%sOffered' % (fruit))
205 | owned = stateKey(parties[j],'%sOwned' % (fruit))
206 | tree = makeTree({'if': trueRow(stateKey(atom['object'],'agree')),
207 | False: noChangeMatrix(owned),
208 | True: setToFeatureMatrix(owned,offer)})
209 | world.setDynamics(parties[j],'%sOwned' % (fruit),atom,tree)
210 | # rejecting give us batna and ends negotiation
211 | atom = Action({'subject': agents[i],'verb': 'rejectNegotiation',
212 | 'object': agents[1-i]})
213 |
214 | tree = makeTree(setToFeatureMatrix(stateKey(atom['subject'],'BatnaOwned') ,stateKey(atom['subject'], 'Batna')))
215 | world.setDynamics(atom['subject'],'BatnaOwned' ,atom,tree)
216 |
217 | tree = makeTree(setToFeatureMatrix(stateKey(atom['object'],'BatnaOwned') ,stateKey(atom['object'], 'Batna')))
218 | world.setDynamics(atom['object'],'BatnaOwned' ,atom,tree)
219 |
220 | tree = makeTree(setTrueMatrix(stateKey(None,'rejectedNegotiation')))
221 | world.setDynamics(None,'rejectedNegotiation' ,atom,tree)
222 |
223 |
224 | for action in stacy.actions | david.actions:
225 | tree = makeTree(incrementMatrix(stateKey(None,'round'),1))
226 | world.setDynamics(None,'round',action,tree)
227 |
228 | # mental models
229 | # David's models of Stacy
230 | stacy.addModel('pearLover',R={appleGoalS: 1.0,pearGoalS: 4.0,BatnaGoalS:6.0},level=2,rationality=0.01)
231 | stacy.addModel('appleLover',R={appleGoalS: 4.0,pearGoalS: 1.0,BatnaGoalS:0.1},level=2,rationality=0.01)
232 | world.setMentalModel(david.name,stacy.name,{'pearLover': 0.5,'appleLover': 0.5})
233 | # Stacy's models of David
234 | david.addModel('pearLover',R={appleGoalD: 1.0,pearGoalD: 4.0,BatnaGoalD: 6.0},level=2,rationality=0.01)
235 | david.addModel('appleLover',R={appleGoalD: 4.0,pearGoalD: 1.0,BatnaGoalD: 0.1},level=2,rationality=0.01)
236 | world.setMentalModel(stacy.name,david.name,{'pearLover': 0.5,'appleLover': 0.5})
237 |
238 |
239 |
240 | # Save scenario to compressed XML file
241 | world.save('default.psy')
242 |
243 | # Create configuration file
244 | # config = SafeConfigParser()
245 | # f = open('default.cfg','w')
246 | # config.write(f)
247 | # f.close()
248 |
249 | # Test saved scenario
250 | world = World('default.psy')
251 | # world.printState()
252 |
253 | for t in range(maxRounds + 1):
254 | world.explain(world.step())
255 | # print world.step()
256 | world.state.select()
257 | world.printState()
258 | if world.terminated():
259 | break
260 |
261 |
262 |
263 |
--------------------------------------------------------------------------------
/psychsim/domains/negotiation/Negotiation_loops.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 |
12 | if __name__ == '__main__':
13 |
14 | # Create scenario
15 | world = World()
16 | totals = {'scotch':1,'tequila':1} # 1 and 1
17 | stacy = Agent('Stacy')
18 | david = Agent('David')
19 | agts = [stacy, david]
20 |
21 | # Player state, actions and parameters common to both players
22 | for i in range(2):
23 | me = agts[i]
24 | other = agts[1-i]
25 | world.addAgent(me)
26 | # State
27 | world.defineState(me.name,'scotchOwned',int,lo=0,hi=totals['scotch'])
28 | me.setState('scotchOwned',0)
29 | world.defineState(me.name,'scotchOffered',int,lo=0,hi=totals['scotch'])
30 | me.setState('scotchOffered',0)
31 | world.defineState(me.name,'tequilaOwned',int,lo=0,hi=totals['tequila'])
32 | me.setState('tequilaOwned',0)
33 | world.defineState(me.name,'tequilaOffered',int,lo=0,hi=totals['tequila'])
34 | me.setState('tequilaOffered',0)
35 | world.defineState(me.name,'agree',bool)
36 | me.setState('agree',False)
37 | # Actions
38 | me.addAction({'verb': 'do nothing'})
39 | for amt in range(totals['scotch'] + 1):
40 | me.addAction({'verb': 'offerScotch','object': other.name,'amount': amt})
41 | for amt in range(totals['tequila'] + 1):
42 | me.addAction({'verb': 'offerTequila','object': other.name,'amount': amt})
43 | meAccept = me.addAction({'verb': 'accept offer','object': other.name})
44 | me.setLegal(meAccept,makeTree({'if': trueRow(stateKey(None, 'scotchOffer')),
45 | True: {'if': trueRow(stateKey(None, 'tequilaOffer')),
46 | True: True,
47 | False: False},
48 | False: False}))
49 | # Parameters
50 | me.setHorizon(4)
51 | # me.setParameter('discount',0.9)
52 | me.setParameter('discount',0.9)
53 | # Levels of belief
54 | david.setRecursiveLevel(3)
55 | stacy.setRecursiveLevel(3)
56 |
57 | # Turn order: Uncomment the following if you want agents to act in parallel
58 | #world.setOrder([{agts[0].name,agts[1].name}])
59 | # Turn order: Uncomment the following if you want agents to act sequentially
60 | world.setOrder([agts[0].name,agts[1].name])
61 |
62 | # World state
63 | world.defineState(None,'agreement',bool)
64 | world.setState(None,'agreement',False)
65 | world.defineState(None,'scotchOffer',bool)
66 | world.setState(None,'scotchOffer',False)
67 | world.defineState(None,'tequilaOffer',bool)
68 | world.setState(None,'tequilaOffer',False)
69 |
70 | world.termination.append(makeTree({'if': trueRow(stateKey(None,'agreement')),
71 | True: True,
72 | False: False}))
73 |
74 |
75 |
76 | #######################
77 | # A more flexible way to specify the payoffs would be better
78 | # for example we would want to capture that a person might want
79 | # one scotch but no more and as many tequila as they could get
80 | #
81 | # Also a more flexbile way to specify the model of the other is needed.
82 | # We specifically need ways to specify the model of the other
83 | # that supports abstraction and perhaps easy calculation
84 | # eg "the other will accept any offer that includes at least one scotch"
85 |
86 | # Here I just give a simple contrary preferences
87 | # Goals for Stacy
88 | scotchGoalS = maximizeFeature(stateKey(stacy.name,'scotchOwned'))
89 | stacy.setReward(scotchGoalS,4.0)
90 | tequilaGoalS = maximizeFeature(stateKey(stacy.name,'tequilaOwned'))
91 | stacy.setReward(tequilaGoalS,1.0)
92 |
93 | # Goals for David
94 | scotchGoalD = maximizeFeature(stateKey(david.name,'scotchOwned'))
95 | david.setReward(scotchGoalD,1.0)
96 | tequilaGoalD = maximizeFeature(stateKey(david.name,'tequilaOwned'))
97 | david.setReward(tequilaGoalD,4.0)
98 |
99 |
100 | # So the following would be a tree capturing both of Stacy's current goals:
101 | # scotch = stateKey(stacy.name,'scotchOwned')
102 | # tequila = stateKey(stacy.name,'tequilaOwned')
103 | # goal = makeTree(KeyedVector({scotch: -1.,tequila: 2.}))
104 | # stacy.setReward(goal,1.)
105 |
106 | # The following would be more complicated, saying that the badness of scotch plateaus at 2
107 | # goal = makeTree({'if': thresholdRow(scotch,1.5),
108 | # True: KeyedVector({CONSTANT: -2.,tequila: 2.}),
109 | # False: KeyedVector({scotch: -1.,tequila: 2.})})
110 | # stacy.setReward(goal,1.)
111 |
112 | # Dynamics of offers
113 | agents = [david.name,stacy.name]
114 | for fruit in ['scotch','tequila']:
115 | for i in range(2):
116 | atom = Action({'subject': agents[i],'verb': 'offer%s' % (fruit.capitalize()),
117 | 'object': agents[1-i]})
118 | parties = [atom['subject'], atom['object']]
119 | for j in range(2):
120 | # Set offer amount
121 | offer = stateKey(parties[j],'%sOffered' % (fruit))
122 | amount = actionKey('amount') if j == 1 else '%d-%s' % (totals[fruit],actionKey('amount'))
123 | tree = makeTree({'if': trueRow('agreement'),
124 | True: noChangeMatrix(offer),
125 | False: setToConstantMatrix(offer,amount)})
126 | world.setDynamics(parties[j],'%sOffered' % (fruit),atom,tree)
127 | # reset agree flags whenever an offer is made
128 | agreeFlag = stateKey(parties[j],'agree')
129 | tree = makeTree({'if': trueRow('agreement'),
130 | True: noChangeMatrix(offer),
131 | False: setFalseMatrix(agreeFlag)})
132 | world.setDynamics(parties[j],'agree',atom,tree)
133 | # Offers set offer flag in world state
134 | tree = makeTree({'if': trueRow(stateKey(None,'%sOffer' % (fruit))),
135 | True: noChangeMatrix(stateKey(None,'%sOffer' % (fruit))),
136 | False: setTrueMatrix(stateKey(None,'%sOffer' % (fruit)))})
137 | world.setDynamics(None,'%sOffer' % (fruit) ,atom,tree)
138 |
139 | # agents = [david.name,stacy.name]
140 | # Dynamics of agreements
141 | for i in range(2):
142 | atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
143 |
144 | # accept offer sets accept
145 | tree = makeTree(setTrueMatrix(stateKey(atom['subject'],'agree')))
146 | world.setDynamics(atom['subject'],'agree',atom,tree)
147 |
148 | # accept offer sets agreement if object has accepted
149 | tree = makeTree({'if': trueRow(stateKey(atom['object'],'agree')),
150 | True: setTrueMatrix(stateKey(None,'agreement')),
151 | False: noChangeMatrix(stateKey(None,'agreement'))})
152 | world.setDynamics(None,'agreement',atom,tree)
153 |
154 | # Accepting offer sets ownership
155 | parties = [atom['subject'], atom['object']]
156 | for fruit in ['scotch','tequila']:
157 | # atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
158 | for j in range(2):
159 | offer = stateKey(parties[j],'%sOffered' % (fruit))
160 | owned = stateKey(parties[j],'%sOwned' % (fruit))
161 | tree = makeTree({'if': trueRow(stateKey(atom['object'],'agree')),
162 | False: noChangeMatrix(owned),
163 | True: setToFeatureMatrix(owned,offer)})
164 | world.setDynamics(parties[j],'%sOwned' % (fruit),atom,tree)
165 |
166 |
167 | # mental models
168 | # David's models of Stacy
169 | stacy.addModel('tequilaLover',R={scotchGoalS: 1.0,tequilaGoalS: 4.0},level=2,rationality=0.01)
170 | stacy.addModel('scotchLover',R={scotchGoalS: 4.0,tequilaGoalS: 1.0},level=2,rationality=0.01)
171 | world.setMentalModel(david.name,stacy.name,{'tequilaLover': 0.5,'scotchLover': 0.5})
172 | # Stacy's models of David
173 | david.addModel('tequilaLover',R={scotchGoalD: 1.0,tequilaGoalD: 4.0},level=2,rationality=0.01)
174 | david.addModel('scotchLover',R={scotchGoalD: 4.0,tequilaGoalD: 1.0},level=2,rationality=0.01)
175 | world.setMentalModel(stacy.name,david.name,{'tequilaLover': 0.5,'scotchLover': 0.5})
176 |
177 |
178 |
179 | # Save scenario to compressed XML file
180 | world.save('default.psy')
181 |
182 | # Create configuration file
183 | # config = SafeConfigParser()
184 | # f = open('default.cfg','w')
185 | # config.write(f)
186 | # f.close()
187 |
188 | # Test saved scenario
189 | world = World('default.psy')
190 | # world.printState()
191 |
192 | for t in range(7):
193 | world.explain(world.step())
194 | # print world.step()
195 | world.state.select()
196 | world.printState()
197 | print stacy.name, "'s beliefs about ", david.name
198 | stacy.printBeliefs()
199 | print david.name, "'s belief about ", stacy.name
200 | david.printBeliefs()
201 | if world.terminated():
202 | break
203 |
204 |
205 |
206 |
--------------------------------------------------------------------------------
/psychsim/domains/negotiation/Negotiation_rounds.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ConfigParser import SafeConfigParser
3 | from optparse import OptionParser
4 |
5 | from psychsim.pwl import *
6 | from psychsim.action import Action,ActionSet
7 | from psychsim.world import World,stateKey,actionKey
8 | from psychsim.agent import Agent
9 |
10 |
11 |
12 | if __name__ == '__main__':
13 |
14 | # Create scenario
15 | maxRounds=8
16 | world = World()
17 | totals = {'scotch':1,'tequila':1} # 1 and 1
18 | stacy = Agent('Stacy')
19 | david = Agent('David')
20 | agts = [stacy, david]
21 |
22 | # Player state, actions and parameters common to both players
23 | for i in range(2):
24 | me = agts[i]
25 | other = agts[1-i]
26 | world.addAgent(me)
27 | # State
28 | world.defineState(me.name,'scotchOwned',int,lo=0,hi=totals['scotch'])
29 | me.setState('scotchOwned',0)
30 | world.defineState(me.name,'scotchOffered',int,lo=0,hi=totals['scotch'])
31 | me.setState('scotchOffered',0)
32 | world.defineState(me.name,'tequilaOwned',int,lo=0,hi=totals['tequila'])
33 | me.setState('tequilaOwned',0)
34 | world.defineState(me.name,'tequilaOffered',int,lo=0,hi=totals['tequila'])
35 | me.setState('tequilaOffered',0)
36 | world.defineState(me.name,'agree',bool)
37 | me.setState('agree',False)
38 | # Actions
39 | me.addAction({'verb': 'do nothing'})
40 | for amt in range(totals['scotch'] + 1):
41 | me.addAction({'verb': 'offerScotch','object': other.name,'amount': amt})
42 | for amt in range(totals['tequila'] + 1):
43 | me.addAction({'verb': 'offerTequila','object': other.name,'amount': amt})
44 | meAccept = me.addAction({'verb': 'accept offer','object': other.name})
45 | me.setLegal(meAccept,makeTree({'if': trueRow(stateKey(None, 'scotchOffer')),
46 | True: {'if': trueRow(stateKey(None, 'tequilaOffer')),
47 | True: True,
48 | False: False},
49 | False: False}))
50 | # Parameters
51 | me.setHorizon(6)
52 | # me.setParameter('discount',0.9)
53 | #me.setParameter('discount',0.9)
54 | # Levels of belief
55 | david.setRecursiveLevel(3)
56 | stacy.setRecursiveLevel(3)
57 |
58 | # Turn order: Uncomment the following if you want agents to act in parallel
59 | #world.setOrder([{agts[0].name,agts[1].name}])
60 | # Turn order: Uncomment the following if you want agents to act sequentially
61 | world.setOrder([agts[0].name,agts[1].name])
62 |
63 | # World state
64 | world.defineState(None,'agreement',bool)
65 | world.setState(None,'agreement',False)
66 | world.defineState(None,'scotchOffer',bool)
67 | world.setState(None,'scotchOffer',False)
68 | world.defineState(None,'tequilaOffer',bool)
69 | world.setState(None,'tequilaOffer',False)
70 | world.defineState(None,'round',int,description='The current round of the negotiation')
71 | world.setState(None,'round',0)
72 |
73 | world.addTermination(makeTree({'if': trueRow(stateKey(None,'agreement')),
74 | True: True,
75 | False: False}))
76 | world.addTermination(makeTree({'if': thresholdRow(stateKey(None,'round'),maxRounds),
77 | True: True, False: False}))
78 |
79 |
80 |
81 | #######################
82 | # A more flexible way to specify the payoffs would be better
83 | # for example we would want to capture that a person might want
84 | # one scotch but no more and as many tequila as they could get
85 | #
86 | # Also a more flexbile way to specify the model of the other is needed.
87 | # We specifically need ways to specify the model of the other
88 | # that supports abstraction and perhaps easy calculation
89 | # eg "the other will accept any offer that includes at least one scotch"
90 |
91 | # Here I just give a simple contrary preferences
92 | # Goals for Stacy
93 | scotchGoalS = maximizeFeature(stateKey(stacy.name,'scotchOwned'))
94 | stacy.setReward(scotchGoalS,4.0)
95 | tequilaGoalS = maximizeFeature(stateKey(stacy.name,'tequilaOwned'))
96 | stacy.setReward(tequilaGoalS,1.0)
97 |
98 | # Goals for David
99 | scotchGoalD = maximizeFeature(stateKey(david.name,'scotchOwned'))
100 | david.setReward(scotchGoalD,1.0)
101 | tequilaGoalD = maximizeFeature(stateKey(david.name,'tequilaOwned'))
102 | david.setReward(tequilaGoalD,4.0)
103 |
104 |
105 | # So the following would be a tree capturing both of Stacy's current goals:
106 | # scotch = stateKey(stacy.name,'scotchOwned')
107 | # tequila = stateKey(stacy.name,'tequilaOwned')
108 | # goal = makeTree(KeyedVector({scotch: -1.,tequila: 2.}))
109 | # stacy.setReward(goal,1.)
110 |
111 | # The following would be more complicated, saying that the badness of scotch plateaus at 2
112 | # goal = makeTree({'if': thresholdRow(scotch,1.5),
113 | # True: KeyedVector({CONSTANT: -2.,tequila: 2.}),
114 | # False: KeyedVector({scotch: -1.,tequila: 2.})})
115 | # stacy.setReward(goal,1.)
116 |
117 | # Dynamics of offers
118 | agents = [david.name,stacy.name]
119 | for fruit in ['scotch','tequila']:
120 | for i in range(2):
121 | atom = Action({'subject': agents[i],'verb': 'offer%s' % (fruit.capitalize()),
122 | 'object': agents[1-i]})
123 | parties = [atom['subject'], atom['object']]
124 | for j in range(2):
125 | # Set offer amount
126 | offer = stateKey(parties[j],'%sOffered' % (fruit))
127 | amount = actionKey('amount') if j == 1 else '%d-%s' % (totals[fruit],actionKey('amount'))
128 | tree = makeTree({'if': trueRow('agreement'),
129 | True: noChangeMatrix(offer),
130 | False: setToConstantMatrix(offer,amount)})
131 | world.setDynamics(parties[j],'%sOffered' % (fruit),atom,tree)
132 | # reset agree flags whenever an offer is made
133 | agreeFlag = stateKey(parties[j],'agree')
134 | tree = makeTree({'if': trueRow('agreement'),
135 | True: noChangeMatrix(offer),
136 | False: setFalseMatrix(agreeFlag)})
137 | world.setDynamics(parties[j],'agree',atom,tree)
138 | # Offers set offer flag in world state
139 | tree = makeTree({'if': trueRow(stateKey(None,'%sOffer' % (fruit))),
140 | True: noChangeMatrix(stateKey(None,'%sOffer' % (fruit))),
141 | False: setTrueMatrix(stateKey(None,'%sOffer' % (fruit)))})
142 | world.setDynamics(None,'%sOffer' % (fruit) ,atom,tree)
143 |
144 | # agents = [david.name,stacy.name]
145 | # Dynamics of agreements
146 | for i in range(2):
147 | atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
148 |
149 | # accept offer sets accept
150 | tree = makeTree(setTrueMatrix(stateKey(atom['subject'],'agree')))
151 | world.setDynamics(atom['subject'],'agree',atom,tree)
152 |
153 | # accept offer sets agreement if object has accepted
154 | tree = makeTree({'if': trueRow(stateKey(atom['object'],'agree')),
155 | True: setTrueMatrix(stateKey(None,'agreement')),
156 | False: noChangeMatrix(stateKey(None,'agreement'))})
157 | world.setDynamics(None,'agreement',atom,tree)
158 |
159 | # Accepting offer sets ownership
160 | parties = [atom['subject'], atom['object']]
161 | for fruit in ['scotch','tequila']:
162 | # atom = Action({'subject': agents[i],'verb': 'accept offer', 'object': agents[1-i]})
163 | for j in range(2):
164 | offer = stateKey(parties[j],'%sOffered' % (fruit))
165 | owned = stateKey(parties[j],'%sOwned' % (fruit))
166 | tree = makeTree({'if': trueRow(stateKey(atom['object'],'agree')),
167 | False: noChangeMatrix(owned),
168 | True: setToFeatureMatrix(owned,offer)})
169 | world.setDynamics(parties[j],'%sOwned' % (fruit),atom,tree)
170 | for action in stacy.actions | david.actions:
171 | tree = makeTree(incrementMatrix(stateKey(None,'round'),1))
172 | world.setDynamics(None,'round',action,tree)
173 |
174 | # mental models
175 | # David's models of Stacy
176 | stacy.addModel('tequilaLover',R={scotchGoalS: 1.0,tequilaGoalS: 4.0},level=2,rationality=0.01)
177 | stacy.addModel('scotchLover',R={scotchGoalS: 4.0,tequilaGoalS: 1.0},level=2,rationality=0.01)
178 | world.setMentalModel(david.name,stacy.name,{'tequilaLover': 0.5,'scotchLover': 0.5})
179 | # Stacy's models of David
180 | david.addModel('tequilaLover',R={scotchGoalD: 1.0,tequilaGoalD: 4.0},level=2,rationality=0.01)
181 | david.addModel('scotchLover',R={scotchGoalD: 4.0,tequilaGoalD: 1.0},level=2,rationality=0.01)
182 | world.setMentalModel(stacy.name,david.name,{'tequilaLover': 0.5,'scotchLover': 0.5})
183 |
184 |
185 |
186 | # Save scenario to compressed XML file
187 | world.save('default.psy')
188 |
189 | # Create configuration file
190 | # config = SafeConfigParser()
191 | # f = open('default.cfg','w')
192 | # config.write(f)
193 | # f.close()
194 |
195 | # Test saved scenario
196 | world = World('default.psy')
197 | # world.printState()
198 |
199 | for t in range(maxRounds + 1):
200 | world.explain(world.step())
201 | # print world.step()
202 | world.state.select()
203 | world.printState()
204 | if world.terminated():
205 | break
206 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/psychsim/domains/teamofrivals/.gitignore:
--------------------------------------------------------------------------------
1 | *.psy
2 | *.csv
3 | *.cfg
4 |
--------------------------------------------------------------------------------
/psychsim/domains/teamofrivals/asia.xml:
--------------------------------------------------------------------------------
1 |
71 |
--------------------------------------------------------------------------------
/psychsim/domains/teamofrivals/getLog.py:
--------------------------------------------------------------------------------
1 | from argparse import ArgumentParser
2 | import ConfigParser
3 |
4 | import csv
5 |
6 | import MySQLdb
7 | import MySQLdb.cursors
8 |
9 | def readLog(gameID,cursor):
10 | for player in range(4):
11 | # Read in-game survey data from db
12 | cursor.execute('SELECT * FROM sim_in_game_survey WHERE '+
13 | 'sigs_game_id=%d ' % (gameID)+
14 | 'AND sigs_player_number=%d' % (player+1))
15 | results = cursor.fetchall()
16 | if results:
17 | fields = sorted(results[0].keys())
18 | with open('%d_%d_survey_db.csv' % (gameID,player+1),'w') as csvfile:
19 | writer = csv.DictWriter(csvfile,fields,extrasaction='ignore')
20 | writer.writeheader()
21 | for record in results:
22 | writer.writerow(record)
23 | # Read game log data from db
24 | cursor.execute('SELECT spl_message FROM sim_project_log WHERE '+
25 | 'spl_game_id=%d ' % (gameID)+
26 | 'AND spl_player_number=%d' % (player+1))
27 | results = cursor.fetchall()
28 | if results:
29 | fields = sorted(results[0].keys())
30 | with open('%d_%d_logs.csv' % (gameID,player+1),'w') as csvfile:
31 | writer = csv.DictWriter(csvfile,fields,extrasaction='ignore')
32 | writer.writeheader()
33 | for record in results:
34 | writer.writerow(record)
35 |
36 | if __name__ == '__main__':
37 | config = ConfigParser.SafeConfigParser()
38 | config.read('db.cfg')
39 | if not config.has_section('mysql'):
40 | config.add_section('mysql')
41 | config.set('mysql','host','localhost')
42 | config.set('mysql','user','')
43 | config.set('mysql','passwd','')
44 | with open('db.cfg','wb') as configfile:
45 | config.write(configfile)
46 | parser = ArgumentParser()
47 | parser.add_argument('gameID',help='Game ID')
48 | args = vars(parser.parse_args())
49 |
50 | db = MySQLdb.connect(host=config.get('mysql','host'),user=config.get('mysql','user'),
51 | passwd=config.get('mysql','passwd'),db='simproject',
52 | cursorclass=MySQLdb.cursors.DictCursor)
53 | cursor = db.cursor()
54 | idList = args['gameID'].split(',')
55 | idList = [int(element) if isinstance(element,str) else map(int,element.split('-')) for element in idList]
56 | for gameId in idList:
57 | if isinstance(gameId,int):
58 | readLog(gameId,cursor)
59 | else:
60 | for subgameId in range(gameId[0],gameId[1]):
61 | readLog(subgameId,cursor)
62 | cursor.close()
63 | db.close()
64 |
--------------------------------------------------------------------------------
/psychsim/domains/teamofrivals/newmap.xml:
--------------------------------------------------------------------------------
1 |
253 |
--------------------------------------------------------------------------------
/psychsim/domains/teamofrivals/risk.xml:
--------------------------------------------------------------------------------
1 |
253 |
--------------------------------------------------------------------------------
/psychsim/domains/teamofrivals/torAnalysis.py:
--------------------------------------------------------------------------------
1 | import csv
2 |
3 | import teamofrivals
4 |
5 | if __name__ == '__main__':
6 | data = []
7 | expectedTime = {}
8 | probability = {}
9 | values = {}
10 | territories = {}
11 | R = {}
12 | Values = {}
13 | Territories = {}
14 | with open('risk.csv') as csvfile:
15 | reader = csv.DictReader(csvfile)
16 | for row in reader:
17 | data.append(row)
18 | for row in data:
19 | incentives = teamofrivals.counts2incentives({'value': int(row['valueRewards']),
20 | 'individual': int(row['territoryRewards']),
21 | 'team': int(row['teamRewards'])})
22 | key = ''.join(incentives)
23 | R[key] = incentives
24 | if not expectedTime.has_key(key):
25 | expectedTime[key] = 0.
26 | probability[key] = 0.
27 | values[key] = {}
28 | territories[key] = {}
29 | for player in R[key]:
30 | values[key][player] = 0.
31 | territories[key][player] = 0.
32 | Values[player] = 0.
33 | Territories[player] = 0.
34 |
35 | probability[key] += float(row['probability'])
36 | expectedTime[key] += float(row['probability'])*float(row['rounds'])
37 | for player in range(len(R[key])):
38 | values[key][R[key][player]] += float(row['probability'])*\
39 | float(row['valuePlayer%d' % (player+1)])
40 | territories[key][R[key][player]] += float(row['probability'])*\
41 | float(row['territoryPlayer%d' % (player+1)])
42 | for key,value in sorted(expectedTime.items()):
43 | print '%s\t%5.2f' % (key,value/probability[key])
44 | for reward in values[key].keys():
45 | V = values[key][reward]/(probability[key]*float(R[key].count(reward)))
46 | print '\tV[%s]\t%5.2f' % (reward[:4],V)
47 | Values[reward] += V
48 | for reward in values[key].keys():
49 | T = territories[key][reward]/(probability[key]*float(R[key].count(reward)))
50 | print '\tT[%s]\t%5.2f' % (reward[:4],T)
51 | Territories[reward] += T
52 | for key,value in sorted(Values.items()):
53 | print '%s\t%5.2f' % (key,value)
54 | for key,value in sorted(Territories.items()):
55 | print '%s\t%5.2f' % (key,value)
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/__init__.py
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/loader.py:
--------------------------------------------------------------------------------
1 | from Tkinter import *
2 | from teamwork import *
3 | from time import sleep
4 |
5 | class BaseLoader:
6 |
7 | def __init__(self, master):
8 | master.wm_title("Scenario Config")
9 | labels = ["Map Size X", "Map Size Y", "Soldiers", "Enemies", "Bases"] # Display text
10 | fields = [] # Fields for storing entries
11 | for i in range(0, 5):
12 | Label(master, text=labels[i]).grid(row=i)
13 | fields.append(Entry(master, width=5))
14 | fields[i].grid(row=i, column=1)
15 |
16 | labels = ["Soldier Rewards:", "S/G Dist", "S/E Dist",
17 | "Base Rewards:", "H/E Dist", "H Cost",
18 | "Helicopter Rewards:", "H/E Dist", "S/E Dist",
19 | "Enemy Rewards:", "S/E Dist", "H/E Dist", "S/G Dist"]
20 | weights = [None] * 14
21 |
22 | for i in range(0, len(labels)):
23 | Label(master, text=labels[i]).grid(row=i + 6)
24 | if i % 3 or i == 12:
25 | weights[i] = Entry(master, width=5)
26 | weights[i].grid(row=i + 6, column=1)
27 |
28 | def configure():
29 | for i in range(0, 5):
30 | fields[i].configure(state='disabled')
31 |
32 | newWindow = Toplevel(master)
33 | app = AdvancedLoader(newWindow, fields)
34 |
35 |
36 | def load_default():
37 | defaults = [8, 5, 1, 1, 1]
38 | for i in range(0, 5):
39 | fields[i].configure(state='normal')
40 | fields[i].delete(0, 'end')
41 | fields[i].insert(0, defaults[i])
42 |
43 | default_weights = [None, 0.5, -0.5, None, 0.5, 0.2, None, -1.0, 1.0, None, 0.5, 0.6, -1.0]
44 | for i in range(0, len(labels)):
45 | if i % 3 or i == 12:
46 | weights[i].delete(0, 'end')
47 | weights[i].insert(0, default_weights[i])
48 |
49 | def run_with_visual():
50 | MAP_SIZE_X = int(fields[0].get())
51 | MAP_SIZE_Y = int(fields[1].get())
52 | F_ACTORS = int(fields[2].get())
53 | F_START_LOC = stored[0]
54 | F_GOAL_LOC = stored[1]
55 | E_ACTORS = int(fields[3].get())
56 | E_START_LOC = stored[2]
57 | D_ACTORS = int(fields[4].get())
58 | D_START_LOC = stored[3]
59 | AGENT = [float(weights[1].get()), float(weights[2].get())]
60 | BASE = [float(weights[4].get()), float(weights[5].get())]
61 | DISTRACTOR = [float(weights[7].get()), float(weights[8].get())]
62 | ENEMY = [float(weights[10].get()), float(weights[11].get()), float(weights[12].get())]
63 |
64 | self.run = Scenario(
65 | MAP_SIZE_X,
66 | MAP_SIZE_Y,
67 | F_ACTORS,
68 | F_START_LOC,
69 | F_GOAL_LOC,
70 | E_ACTORS,
71 | E_START_LOC,
72 | 5,
73 | D_ACTORS,
74 | D_START_LOC,
75 | BASE,
76 | DISTRACTOR,
77 | ENEMY,
78 | AGENT
79 | )
80 | self.run.run_with_visual()
81 | del stored[:]
82 | for i in range(0, 5):
83 | fields[i].configure(state='normal')
84 |
85 | def run_without_visual():
86 | MAP_SIZE_X = int(fields[0].get())
87 | MAP_SIZE_Y = int(fields[1].get())
88 | F_ACTORS = int(fields[2].get())
89 | F_START_LOC = stored[0]
90 | F_GOAL_LOC = stored[1]
91 | E_ACTORS = int(fields[3].get())
92 | E_START_LOC = stored[2]
93 | D_ACTORS = int(fields[4].get())
94 | D_START_LOC = stored[3]
95 | AGENT = [float(weights[1].get()), float(weights[2].get())]
96 | BASE = [float(weights[4].get()), float(weights[5].get())]
97 | DISTRACTOR = [float(weights[7].get()), float(weights[8].get())]
98 | ENEMY = [float(weights[10].get()), float(weights[11].get()), float(weights[12].get())]
99 |
100 | self.run = Scenario(
101 | MAP_SIZE_X,
102 | MAP_SIZE_Y,
103 | F_ACTORS,
104 | F_START_LOC,
105 | F_GOAL_LOC,
106 | E_ACTORS,
107 | E_START_LOC,
108 | 5,
109 | D_ACTORS,
110 | D_START_LOC,
111 | BASE,
112 | DISTRACTOR,
113 | ENEMY,
114 | AGENT
115 | )
116 | self.run.run_without_visual()
117 | del stored[:]
118 | for i in range(0, 5):
119 | fields[i].configure(state='normal')
120 |
121 | Button(master, text='Configure', command=configure).grid(row=100, column=0, sticky=E + W, pady=4)
122 | Button(master, text='Default', command=load_default).grid(row=100, column=1, sticky=E + W, pady=4)
123 | Button(master, text='Run Visual', command=run_with_visual).grid(row=101, column=0, sticky=E + W, pady=4)
124 | Button(master, text='Run', command=run_without_visual).grid(row=101, column=1, sticky=E + W, pady=4)
125 | # Button(master, text='Stop', command=stop).grid(row=101, column=1, sticky=E + W, pady=4)
126 |
127 |
128 | class AdvancedLoader:
129 | soldier_locations = []
130 | soldier_goals = []
131 | enemy_locations = []
132 | base_locations = []
133 |
134 | def __init__(self, master, fields):
135 | self.master = master
136 | self.fields = fields
137 | soldiers_count = int(fields[2].get())
138 | enemies_count = int(fields[3].get())
139 | bases_count = int(fields[4].get())
140 |
141 | Label(master, text='x').grid(row=0, column=1)
142 | Label(master, text='y').grid(row=0, column=2)
143 |
144 | for i in range(0, soldiers_count):
145 | Label(master, text='Soldier' + str(i) + " Location:").grid(row=i + 1)
146 | soldier_x = Entry(master, width=5)
147 | soldier_x.grid(row=i + 1, column=1)
148 | soldier_x.insert(0, "1")
149 | soldier_y = Entry(master, width=5)
150 | soldier_y.grid(row=i + 1, column=2)
151 | soldier_y.insert(0, "2")
152 | location = [soldier_x, soldier_y]
153 | self.soldier_locations.append(location)
154 |
155 | for i in range(0, soldiers_count):
156 | Label(master, text='Soldier' + str(i) + " Goal:").grid(row=i + soldiers_count + 1)
157 | soldier_x = Entry(master, width=5)
158 | soldier_x.grid(row=i + soldiers_count + 1, column=1)
159 | soldier_x.insert(0, "5")
160 | soldier_y = Entry(master, width=5)
161 | soldier_y.grid(row=i + soldiers_count + 1, column=2)
162 | soldier_y.insert(0, "4")
163 | location = [soldier_x, soldier_y]
164 | self.soldier_goals.append(location)
165 |
166 | for i in range(0, enemies_count):
167 | Label(master, text='Enemy' + str(i) + " Location:").grid(row=i + 2 * soldiers_count + 1)
168 | enemy_x = Entry(master, width=5)
169 | enemy_x.grid(row=i + 2 * soldiers_count + 1, column=1)
170 | enemy_x.insert(0, "4")
171 | enemy_y = Entry(master, width=5)
172 | enemy_y.grid(row=i + 2 * soldiers_count + 1, column=2)
173 | enemy_y.insert(0, "3")
174 | location = [enemy_x, enemy_y]
175 | self.enemy_locations.append(location)
176 |
177 | for i in range(0, bases_count):
178 | Label(master, text='Base' + str(i) + " Location:").grid(row=i + 2 * soldiers_count + enemies_count + 1)
179 | base_x = Entry(master, width=5)
180 | base_x.grid(row=i + 2 * soldiers_count + enemies_count + 1, column=1)
181 | base_x.insert(0, "0")
182 | base_y = Entry(master, width=5)
183 | base_y.grid(row=i + 2 * soldiers_count + enemies_count + 1, column=2)
184 | base_y.insert(0, "0")
185 | location = [base_x, base_y]
186 | self.base_locations.append(location)
187 | # For debug
188 | # print('AdvancedLoader Init')
189 | # print(self.soldier_locations)
190 |
191 | Button(master, text='Save Locations', command=self.return_locations).grid(row=100, column=0, sticky=E + W,
192 | pady=4)
193 |
194 | def return_locations(self):
195 | # For debug
196 | # print('Return locations called')
197 | # print(self.soldier_locations)
198 |
199 | soldier_locations_values = []
200 | soldier_goals_values = []
201 | enemy_locations_values = []
202 | base_locations_values = []
203 | for i in range(0, len(self.soldier_locations)):
204 | soldier_locations_values.append(
205 | str(self.soldier_locations[i][0].get()) + ',' + str(self.soldier_locations[i][1].get()))
206 | # print(soldier_locations_values)
207 |
208 | for i in range(0, len(self.soldier_locations)):
209 | soldier_goals_values.append(
210 | str(self.soldier_goals[i][0].get()) + ',' + str(self.soldier_goals[i][1].get()))
211 | # print(soldier_goals_values)
212 |
213 | for i in range(0, len(self.enemy_locations)):
214 | enemy_locations_values.append(
215 | str(self.enemy_locations[i][0].get()) + ',' + str(self.enemy_locations[i][1].get()))
216 | # print(enemy_locations_values)
217 |
218 | for i in range(0, len(self.base_locations)):
219 | base_locations_values.append(
220 | str(self.base_locations[i][0].get()) + ',' + str(self.base_locations[i][1].get()))
221 | # print(base_locations_values)
222 | global stored
223 | stored = [soldier_locations_values, soldier_goals_values, enemy_locations_values, base_locations_values]
224 | del self.soldier_locations[:]
225 | del self.soldier_goals[:]
226 | del self.base_locations[:]
227 | del self.enemy_locations[:]
228 |
229 | # For debug
230 | # print('Return locations finished')
231 | # print(stored)
232 |
233 |
234 | if __name__ == '__main__':
235 | root = Tk()
236 | app = BaseLoader(root)
237 | root.mainloop()
238 | # root.mainloop()
239 | # Thread(target=root.mainloop()).start()
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/base.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/base.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/grass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/grass.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/grass_noborder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/grass_noborder.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/heli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/heli.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/soldier_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/soldier_blue.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/soldier_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/soldier_red.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/target.png
--------------------------------------------------------------------------------
/psychsim/domains/teamwork/resources/target_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/domains/teamwork/resources/target_old.png
--------------------------------------------------------------------------------
/psychsim/graph.py:
--------------------------------------------------------------------------------
1 | """
2 | Class definition for representation of dependency structure among all variables in a PsychSim scenario
3 | """
4 | from psychsim.pwl.keys import *
5 | from psychsim.action import ActionSet
6 |
7 | class DependencyGraph(dict):
8 | """
9 | Representation of dependency structure among PsychSim variables
10 | """
11 | def __init__(self,myworld=None):
12 | self.world = myworld
13 | self.clear()
14 | dict.__init__(self)
15 |
16 | def clear(self):
17 | self.root = None
18 | self.layers = None
19 | self.evaluation = None
20 | dict.clear(self)
21 |
22 | def getLayers(self):
23 | if self.layers is None:
24 | self.computeLineage()
25 | return self.layers
26 |
27 | def getEvaluation(self):
28 | if self.evaluation is None:
29 | self.computeEvaluation()
30 | return self.evaluation
31 |
32 | def getRoot(self):
33 | if self.root is None:
34 | self.computeLineage()
35 | return self.root
36 |
37 | def deleteKeys(self,toDelete):
38 | self.evaluation = [keySet-toDelete for keySet in self.evaluation if keySet-toDelete]
39 |
40 | def __getitem__(self,key):
41 | if len(self) == 0:
42 | self.computeGraph()
43 | return dict.__getitem__(self,key)
44 |
45 | def computeGraph(self,agents=None,state=None,belief=False):
46 | # Process the unary state features
47 | if agents is None:
48 | agents = sorted(self.world.agents.keys())
49 | agents.append(WORLD)
50 | if state is None:
51 | state = self.world.state
52 | for agent in agents:
53 | if agent in self.world.locals:
54 | variables = self.world.locals[agent]
55 | for feature in variables.keys():
56 | key = stateKey(agent,feature)
57 | if key in state:
58 | self[key] = {'agent': agent,'type': 'state pre',
59 | 'children': set(),'parents': set()}
60 | self[makeFuture(key)] = {'agent': agent,'type': 'state post',
61 | 'children': set(),'parents': set()}
62 | # Process the binary state features
63 | for relation,table in self.world.relations.items():
64 | for key,entry in table.items():
65 | if key in state and entry['subject'] in agents and entry['object'] in agents:
66 | self[key] = {'agent': entry['subject'],
67 | 'type': 'state pre',
68 | 'children': set(),
69 | 'parents': set()}
70 | self[makeFuture(key)] = {'agent': entry['subject'],
71 | 'type': 'state post',
72 | 'children': set(),
73 | 'parents': set()}
74 | for name in agents:
75 | if name != WORLD:
76 | # Create the agent reward node
77 | agent = self.world.agents[name]
78 | R = agent.getReward()
79 | if R:
80 | if {reward for reward in R.values()} != {None}:
81 | self[name] = {'agent': name,
82 | 'type': 'utility',
83 | 'parents': set(),
84 | 'children': set()}
85 | # Process the agent actions
86 | for action in agent.actions:
87 | action = ActionSet([a.root() for a in action])
88 | if not action in self:
89 | self[action] = {'agent': name,
90 | 'type': 'action',
91 | 'parents': set(),
92 | 'children': set()}
93 | # Create links from dynamics
94 | for key,dynamics in self.world.dynamics.items():
95 | if not isinstance(key,str):
96 | continue
97 | if isTurnKey(key):
98 | continue
99 | if isStateKey(key) and not state2agent(key) in agents:
100 | continue
101 | if isBinaryKey(key) and not key2relation(key)['subject'] in agents and \
102 | not key2relation(key)['object'] in agents:
103 | continue
104 | if not key in self:
105 | continue
106 | # assert self.has_key(key),'Graph has not accounted for key: %s' % (key)
107 | if isinstance(dynamics,bool):
108 | continue
109 | for action,tree in dynamics.items():
110 | if not action is True and action['subject'] in agents:
111 | # Link between action to this feature
112 | if action in self:
113 | # assert self.has_key(action),'Graph has not accounted for action: %s' % (action)
114 | dict.__getitem__(self,makeFuture(key))['parents'].add(action)
115 | dict.__getitem__(self,action)['children'].add(makeFuture(key))
116 | # Link between dynamics variables and this feature
117 | for parent in tree.getKeysIn() - set([CONSTANT]):
118 | if (state2agent(parent) == WORLD or state2agent(parent) in agents) and \
119 | parent in self:
120 | dict.__getitem__(self,makeFuture(key))['parents'].add(parent)
121 | dict.__getitem__(self,parent)['children'].add(makeFuture(key))
122 | for name in agents:
123 | if name in self:
124 | agent = self.world.agents[name]
125 | # Create links from reward
126 | model = '%s0' % (agent.name)
127 | R = agent.getReward(model)
128 | for parent in R.getKeysIn() - set([CONSTANT]):
129 | if isStateKey(parent) and not state2agent(parent) in agents:
130 | continue
131 | if parent in self:
132 | # Link between variable and agent utility
133 | dict.__getitem__(self,name)['parents'].add(makeFuture(parent))
134 | dict.__getitem__(self,makeFuture(parent))['children'].add(name)
135 | # Create links from legality
136 | for action,tree in agent.legal.items():
137 | action = ActionSet([a.root() for a in action])
138 | for parent in tree.getKeysIn() - set([CONSTANT]):
139 | if isStateKey(parent) and not state2agent(parent) in agents:
140 | continue
141 | if action in self and parent in self:
142 | # Link between prerequisite variable and action
143 | dict.__getitem__(self,action)['parents'].add(parent)
144 | dict.__getitem__(self,parent)['children'].add(action)
145 |
146 | def items(self):
147 | if len(self) == 0:
148 | self.computeGraph()
149 | return dict.items(self)
150 |
151 | def keys(self):
152 | if len(self) == 0:
153 | self.computeGraph()
154 | return dict.keys(self)
155 |
156 | def values(self):
157 | if len(self) == 0:
158 | self.computeGraph()
159 | return dict.values(self)
160 |
161 | def computeLineage(self):
162 | """
163 | Add ancestors to everybody, also computes layers
164 | """
165 | self.root = set()
166 | self.layers = []
167 | for key,node in self.items():
168 | node['ancestors'] = set(node['parents'])
169 | if len(node['parents']) == 0:
170 | # Root node
171 | self.root.add(key)
172 | node['level'] = 0
173 | self.layers = [self.root]
174 | level = 0
175 | while sum(map(len,self.layers)) < len(self):
176 | layer = set()
177 | for key in self.layers[level]:
178 | for child in self[key]['children']:
179 | # Update ancestors
180 | self[child]['ancestors'] |= self[key]['ancestors']
181 | if not child in layer:
182 | # Check whether eligible for the new layer
183 | for parent in self[child]['parents']:
184 | if not 'level' in self[parent] or self[parent]['level'] > level:
185 | # Ineligible to be in this layer
186 | break
187 | else:
188 | # All parents are in earlier layers
189 | layer.add(child)
190 | self[child]['level'] = level + 1
191 | # Add new layer
192 | self.layers.append(layer)
193 | level += 1
194 |
195 | def computeEvaluation(self):
196 | """
197 | Determine the order in which to compute new values for state features
198 | """
199 | self.getLayers()
200 | self.evaluation = []
201 | # for key in self.world.variables:
202 | # while len(self.evaluation) <= self[key]['level']:
203 | # self.evaluation.append(set())
204 | # self.evaluation[self[key]['level']].add(makePresent(key))
205 |
206 | for agent,variables in self.world.locals.items():
207 | for feature in variables.keys():
208 | key = stateKey(agent,feature,True)
209 | while len(self.evaluation) <= self[key]['level']:
210 | self.evaluation.append(set())
211 | self.evaluation[self[key]['level']].add(makePresent(key))
212 | for relation,variables in self.world.relations.items():
213 | for key,table in variables.items():
214 | while len(self.evaluation) <= self[key]['level']:
215 | self.evaluation.append(set())
216 | self.evaluation[self[key]['level']].add(makePresent(key))
217 |
--------------------------------------------------------------------------------
/psychsim/modeling.py:
--------------------------------------------------------------------------------
1 | from argparse import ArgumentParser
2 | import csv
3 | import itertools
4 | import logging
5 | import os
6 |
7 | from psychsim.pwl import *
8 | from psychsim.world import *
9 |
10 | """
11 | Functions for automatic construction of PsychSim models
12 | """
13 | class Domain:
14 | """
15 | Structure for representing model-building information
16 | @ivar idFields: fields representing unique IDs for each record
17 | @type idFields: str[]
18 | @ivar filename: root filename for all model-related files
19 | @type filename: str
20 | @ivar fields: key of mappings from fields of data to model variables
21 | @type fields: strS{->}dict
22 | @ivar data: table of records with relevant variables
23 | @ivar variations: list of dependency variations to explore
24 | @ivar models: list of model name codes to explore
25 | @ivar targets: set of fields to predict
26 | """
27 | def __init__(self,fname,logger=logging.getLogger()):
28 | self.logger = logger.getChild('Domain')
29 | self.filename = fname
30 | # Read variable/field definitions
31 | self.idFields = []
32 | self.fields = {}
33 | self.targets = set()
34 | self.readKey()
35 | # Read input data
36 | self.data = {}
37 | self.readInputData()
38 | # What is the hypothesis space?
39 | self.readVariations()
40 | if self.variations:
41 | varLens = [range(link['range']) for link in self.variations]
42 | self.models = [self.links2model(model) for model in itertools.product(*varLens)]
43 | # What are the previously tested hypotheses?
44 | self.readPredictions()
45 | self.logger.info('|Unmatched| = %d' % (len(self.unmatched())))
46 |
47 | def unmatched(self):
48 | return {ID: record for ID,record in self.data.items() \
49 | if min(map(len,record['__matches__'].values())) == 0}
50 |
51 | def targetHistogram(self,missing=None,data=None):
52 | if data is None:
53 | data = self.data
54 | result = {field: {} for field in self.targets}
55 | for ID,record in data.items():
56 | for field in self.targets:
57 | value = record[field]
58 | if missing is not None and len(value.strip()) == 0:
59 | value = missing
60 | if not value in result[field]:
61 | result[field][value] = set()
62 | result[field][value].add(ID)
63 | return result
64 |
65 | def recordID(self,record):
66 | return ''.join(['%s%s' % (field,record[field]) for field in self.idFields])
67 |
68 | def readPredictions(self,fname=None):
69 | if fname is None:
70 | fname = '%s-predictions.csv' % (self.filename)
71 | if os.path.isfile('%s-predictions.csv' % (args['input'])):
72 | with open(fname,'r') as csvfile:
73 | reader = csv.DictReader(csvfile)
74 | for record in reader:
75 | ID = self.recordID(record)
76 | if not self.data[ID].has_key('__prediction__'):
77 | self.data[ID]['__prediction__'] = {}
78 | self.data[ID]['__probability__'] = {}
79 | for field in self.targets:
80 | self.data[ID]['__prediction__'][field] = {}
81 | self.data[ID]['__probability__'][field] = {}
82 | if not record.has_key('__target__'):
83 | assert len(self.targets) == 1
84 | record['__target__'] = list(self.targets)[0]
85 | field = record['__target__']
86 | links = []
87 | for variation in self.variations:
88 | code = variation['code']
89 | if record[code] == '':
90 | value = None
91 | else:
92 | value = map(int,list(record[code].split(':')))
93 | links.append(variation['domain'].index(value))
94 | model = self.links2model(links)
95 | self.data[ID]['__prediction__'][field][model] = record[field]
96 | if record['P(%s)' % (field)]:
97 | self.data[ID]['__probability__'][field][model] = float(record['P(%s)' % (field)])
98 | for record in self.data.values():
99 | # Missing predictions, so let's enter empty tables
100 | if not record.has_key('__prediction__'):
101 | record['__prediction__'] = {}
102 | record['__probability__'] = {}
103 | for field in self.targets:
104 | record['__prediction__'][field] = {}
105 | record['__probability__'][field] = {}
106 | record['__matches__'] = {}
107 | for field in self.targets:
108 | record['__matches__'][field] = {m for m in record['__prediction__'][field].keys() if record['__prediction__'][field][m] == record[field]}
109 |
110 | def writePredictions(self,fname=None):
111 | if fname is None:
112 | fname = '%s-predictions.csv' % (self.filename)
113 | with open(filename,'w') as csvfile:
114 | fields = None
115 | for ID,record in sorted(self.data.items()):
116 | for field in sorted(self.targets):
117 | for model,prediction in sorted(record['__prediction__'][field].items()):
118 | newRecord = {field: record[field] for field in self.idFields}
119 | newRecord['__target__'] = field
120 | newRecord[field] = prediction
121 | try:
122 | newRecord['P(%s)' % (field)] = record['__probability__'][field][model]
123 | except KeyError:
124 | newRecord['P(%s)' % (field)] = ''
125 | links = self.model2links(model)
126 | for variation in self.variations:
127 | code = variation['code']
128 | value = variation['domain'][links[code]]
129 | if value is None:
130 | newRecord[code] = ''
131 | elif len(value) == 1:
132 | newRecord[code] = '%d' % (value[0])
133 | else:
134 | assert len(value) == 2
135 | newRecord[code] = '%d:%d' % tuple(value)
136 | if fields is None:
137 | fields = sorted(newRecord.keys())
138 | writer = csv.DictWriter(csvfile,fields,extrasaction='ignore')
139 | writer.writeheader()
140 | writer.writerow(newRecord)
141 |
142 | def readDataFile(self,fname):
143 | data = {}
144 | with open(fname) as csvfile:
145 | reader = csv.DictReader(csvfile)
146 | for record in reader:
147 | ID = self.recordID(record)
148 | assert not ID in data,'Duplicate ID: %s' % (ID)
149 | data[ID] = record
150 | return data
151 |
152 | def readInputData(self,fname=None):
153 | if fname is None:
154 | fname = '%s-input.csv' % (self.filename)
155 | if os.path.isfile(fname):
156 | self.data = self.readDataFile(fname)
157 | else:
158 | raw = self.readDataFile('%s-raw.csv' % (self.filename))
159 | self.processData(raw)
160 | fields = sorted(next(iter(self.data.values())).keys())
161 | for row in self.data.values():
162 | assert set(row.keys()) == set(fields)
163 | with open(fname,'w') as csvfile:
164 | writer = csv.DictWriter(csvfile,fields,extrasaction='ignore')
165 | writer.writeheader()
166 | for ID,record in sorted(self.data.items()):
167 | writer.writerow(record)
168 |
169 | def processData(self,raw):
170 | """
171 | Takes in raw data and extracts the relevant fields
172 | """
173 | if isinstance(raw,dict):
174 | raw = raw.values()
175 | logger = self.logger.getChild('processData')
176 | self.data.clear()
177 | for record in raw:
178 | ID = self.recordID(record)
179 | logger.debug('Processing record: %s' % (ID))
180 | newRecord = {field: record[field] for field in self.idFields}
181 | for field,entry in self.fields.items():
182 | if field and not entry['class'] == 'id':
183 | assert field in record,'Missing field %s from record %s' % (field,ID)
184 | assert entry['variable'],'Field %s has no variable' % (field)
185 | newRecord[field] = record[field]
186 | self.data[ID] = newRecord
187 |
188 | def readKey(self,fname=None):
189 | if fname is None:
190 | fname = '%s-key.csv' % (self.filename)
191 | with open(fname) as csvfile:
192 | reader = csv.DictReader(row for row in csvfile if not row.startswith('#'))
193 | for field in reader:
194 | if field['class'] == 'id':
195 | self.idFields.append(field['field'])
196 | else:
197 | self.fields[field['field']] = field
198 | if len(field['variable']) == 0:
199 | field['variable'] = field['field']
200 | if field['target'] == 'yes':
201 | self.targets.add(field['field'])
202 |
203 | def readVariations(self,fname=None):
204 | if fname is None:
205 | fname = '%s-variations.csv' % (self.filename)
206 | self.variations = []
207 | if os.path.isfile(fname):
208 | # Read in modeling variations from file
209 | with open(fname) as csvfile:
210 | reader = csv.DictReader(row for row in csvfile if not row.startswith('#'))
211 | index = 0
212 | for link in reader:
213 | # Read variables involved and possible link values
214 | link['from'] = link['from'].split(';')
215 | link['domain'] = [None if val == 'None' else map(int,val.split(':')) for val in link['domain'].split(';')]
216 | link['range'] = int(link['range'])
217 | link['index'] = index
218 | self.variations.append(link)
219 | # Derive downstream effects
220 | link['effects'] = {}
221 | link['effects'][link['to']] = link['domain'][:]
222 | index += 1
223 | return self.variations
224 |
225 | def links2model(self,links):
226 | return ''.join(['%s%s' % (self.variations[i]['code'],links[i]) \
227 | for i in range(len(self.variations))])
228 |
229 | def noisyOrTree(tree,value):
230 | if isinstance(tree,dict):
231 | return {'if': tree['if'],
232 | True: noisyOrTree(tree[True],value),
233 | False: noisyOrTree(tree[False],value)}
234 | else:
235 | return tree*(1.-value)
236 |
237 | def leaf2matrix(tree,key):
238 | if isinstance(tree,dict):
239 | return {'if': tree['if'],
240 | True: leaf2matrix(tree[True],key),
241 | False: leaf2matrix(tree[False],key)}
242 | else:
243 | prob = 1.-tree
244 | return {'distribution': [(setTrueMatrix(key),prob),(setFalseMatrix(key),1.-prob)]}
245 |
246 | if __name__ == '__main__':
247 | logging.basicConfig(level=logging.ERROR)
248 | parser = ArgumentParser()
249 | parser.add_argument('-d','--debug',default='WARNING',help='Level of logging detail')
250 | # Positional argument for input file
251 | parser.add_argument('input',nargs='?',default='seattle',
252 | help='Root name of CSV files for input/output [default: %(default)s]')
253 | args = vars(parser.parse_args())
254 | # Extract logging level from command-line argument
255 | level = getattr(logging, args['debug'].upper(), None)
256 | if not isinstance(level, int):
257 | raise ValueError('Invalid debug level: %s' % args['debug'])
258 | logging.getLogger().setLevel(level)
259 | domain = Domain(args['input'])
260 |
--------------------------------------------------------------------------------
/psychsim/probability.py:
--------------------------------------------------------------------------------
1 | import math
2 | from xml.dom.minidom import Document,Node
3 |
4 | class Distribution(dict):
5 | """
6 | A probability distribution over hashable objects
7 |
8 | .. warning:: If you make the domain values mutable types, try not to change their values while they are inside the distribution. If you must change a domain value, it is better to first delete the old value, change it, and then re-insert it.
9 | """
10 | epsilon = 1e-8
11 |
12 | def __new__(cls,args=None,rationality=None):
13 | obj = dict.__new__(cls)
14 | obj._domain = {}
15 | return obj
16 |
17 | def __init__(self,args=None,rationality=None):
18 | """
19 | :param args: the initial elements of the probability distribution
20 | :type args: dict
21 | :param rationality: if not ``None``, then use as a rationality parameter in a quantal response over the provided values
22 | :type rationality: float
23 | """
24 | dict.__init__(self)
25 | # self._domain = {}
26 | if isinstance(args,Node):
27 | self.parse(args)
28 | elif isinstance(args,Distribution):
29 | # Some other distribution given
30 | for key in args.domain():
31 | self[key] = args[key]
32 | elif isinstance(args,dict):
33 | if rationality is None:
34 | # Probability dictionary provided
35 | for key,value in args.items():
36 | self[key] = value
37 | else:
38 | # Do quantal response / softmax on table of values
39 | for key,V in args.items():
40 | self[key] = math.exp(rationality*V)
41 | self.normalize()
42 |
43 | def first(self):
44 | """
45 | :returns: the first element in this distribution's domain (most useful if there's only one element)
46 | """
47 | return next(iter(self.domain()))
48 |
49 | def get(self,element):
50 | key = hash(element)
51 | return dict.get(self,key,0.)
52 |
53 | def __getitem__(self,element):
54 | key = hash(element)
55 | return dict.__getitem__(self,key)
56 |
57 | def __setitem__(self,element,value):
58 | """
59 | :param element: the domain element
60 | :param value: the probability to associate with the given key
61 | :type value: float
62 | """
63 | key = hash(element)
64 | self._domain[key] = element
65 | dict.__setitem__(self,key,value)
66 |
67 | def items(self):
68 | for key,value in dict.items(self):
69 | yield self._domain[key],value
70 |
71 | def addProb(self,element,value):
72 | """
73 | Utility method that increases the probability of the given element by the given value
74 | """
75 | key = hash(element)
76 | if key in self._domain:
77 | dict.__setitem__(self,key,dict.__getitem__(self,key)+value)
78 | else:
79 | self._domain[key] = element
80 | dict.__setitem__(self,key,value)
81 | # try:
82 | # self[element] += value
83 | # except KeyError:
84 | # self[element] = value
85 |
86 | def getProb(self,element):
87 | """
88 | Utility method that is almost identical to __getitem__, except that it returns 0 for missing elements, instead of throwing a C{KeyError}
89 | """
90 | try:
91 | return self[element]
92 | except KeyError:
93 | return 0.
94 |
95 | def __delitem__(self,element):
96 | key = hash(element)
97 | dict.__delitem__(self,key)
98 | del self._domain[key]
99 |
100 | def clear(self):
101 | dict.clear(self)
102 | self._domain.clear()
103 |
104 | def replace(self,old,new):
105 | """Replaces on element in the sample space with another. Raises an exception if the original element does not exist, and an exception if the new element already exists (i.e., does not do a merge)
106 | """
107 | prob = self[old]
108 | del self[old]
109 | self[new] = prob
110 |
111 | def domain(self):
112 | """
113 | :returns: the sample space of this probability distribution
114 | :rtype: list
115 | """
116 | return list(self._domain.values())
117 |
118 | def normalize(self):
119 | """Normalizes the distribution so that the sum of values = 1
120 | """
121 | total = sum(self.values())
122 | if abs(total-1.) > self.epsilon:
123 | for key in self.domain():
124 | try:
125 | self[key] /= total
126 | except ZeroDivisionError:
127 | self[key] = 1./float(len(self))
128 |
129 | def expectation(self):
130 | """
131 | :returns: the expected value of this distribution
132 | :rtype: float
133 | """
134 | if len(self) == 1:
135 | # Shortcut if no uncertainty
136 | return self.domain()[0]
137 | else:
138 | total = None
139 | for element in self.domain():
140 | if total is None:
141 | total = element*self[element]
142 | else:
143 | total += element*self[element]
144 | return total
145 |
146 | def __float__(self):
147 | return self.expectation()
148 |
149 | def sample(self,quantify=False):
150 | """
151 | :param quantify: if ``True``, also returns the amount of mass by which the sampling crosssed the threshold of the generated sample's range
152 | :returns: an element from this domain, with a sample probability given by this distribution
153 | """
154 | import random
155 | selection = random.uniform(0.,sum(self.values()))
156 | original = selection
157 | for element in self.domain():
158 | if selection > self[element]:
159 | selection -= self[element]
160 | else:
161 | if quantify:
162 | return element,selection
163 | else:
164 | return element
165 | # We shouldn't get here. But in case of some floating-point weirdness?
166 | return element
167 |
168 | def set(self,element):
169 | """
170 | Reduce distribution to be 100% for the given element
171 | :param element: the element that will be the only one with nonzero probability
172 | """
173 | self.clear()
174 | self[element] = 1.
175 |
176 | def select(self,maximize=False):
177 | """
178 | Reduce distribution to a single element, sampled according to the given distribution
179 | :returns: the probability of the selection made
180 | """
181 | if maximize:
182 | element = self.max()
183 | else:
184 | element = self.sample()
185 | prob = self[element]
186 | self.set(element)
187 | return prob
188 |
189 | def max(self):
190 | """
191 | :returns: the most probable element in this distribution (breaking ties by returning the highest-valued element)
192 | """
193 | return self._domain[max([(dict.__getitem__(self,element),element) for element in self._domain])[1]]
194 |
195 | def entropy(self):
196 | """
197 | :returns: entropy (in bits) of this distribution
198 | """
199 | return sum([-p*math.log2(p) for p in dict.values(self)])
200 |
201 | def __add__(self,other):
202 | if isinstance(other,Distribution):
203 | result = self.__class__()
204 | for me in self.domain():
205 | for you in other.domain():
206 | result.addProb(me+you,self[me]*other[you])
207 | return result
208 | else:
209 | result = self.__class__()
210 | for element in self.domain():
211 | result.addProb(element+other,self[element])
212 | return result
213 |
214 | def __sub__(self,other):
215 | return self + (-other)
216 |
217 | def __neg__(self):
218 | result = self.__class__()
219 | for element in self.domain():
220 | result.addProb(-element,self[element])
221 | return result
222 |
223 | def __mul__(self,other):
224 | if isinstance(other,Distribution):
225 | raise NotImplementedError('Unable to multiply %s by %s.' \
226 | % (self.__class__.__name__,other.__class__.__name__))
227 | else:
228 | result = self.__class__()
229 | for element in self.domain():
230 | result.addProb(element*other,self[element])
231 | return result
232 |
233 | def prune(self,epsilon=1e-8):
234 | elements = self.domain()
235 | i = 0
236 | while i < len(self)-1:
237 | el1 = elements[i]
238 | j = i+1
239 | while j < len(self):
240 | el2 = elements[j]
241 | if abs(el1-el2) < epsilon:
242 | self[el1] += self[el2]
243 | del self[el2]
244 | del elements[j]
245 | else:
246 | j += 1
247 | i += 1
248 |
249 | def __xml__(self):
250 | """
251 | :returns: An XML Document object representing this distribution
252 | """
253 | doc = Document()
254 | root = doc.createElement('distribution')
255 | doc.appendChild(root)
256 | for key,value in self._domain.items():
257 | prob = dict.__getitem__(self,key)
258 | node = doc.createElement('entry')
259 | root.appendChild(node)
260 | node.setAttribute('probability',str(prob))
261 | # if key != hash(value):
262 | # node.setAttribute('key',key)
263 | if isinstance(value,str):
264 | node.setAttribute('key',key)
265 | else:
266 | node.appendChild(self.element2xml(value))
267 | return doc
268 |
269 | def element2xml(self,value):
270 | raise NotImplementedError('Unable to generate XML for distributions over %s' % (value.__class__.__name__))
271 |
272 | def parse(self,element):
273 | """Extracts the distribution from the given XML element
274 | :param element: The XML Element object specifying the distribution
275 | :type element: Element
276 | :returns: This L{Distribution} object"""
277 | assert element.tagName == 'distribution','Unexpected tag %s for %s' \
278 | % (element.tagName,self.__class__.__name__)
279 | self.clear()
280 | node = element.firstChild
281 | while node:
282 | if node.nodeType == node.ELEMENT_NODE:
283 | prob = float(node.getAttribute('probability'))
284 | value = str(node.getAttribute('key'))
285 | if not value:
286 | subNode = node.firstChild
287 | while subNode and subNode.nodeType != subNode.ELEMENT_NODE:
288 | subNode = subNode.nextSibling
289 | value = self.xml2element(None,subNode)
290 | self[value] = prob
291 | # if not key:
292 | # key = str(value)
293 | # dict.__setitem__(self,key,prob)
294 | # self._domain[key] = value
295 | node = node.nextSibling
296 |
297 | def xml2element(self,key,node):
298 | return key
299 |
300 | def sortedString(self):
301 | elements = self.domain()
302 | elements.sort(lambda x,y: cmp(str(x),str(y)))
303 | return '\n'.join(['%4.1f%%\t%s' % (100.*self[el],str(el)) for el in elements])
304 |
305 | def __str__(self):
306 | return '\n'.join(['%d%%\t%s' % (100*self[el],str(el).replace('\n','\n\t'))
307 | for el in self._domain.values()])
308 | # return '\n'.join(map(lambda el: '%d%%\t%s' % (100.*self[el],str(el).replace('\n','\n\t')),self.domain()))
309 |
310 | def __hash__(self):
311 | return hash(str(self))
312 |
313 | def __copy__(self):
314 | return self.__class__(self.__xml__().documentElement)
315 |
316 | def __getstate__(self):
317 | return {el: self[el] for el in self.domain()}
318 |
319 | def __setstate__(self,state):
320 | self.clear()
321 | for el,prob in state.items():
322 | self[el] = prob
323 |
--------------------------------------------------------------------------------
/psychsim/pwl/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Class and function definitions for PieceWise Linear (PWL) representations
3 | """
4 |
5 | from psychsim.pwl.keys import *
6 | from psychsim.pwl.vector import *
7 | from psychsim.pwl.matrix import *
8 | from psychsim.pwl.plane import *
9 | from psychsim.pwl.tree import *
10 | from psychsim.pwl.state import *
11 |
--------------------------------------------------------------------------------
/psychsim/pwl/keys.py:
--------------------------------------------------------------------------------
1 |
2 | # Special keys
3 | CONSTANT = '' #: Special key for column whose value is always 1
4 | VALUE = '__VALUE__'
5 | WORLD = '__WORLD__'
6 | ACTION = '__ACTION__'
7 | REWARD = '__REWARD__'
8 | MODEL = '__MODEL__'
9 | TURN = '__TURN__'
10 |
11 | def stateKey(name,feature,future=False):
12 | """
13 | :param future: if C{True}, then this refers to the projected value of this feature (default is C{False})
14 | :type future: bool
15 | :returns: a key representation of a given entity's state feature
16 | :rtype: str
17 | """
18 | assert isinstance(future,bool),'Future flag is non-boolean: %s' % (future)
19 | if future:
20 | return stateKey(name,feature)+"'"
21 | elif name is None:
22 | return feature
23 | else:
24 | return '%s\'s %s' % (name,feature)
25 | TERMINATED = stateKey(WORLD,'__END__')
26 |
27 | def isStateKey(key):
28 | """
29 | :returns: C{True} iff this key refers to a state feature
30 | :rtype: bool
31 | """
32 | return '\'s ' in key
33 |
34 | def state2feature(key):
35 | """
36 | :returns: the feature string from the given key
37 | :rtype: str
38 | """
39 | index = key.find("'")
40 | if index < 0:
41 | return key
42 | else:
43 | return key[index+3:]
44 |
45 | def state2agent(key):
46 | """
47 | :returns: the agent name from the given key
48 | :rtype: str
49 | """
50 | index = key.find("'")
51 | if index < 0:
52 | return None
53 | else:
54 | return key[:index]
55 |
56 | def state2tuple(key):
57 | """
58 | :returns: the separated agent name and feature from the given key
59 | :rtype: (str,str)
60 | """
61 | index = key.find("'")
62 | if index < 0:
63 | return None
64 | else:
65 | return key[:index],key[index+3:]
66 |
67 | def makePresent(key):
68 | """
69 | :returns: a reference to the given state features' current value
70 | :rtype: str
71 | """
72 | if isinstance(key,set):
73 | return {makePresent(k) for k in key}
74 | elif key[-1] == "'":
75 | return key[:-1]
76 | else:
77 | return key
78 |
79 | def makeFuture(key):
80 | """
81 | :returns: a reference to the given state features' projected future value
82 | :rtype: str
83 | """
84 | if key[-1] == "'":
85 | raise ValueError('%s is already a future key' % (key))
86 | else:
87 | return key+"'"
88 |
89 | def isFuture(key):
90 | return len(key) > 0 and key[-1] == "'"
91 |
92 | def turnKey(name):
93 | return stateKey(name,TURN)
94 |
95 | def isTurnKey(key):
96 | return key[-(len(TURN)+3):] == '\'s %s' % (TURN)
97 |
98 | def turn2name(key):
99 | return key[:-(len(TURN)+3)]
100 |
101 | def actionFieldKey(feature):
102 | return '__action__%s__' % (feature)
103 |
104 | def actionKey(name,future=False):
105 | return stateKey(name,ACTION,future)
106 |
107 | def isActionKey(key):
108 | return isStateKey(key) and state2feature(key) == ACTION
109 |
110 | def modelKey(name):
111 | return stateKey(name,MODEL)
112 |
113 | def isModelKey(key):
114 | return key[-(len(MODEL)+3):] == '\'s %s' % (MODEL)
115 |
116 | def model2name(key):
117 | return key[:-(len(MODEL)+3)]
118 |
119 | def isSpecialKey(key):
120 | """
121 | :return: True iff the given key is a state key and its feature is a reserved name (e.g., for a turn, model, reward, etc)
122 | """
123 | return isStateKey(key) and state2feature(key)[:2] == '__'
124 |
125 | def binaryKey(subj,obj,relation):
126 | return '%s %s -- %s' % (subj,relation,obj)
127 |
128 | def isBinaryKey(key):
129 | return ' -- ' in key
130 |
131 | def key2relation(key):
132 | sides = key.split(' -- ')
133 | first = sides[0].split()
134 | return {'subject': ' '.join(first[:-1]),
135 | 'object': sides[1],
136 | 'relation': first[-1]}
137 |
138 | def likesKey(subj,obj):
139 | return binaryKey(subj,obj,'likes')
140 |
141 | def isLikesKey(key):
142 | return ' likes -- ' in key
143 |
144 | def rewardKey(name,future=False):
145 | return stateKey(name,REWARD,future)
146 |
147 | def isRewardKey(key):
148 | return isStateKey(key) and state2feature(key) == REWARD
149 |
150 | def beliefKey(name,key):
151 | return '%s(%s)' % (name,key)
152 |
153 | def isBeliefKey(key):
154 | return '(' in key
155 |
156 | def belief2believer(key):
157 | return key[:key.index('(')]
158 |
159 | def belief2key(key):
160 | return key[key.index('(')+1:-1]
161 |
162 | def escapeKey(key):
163 | """
164 | :returns: filename-ready version of the key
165 | """
166 | if not isinstance(key,str):
167 | key = str(key)
168 | if isBeliefKey(key):
169 | believer = belief2believer(key)
170 | subkey = belief2key(key)
171 | return 'Belief(%s)Of%s' % (escapeKey(subkey),believer)
172 | future = isFuture(key)
173 | if future:
174 | key = makePresent(key)
175 | if isStateKey(key):
176 | agent = state2agent(key)
177 | if agent == WORLD:
178 | name = state2feature(key)
179 | else:
180 | name = '%sOf%s' % (state2feature(key),agent)
181 | else:
182 | name = key
183 | return name.replace(' ','')
184 |
--------------------------------------------------------------------------------
/psychsim/reward.py:
--------------------------------------------------------------------------------
1 | from psychsim.pwl import *
2 |
3 | def maximizeFeature(key,agent):
4 | return KeyedTree(setToFeatureMatrix(rewardKey(agent),key,1.))
5 |
6 | def minimizeFeature(key,agent):
7 | return KeyedTree(setToFeatureMatrix(rewardKey(agent),key,-1.))
8 |
9 | def achieveFeatureValue(key,value,agent):
10 | return makeTree({'if': equalRow(key,value),
11 | True: setToConstantMatrix(rewardKey(agent),1.),
12 | False: setToConstantMatrix(rewardKey(agent),0.)})
13 |
14 | def achieveGoal(key,agent):
15 | return makeTree({'if': trueRow(key),
16 | True: setToConstantMatrix(rewardKey(agent),1.),
17 | False: setToConstantMatrix(rewardKey(agent),0.),})
18 |
19 | def minimizeDifference(key1,key2,agent):
20 | return makeTree({'if': greaterThanRow(key1, key2),
21 | True: dynamicsMatrix(rewardKey(agent),{key1: -1.,key2: 1.}),
22 | False: dynamicsMatrix(rewardKey(agent),{key1: 1.,key2: -1.})})
23 |
--------------------------------------------------------------------------------
/psychsim/shell.py:
--------------------------------------------------------------------------------
1 | from argparse import ArgumentParser
2 | import sys
3 |
4 | from psychsim.world import World
5 |
6 | def printHelp():
7 | keys = commands.keys()
8 | keys.sort()
9 | for cmd in keys:
10 | print('%s\t%s' % (cmd,commands[cmd]['help']))
11 |
12 | def loadScenario(filename):
13 | global world
14 | world = World(filename)
15 |
16 | def step(actions=None):
17 | outcome = world.step(actions)
18 | world.explain(outcome,args['debug'])
19 | world.state.select()
20 |
21 | def act(label):
22 | assert len(world.state) == 1,'Unable to work with uncertain state'
23 | vector = world.state.domain()[0]
24 | assert len(world.next(vector)) == 1,'Forcing actions allowed only in serial execution'
25 | agent = world.agents[world.next(vector)[0]]
26 | for action in agent.getActions(vector):
27 | if str(action) == label:
28 | step({agent.name: action})
29 | break
30 | else:
31 | raise AssertionError('%s has no legal action: %s' % (agent.name,label))
32 |
33 | def choose():
34 | assert len(world.state) == 1,'Unable to work with uncertain state'
35 | vector = world.state.domain()[0]
36 | assert len(world.next(vector)) == 1,'Choosing actions allowed only in serial execution'
37 | choice = {}
38 | for name in world.next(vector):
39 | agent = world.agents[name]
40 | actions = list(agent.getActions(vector))
41 | actions.sort()
42 | for index in range(len(actions)):
43 | print('%d) %s' % (index,actions[index]))
44 | print('Choose action: ',)
45 | choice[name] = actions[int(stream.readline())]
46 | print()
47 | step(choice)
48 |
49 | if __name__ == '__main__':
50 | parser = ArgumentParser()
51 | # Optional argument that sets the filename for a command script
52 | parser.add_argument('-i','--input',action='store',
53 | dest='input',default=None,
54 | help='input file [default: %(default)s]')
55 | # Optional argument that sets the filename for the scenario
56 | parser.add_argument('-s','--scenario',action='store',
57 | dest='scenario',default=None,
58 | help='scenario file [default: %(default)s]')
59 | # Optional argument that sets the level of explanations when running the simulation
60 | parser.add_argument('-d',action='store',
61 | dest='debug',type=int,default=1,
62 | help='level of explanation detail [default: %(default)s]')
63 | args = vars(parser.parse_args())
64 | if args['input'] is None:
65 | stream = sys.stdin
66 | else:
67 | stream = open(args['input'],'r')
68 | if args['scenario'] is None:
69 | world = None
70 | else:
71 | loadScenario(args['scenario'])
72 |
73 | commands = {'quit': {'function': sys.exit,
74 | 'args': [],'state': False,'world': False,
75 | 'help': 'Exit the shell'},
76 | 'help': {'function': printHelp,
77 | 'args': [],'state': False,'world': False,
78 | 'help': 'Print this message'},
79 | 'load': {'function': loadScenario,
80 | 'args': ['filename'],'state': True,'world': False,
81 | 'help': 'Load a PsychSim Scenario'},
82 | 'step': {'function': step,
83 | 'args': [],'state': True,'world': True,
84 | 'help': 'Perform a simulation step'},
85 | 'choose': {'function': choose,
86 | 'args': [],'state': True,'world': True,
87 | 'help': 'Make an interactive choice for the agent'},
88 | 'act': {'function': act,
89 | 'args': ['action'],'state': True,'world': True,
90 | 'help': 'Force agent to take specified action'},
91 | }
92 | while True:
93 | prompt = '> '
94 | if world:
95 | prompt = '%s%s' % (','.join(world.next()),prompt)
96 | print(prompt,)
97 | line = stream.readline().strip()
98 | print()
99 | elements = line.split()
100 | if elements:
101 | cmd = elements[0]
102 | if commands.has_key(cmd):
103 | if len(commands[cmd]['args']) == 1:
104 | params = (' '.join(elements[1:]),)
105 | else:
106 | params = elements[1:]
107 | if len(params) != len(commands[cmd]['args']):
108 | print('Usage: %s %s' % (cmd,' '.join(commands[cmd]['args'])))
109 | elif commands[cmd]['world'] and world is None:
110 | print('Must load scenario before performing command "%s"' % (cmd))
111 | else:
112 | try:
113 | apply(commands[cmd]['function'],params)
114 | if commands[cmd]['state']:
115 | world.printState()
116 | except AssertionError(msg):
117 | print('Error: %s' % (msg))
118 | else:
119 | print('Unknown command: "%s"' % (cmd))
120 | print(elements)
121 | print()
122 |
--------------------------------------------------------------------------------
/psychsim/test/testPWL.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import random
3 |
4 | from psychsim.pwl import *
5 |
6 | class TestPWL(unittest.TestCase):
7 |
8 | def makeVector(self,size=8,gap=0.):
9 | """
10 | @param size: the maximum length of the returned vector (default is 8)
11 | @param gap: the probability that there will be gaps in the keys (default is 0)
12 | @rtype: L{KeyedVector}
13 | """
14 | vector = KeyedVector()
15 | for index in range(size):
16 | if random.random() > gap:
17 | key = chr(65+index)
18 | vector[key] = random.random()
19 | return vector
20 |
21 | def makeMatrix(self,rows=8,cols=8,rowgap=0.,colgap=0.):
22 | """
23 | @param rows: the maximum rows in the returned matrix (default is 1)
24 | @param cols: the maximum columns in the returned matrix (default is 8)
25 | @param rowgap: the probability that there will be gaps in the row keys (default is 0)
26 | @param colgap: the probability that there will be gaps in the column keys (default is 0)
27 | @rtype: L{KeyedMatrix}
28 | """
29 | matrix = KeyedMatrix()
30 | for index in range(rows):
31 | if random.random() > rowgap:
32 | key = chr(65+index)
33 | matrix[key] = self.makeVector(cols,colgap)
34 | return matrix
35 |
36 | def makePlane(self,size=8,gap=0.,comparison=1):
37 | """
38 | @param size: the maximum length of the plane vector (default is 8)
39 | @param gap: the probability that there will be gaps in the column keys (default is 0)
40 | @param comparison: if 1, value must be above hyperplane; if -1, below; if 0, equal (default is 1)
41 | @rtype: L{KeyedPlane}
42 | """
43 | return KeyedPlane(self.makeVector(size,gap),random.random(),comparison)
44 |
45 | def makeTree(self,rows=8,cols=8,planecols=8,depth=3,rowgap=0.,colgap=0.,planegap=0.):
46 | """
47 | @param rows: the maximum rows in the leaf matrices (default is 1)
48 | @param cols: the maximum columns in the leaf matrices (default is 8)
49 | @param planecols: the maximum columns in the hyperplanes (default is 4)
50 | @param depth: the depth of the returned tree (default is 3)
51 | @param rowgap: the probability that there will be gaps in the row keys (default is 0)
52 | @param colgap: the probability that there will be gaps in the column keys (default is 0)
53 | @param colgap: the probability that there will be gaps in the hyperplane keys (default is 0)
54 | @rtype: L{KeyedTree}
55 | """
56 | root = KeyedTree()
57 | oldEnvelope = [root]
58 | currentDepth = 0
59 | # Branch until we reach desired depth
60 | while currentDepth < depth:
61 | newEnvelope = []
62 | for node in oldEnvelope:
63 | trueTree = KeyedTree()
64 | falseTree = KeyedTree()
65 | newEnvelope += [trueTree,falseTree]
66 | node.makeBranch(self.makePlane(planecols,planegap),trueTree,falseTree)
67 | oldEnvelope = newEnvelope
68 | currentDepth += 1
69 | # Make leaves
70 | for node in oldEnvelope:
71 | node.makeLeaf(self.makeMatrix(rows,cols,rowgap,colgap))
72 | return root
73 |
74 | def testVectorAddition(self):
75 | for iteration in range(100):
76 | v1 = self.makeVector(gap=0.1)
77 | v2 = self.makeVector(gap=0.2)
78 | total = v1+v2
79 | for key in v1.keys():
80 | self.assertTrue(total.has_key(key))
81 | for key in v2.keys():
82 | self.assertTrue(total.has_key(key))
83 | for key in total.keys():
84 | self.assertTrue(v1.has_key(key) or v2.has_key(key))
85 | if v1.has_key(key) and v2.has_key(key):
86 | self.assertAlmostEqual(v1[key]+v2[key],total[key],8)
87 | elif v1.has_key(key):
88 | self.assertAlmostEqual(v1[key],total[key],8)
89 | else: # v2.has_key(key)
90 | self.assertAlmostEqual(v2[key],total[key],8)
91 |
92 | def testMatrixMultiplication(self):
93 | # Matrix * Matrix
94 | for iteration in range(100):
95 | m1 = self.makeMatrix()
96 | m2 = self.makeMatrix()
97 | mProduct = m1*m2
98 | for testIteration in range(100):
99 | v0 = self.makeVector()
100 | v1 = m2*v0
101 | v2 = m1*v1
102 | product = mProduct*v0
103 | self.assertEqual(v2.keys(),product.keys())
104 | for key in product.keys():
105 | self.assertAlmostEqual(product[key],v2[key],8)
106 |
107 | def DONTtestTreeAddition(self):
108 | for iteration in range(100):
109 | t1 = self.makeTree(colgap=0.75,planegap=0.75)
110 | t2 = self.makeTree(colgap=0.75,planegap=0.75)
111 | tTotal = t1 + t2
112 | for testIteration in range(100):
113 | v = self.makeVector()
114 | total = t1[v]*v + t2[v]*v
115 | self.assertAlmostEqual(tTotal[v]*v,total,8)
116 |
117 | def testTreeMultiplication(self):
118 | for iteration in range(100):
119 | t1 = self.makeTree(rows=2,cols=2,planecols=2,depth=1)
120 | t2 = self.makeTree(rows=2,cols=2,planecols=2,depth=0)
121 | tProduct = t1*t2
122 | for testIteration in range(100):
123 | v1 = self.makeVector()
124 | v2 = t2[v1]*v1
125 | v3 = t1[v2]*v2
126 | self.assertEqual(t1.branch.evaluate(v2),tProduct.branch.evaluate(v1))
127 | product1 = tProduct[v1]*v1
128 | for key in v3.keys():
129 | self.assertAlmostEqual(product1[key],v3[key],8)
130 |
131 | if __name__ == '__main__':
132 | unittest.main()
133 |
--------------------------------------------------------------------------------
/psychsim/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/tools/__init__.py
--------------------------------------------------------------------------------
/psychsim/tools/gdelt.py:
--------------------------------------------------------------------------------
1 | """
2 | Module for parsing CAMEO-encoded GDELT event databases (in CSV format)
3 | Run with -h to get usage information
4 | """
5 | import argparse
6 | import calendar
7 | from datetime import date,timedelta,MINYEAR,MAXYEAR
8 | import fileinput
9 | import os.path
10 | import sys
11 | import time
12 |
13 | from psychsim.world import World
14 | from psychsim.action import Action
15 | from psychsim.agent import Agent
16 |
17 | def parseCAMEO():
18 | """
19 | Extracts mapping and reverse mapping of CAMEO codes to/from event labels
20 | @rtype: dict,dict
21 | """
22 | cameo = {}
23 | oemac = {}
24 | for line in fileinput.input(os.path.join(os.path.dirname(__file__),'cameo.txt')):
25 | elements = line.split(':')
26 | if len(elements) == 2:
27 | code = int(elements[0])
28 | event = elements[1].strip()
29 | cameo[code] = event
30 | oemac[event] = code
31 | return cameo,oemac
32 |
33 | # Global variables for reference, CAMEO and GDELT formats
34 | cameo,oemac = parseCAMEO()
35 | headings = 'GLOBALEVENTID SQLDATE MonthYear Year FractionDate Actor1Code Actor1Name Actor1CountryCode Actor1KnownGroupCode Actor1EthnicCode Actor1Religion1Code Actor1Religion2Code Actor1Type1Code Actor1Type2Code Actor1Type3Code Actor2Code Actor2Name Actor2CountryCode Actor2KnownGroupCode Actor2EthnicCode Actor2Religion1Code Actor2Religion2Code Actor2Type1Code Actor2Type2Code Actor2Type3Code IsRootEvent EventCode EventBaseCode EventRootCode QuadClass GoldsteinScale NumMentions NumSources NumArticles AvgTone Actor1Geo_Type Actor1Geo_FullName Actor1Geo_CountryCode Actor1Geo_ADM1Code Actor1Geo_Lat Actor1Geo_Long Actor1Geo_FeatureID Actor2Geo_Type Actor2Geo_FullName Actor2Geo_CountryCode Actor2Geo_ADM1Code Actor2Geo_Lat Actor2Geo_Long Actor2Geo_FeatureID ActionGeo_Type ActionGeo_FullName ActionGeo_CountryCode ActionGeo_ADM1Code ActionGeo_Lat ActionGeo_Long ActionGeo_FeatureID DATEADDED'.split('\t')
36 |
37 | intHeadings = {'GLOBALEVENTID','SQLDATE','MonthYear','Year',
38 | 'IsRootEvent','EventCode','EventBaseCode','EventRootCode','QuadClass',
39 | 'NumMentions','NumSources','NumArticles',
40 | 'Actor1Geo_Type','Actor1Geo_FeatureID','Actor2Geo_Type','Actor2Geo_FeatureID',
41 | 'ActionGeo_Type','ActionGeo_FeatureID','DATEADDED'}
42 | floatHeadings = {'FractionDate','GoldsteinScale','AvgTone',
43 | 'Actor1Geo_Lat','Actor1Geo_Long','Actor2Geo_Lat','Actor2Geo_Long',
44 | 'ActionGeo_Lat','ActionGeo_Long'}
45 |
46 | def matchActor(name,targets):
47 | """
48 | Determines whether a given actor name is one of a given list of targets
49 | @type name: str
50 | @type targets: str[]
51 | @rtype: bool
52 | """
53 | if len(targets) == 0:
54 | # Nobody given, assume everybody is a target
55 | return True
56 | if name is None:
57 | # There's nobody to match
58 | return False
59 | for actor in targets:
60 | if name[:len(actor)] == actor:
61 | return True
62 | else:
63 | return False
64 |
65 | def parseGDELT(fname,targets=[]):
66 | """
67 | Extracts events from a single GDELT CSV file
68 | """
69 | # Parse filename
70 | root,ext = os.path.splitext(os.path.basename(fname))
71 | assert ext == '.csv','CSV file expected instead of %s' % (fname)
72 | if len(root) == 4:
73 | year = int(root)
74 | month = 0
75 | else:
76 | assert len(root) == 6,'Filename not in YYYY.csv or YYYYMM.csv format: %s' % (fname)
77 | year = int(root[:4])
78 | month = int(root[4:])
79 | # Initialize storage
80 | result = {'month': month,
81 | 'year': year,
82 | 'agents': {},
83 | 'matrix': {},
84 | 'calendar': {},
85 | }
86 | today = None
87 | start = time.time()
88 | lines = 0
89 | for line in fileinput.input(fname):
90 | lines += 1
91 | # Extract the event fields
92 | elements = map(lambda x: x.strip(),line.split('\t'))
93 | event = {}
94 | for index in range(len(elements)):
95 | if len(elements[index]) == 0:
96 | event[headings[index]] = None
97 | elif headings[index] in intHeadings:
98 | event[headings[index]] = int(elements[index])
99 | elif headings[index] in floatHeadings:
100 | event[headings[index]] = float(elements[index])
101 | else:
102 | event[headings[index]] = elements[index].strip()
103 | if event['SQLDATE'] != today:
104 | today = event['SQLDATE']
105 | events = []
106 | result['calendar'][event['SQLDATE']] = events
107 | print >> sys.stderr,today
108 | if event['Actor1Code'] is None:
109 | # No actor?
110 | event['Actor1Code'] = 'Unknown'
111 | if lines%10000 == 0 and events:
112 | print >> sys.stderr,'\t%dK (%d events,%d agents)' % \
113 | (lines/1000,len(events),len(result['agents']))
114 | if matchActor(event['Actor1Code'],targets) or \
115 | matchActor(event['Actor2Code'],targets):
116 | # Event matching our target
117 | events.append(event)
118 | if not result['agents'].has_key(event['Actor1Code']):
119 | agent = Agent(event['Actor1Code'])
120 | result['agents'][agent.name] = agent
121 | event['action'] = Action({'subject': event['Actor1Code'],
122 | 'verb': cameo[event['EventCode']]})
123 | if event['Actor2Code']:
124 | if not result['agents'].has_key(event['Actor2Code']):
125 | agent = Agent(event['Actor2Code'])
126 | result['agents'][agent.name] = agent
127 | event['action']['object'] = event['Actor2Code']
128 | # Update relationship matrix
129 | if event['Actor1Code'] < event['Actor2Code']:
130 | key = '%s,%s' % (event['Actor1Code'],event['Actor2Code'])
131 | else:
132 | key = '%s,%s' % (event['Actor2Code'],event['Actor1Code'])
133 | try:
134 | result['matrix'][key].append(event)
135 | except KeyError:
136 | result['matrix'][key] = [event]
137 | return result
138 |
139 | if __name__ == '__main__':
140 | # Command-line arguments
141 | parser = argparse.ArgumentParser(description='Create a PsychSim World from GDELT database files')
142 | parser.add_argument('files',metavar='FILE',nargs='+',help='names of GDELT CSV files')
143 | parser.add_argument('--actor',metavar='ACTOR',action='append',help='KEDS actor codes to search for')
144 | args = parser.parse_args()
145 | # Initialization
146 | world = World()
147 | count = 0
148 | eventCalendar = {}
149 | events = []
150 | matrix = {}
151 | earliest = MAXYEAR
152 | latest = MINYEAR
153 | # Get cracking
154 | for fname in args.files:
155 | result = parseGDELT(fname,args.actor)
156 | assert result.has_key('calendar')
157 | earliest = min(earliest,result['year'])
158 | latest = max(latest,result['year'])
159 | # Add relationships to the matrix
160 | for key,actions in result['matrix'].items():
161 | try:
162 | matrix[key] += actions
163 | except KeyError:
164 | matrix[key] = actions
165 | # Add new agents to the world
166 | for agent in result['agents'].values():
167 | if not world.has_agent(agent):
168 | world.addAgent(agent)
169 | # Add events to the calendar
170 | for when,day in result['calendar'].items():
171 | try:
172 | eventCalendar[when] += day
173 | except KeyError:
174 | eventCalendar[when] = day
175 | events += day
176 |
177 | # Print summary
178 | print >> sys.stderr,format(len(world.agents),',d'),'agents'
179 | print >> sys.stderr,format(len(events),',d'),'events'
180 | print >> sys.stderr,len(eventCalendar),'days'
181 |
182 | # Print calendar
183 | pairs = matrix.keys()
184 | pairs.sort()
185 | for pair in pairs:
186 | today = None
187 | print pair
188 | for event in matrix[pair]:
189 | if event['SQLDATE'] != today:
190 | print '\t',event['SQLDATE']
191 | today = event['SQLDATE']
192 | print '\t\t%s %4.2f %d/%d/%d %4.2f' % (event['action'],event['GoldsteinScale'],event['NumMentions'],event['NumSources'],event['NumArticles'],event['AvgTone'])
193 |
--------------------------------------------------------------------------------
/psychsim/tools/graph.py:
--------------------------------------------------------------------------------
1 | """
2 | Module that generates a PNG displaying the graph of mental models used by agents in a given world
3 | @author: Camille Barot
4 | """
5 | import pydot
6 |
7 | def createModelGraph(world, filename="modelgraph"):
8 | '''
9 | This method creates a graph of the models used by the agents
10 | '''
11 | modelgraph = pydot.Dot('Model graph', graph_type='digraph')
12 | for agent in world.agents.values():
13 | for my_model in list(agent.models.keys()):
14 | name = agent.name + ' ' + str(my_model)
15 | node_src = pydot.Node(name)
16 | if node_src.get_name() not in modelgraph.obj_dict['nodes'].keys():
17 | modelgraph.add_node(node_src)
18 | if world.agents[agent.name].models[my_model].has_key('beliefs'):
19 | beliefs = world.agents[agent.name].models[my_model]['beliefs']
20 | if beliefs != True:
21 | for belief,pct in beliefs.items():
22 | split = belief.replace('\t','\n').split('\n')
23 | for line in split:
24 | if '_model' in line:
25 | modeled_agent_name = line.split("'")[0]
26 | model_num = int(float(line.split(')')[-1].strip().split(': ')[1]))
27 | model_key = world.agents[modeled_agent_name].index2model(model_num)
28 | name = modeled_agent_name + ' ' + model_key
29 | node_dst = pydot.Node(name)
30 | if node_dst.get_name() not in modelgraph.obj_dict['nodes'].keys():
31 | modelgraph.add_node(node_dst)
32 | if not modelgraph.get_edge(node_src.get_name(), node_dst.get_name()):
33 | edge = pydot.Edge(node_src, node_dst, label=str(pct))
34 | modelgraph.add_edge(edge)
35 | else:
36 | old_edge = modelgraph.get_edge(node_src.get_name(), node_dst.get_name())[0]
37 | old_pct = old_edge.get_attributes()['label']
38 | modelgraph.del_edge(node_src, node_dst)
39 | edge = pydot.Edge(node_src, node_dst, label=str(float(old_pct)+pct))
40 | modelgraph.add_edge(edge)
41 |
42 | modelgraph.write_png(filename+'.png')
43 |
--------------------------------------------------------------------------------
/psychsim/ui/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/ui/__init__.py
--------------------------------------------------------------------------------
/psychsim/ui/diagram.py:
--------------------------------------------------------------------------------
1 | import random
2 | from xml.dom.minidom import Document,Node
3 | from PyQt5.QtWidgets import *
4 | from PyQt5.QtGui import *
5 |
6 | class Diagram:
7 | """
8 | Information for a diagram view of a L{psychsim.world.World}
9 | """
10 | def __init__(self,args=None):
11 | self.x = {}
12 | self.y = {}
13 | self.color = {}
14 | if isinstance(args,Node):
15 | self.parse(args)
16 |
17 | def getX(self,key):
18 | try:
19 | return self.x[key]
20 | except KeyError:
21 | return None
22 |
23 | def getY(self,key):
24 | try:
25 | return self.y[key]
26 | except KeyError:
27 | return None
28 |
29 | def getColor(self,key):
30 | """
31 | @warning: if no color exists, assigns a random color
32 | """
33 | if not key in self.color:
34 | self.color[key] = QColor(random.choice(QColor.colorNames()))
35 | return self.color[key]
36 |
37 | def setColor(self,key,value):
38 | if not isinstance(value,QColor):
39 | value = QColor(value)
40 | self.color[key] = value
41 |
42 | def clear(self):
43 | self.x.clear()
44 | self.y.clear()
45 |
46 | def __xml__(self):
47 | doc = Document()
48 | root = doc.createElement('diagram')
49 | for key,value in self.x.items():
50 | node = doc.createElement('x')
51 | node.setAttribute('key',key)
52 | node.appendChild(doc.createTextNode(str(value)))
53 | root.appendChild(node)
54 | for key,value in self.y.items():
55 | node = doc.createElement('y')
56 | node.setAttribute('key',key)
57 | node.appendChild(doc.createTextNode(str(value)))
58 | root.appendChild(node)
59 | for key,value in self.color.items():
60 | node = doc.createElement('color')
61 | if key:
62 | node.setAttribute('key',key)
63 | node.appendChild(doc.createTextNode(str(value.name())))
64 | root.appendChild(node)
65 | doc.appendChild(root)
66 | return doc
67 |
68 | def parse(self,root):
69 | assert root.tagName == 'diagram'
70 | node = root.firstChild
71 | while node:
72 | if node.nodeType == node.ELEMENT_NODE:
73 | key = str(node.getAttribute('key'))
74 | if not key:
75 | key = None
76 | if node.tagName == 'x':
77 | self.x[key] = float(node.firstChild.data)
78 | elif node.tagName == 'y':
79 | self.y[key] = float(node.firstChild.data)
80 | elif node.tagName == 'color':
81 | self.setColor(key,str(node.firstChild.data).strip())
82 | else:
83 | raise NameError('Unknown element %s when parsing %s' % \
84 | (node.tagName,self.__class__.__name__))
85 | node = node.nextSibling
86 |
--------------------------------------------------------------------------------
/psychsim/ui/mainwindow.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'mainwindow.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.10.1
6 | #
7 | # WARNING! All changes made in this file will be lost!
8 |
9 | from PyQt5 import QtCore, QtGui, QtWidgets
10 |
11 | class Ui_MainWindow(object):
12 | def setupUi(self, MainWindow):
13 | MainWindow.setObjectName("MainWindow")
14 | MainWindow.resize(800, 600)
15 | icon = QtGui.QIcon()
16 | icon.addPixmap(QtGui.QPixmap(":/psychsim.gif"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
17 | MainWindow.setWindowIcon(icon)
18 | MainWindow.setUnifiedTitleAndToolBarOnMac(True)
19 | self.centralwidget = QtWidgets.QWidget(MainWindow)
20 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
21 | sizePolicy.setHorizontalStretch(0)
22 | sizePolicy.setVerticalStretch(0)
23 | sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
24 | self.centralwidget.setSizePolicy(sizePolicy)
25 | self.centralwidget.setObjectName("centralwidget")
26 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
27 | self.horizontalLayout.setObjectName("horizontalLayout")
28 | self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
29 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
30 | sizePolicy.setHorizontalStretch(0)
31 | sizePolicy.setVerticalStretch(0)
32 | sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
33 | self.graphicsView.setSizePolicy(sizePolicy)
34 | self.graphicsView.setRenderHints(QtGui.QPainter.Antialiasing|QtGui.QPainter.TextAntialiasing)
35 | self.graphicsView.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
36 | self.graphicsView.setObjectName("graphicsView")
37 | self.horizontalLayout.addWidget(self.graphicsView)
38 | MainWindow.setCentralWidget(self.centralwidget)
39 | self.menubar = QtWidgets.QMenuBar(MainWindow)
40 | self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
41 | self.menubar.setObjectName("menubar")
42 | self.menuFile = QtWidgets.QMenu(self.menubar)
43 | self.menuFile.setObjectName("menuFile")
44 | self.menuEdit = QtWidgets.QMenu(self.menubar)
45 | self.menuEdit.setObjectName("menuEdit")
46 | self.menuView = QtWidgets.QMenu(self.menubar)
47 | self.menuView.setObjectName("menuView")
48 | self.menuTools = QtWidgets.QMenu(self.menubar)
49 | self.menuTools.setObjectName("menuTools")
50 | MainWindow.setMenuBar(self.menubar)
51 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
52 | self.statusbar.setObjectName("statusbar")
53 | MainWindow.setStatusBar(self.statusbar)
54 | self.actionNew = QtWidgets.QAction(MainWindow)
55 | self.actionNew.setEnabled(False)
56 | self.actionNew.setObjectName("actionNew")
57 | self.actionOpen = QtWidgets.QAction(MainWindow)
58 | self.actionOpen.setObjectName("actionOpen")
59 | self.actionRecent_Files = QtWidgets.QAction(MainWindow)
60 | self.actionRecent_Files.setEnabled(False)
61 | self.actionRecent_Files.setObjectName("actionRecent_Files")
62 | self.actionUndo = QtWidgets.QAction(MainWindow)
63 | self.actionUndo.setEnabled(False)
64 | self.actionUndo.setObjectName("actionUndo")
65 | self.actionRedo = QtWidgets.QAction(MainWindow)
66 | self.actionRedo.setEnabled(False)
67 | self.actionRedo.setObjectName("actionRedo")
68 | self.actionCut = QtWidgets.QAction(MainWindow)
69 | self.actionCut.setEnabled(False)
70 | self.actionCut.setObjectName("actionCut")
71 | self.actionCopy = QtWidgets.QAction(MainWindow)
72 | self.actionCopy.setEnabled(False)
73 | self.actionCopy.setObjectName("actionCopy")
74 | self.actionPaste = QtWidgets.QAction(MainWindow)
75 | self.actionPaste.setEnabled(False)
76 | self.actionPaste.setObjectName("actionPaste")
77 | self.actionSave = QtWidgets.QAction(MainWindow)
78 | self.actionSave.setEnabled(False)
79 | self.actionSave.setObjectName("actionSave")
80 | self.actionSave_As = QtWidgets.QAction(MainWindow)
81 | self.actionSave_As.setEnabled(False)
82 | self.actionSave_As.setObjectName("actionSave_As")
83 | self.actionClose = QtWidgets.QAction(MainWindow)
84 | self.actionClose.setEnabled(False)
85 | self.actionClose.setObjectName("actionClose")
86 | self.actionQuit = QtWidgets.QAction(MainWindow)
87 | self.actionQuit.setObjectName("actionQuit")
88 | self.groupColor = QtWidgets.QActionGroup(MainWindow)
89 | self.actionAgent = QtWidgets.QAction(MainWindow)
90 | self.actionAgent.setCheckable(True)
91 | self.actionAgent.setChecked(True)
92 | self.actionAgent.setObjectName("actionAgent")
93 | self.actionAgent.setActionGroup(self.groupColor)
94 | self.actionLikelihood = QtWidgets.QAction(MainWindow)
95 | self.actionLikelihood.setCheckable(True)
96 | self.actionLikelihood.setObjectName("actionLikelihood")
97 | self.actionLikelihood.setActionGroup(self.groupColor)
98 | self.actionStep = QtWidgets.QAction(MainWindow)
99 | self.actionStep.setObjectName("actionStep")
100 | self.actionMap = QtWidgets.QAction(MainWindow)
101 | self.actionMap.setEnabled(False)
102 | self.actionMap.setObjectName("actionMap")
103 | self.actionScreenshot = QtWidgets.QAction(MainWindow)
104 | self.actionScreenshot.setObjectName("actionScreenshot")
105 | self.groupCyclic = QtWidgets.QActionGroup(MainWindow)
106 | self.actionGround_Truth = QtWidgets.QAction(MainWindow)
107 | self.actionGround_Truth.setCheckable(True)
108 | self.actionGround_Truth.setObjectName("actionGround_Truth")
109 | self.actionGround_Truth.setActionGroup(self.groupCyclic)
110 | self.actionAcyclical = QtWidgets.QAction(MainWindow)
111 | self.actionAcyclical.setCheckable(True)
112 | self.actionAcyclical.setChecked(True)
113 | self.actionAcyclical.setObjectName("actionAcyclical")
114 | self.actionAcyclical.setActionGroup(self.groupCyclic)
115 | self.actionBeliefs = QtWidgets.QAction(MainWindow)
116 | self.actionBeliefs.setCheckable(True)
117 | self.actionBeliefs.setObjectName("actionBeliefs")
118 | self.actionSubgraphs = QtWidgets.QAction(MainWindow)
119 | self.actionSubgraphs.setObjectName("actionSubgraphs")
120 | self.menuFile.addAction(self.actionNew)
121 | self.menuFile.addAction(self.actionOpen)
122 | self.menuFile.addAction(self.actionRecent_Files)
123 | self.menuFile.addSeparator()
124 | self.menuFile.addAction(self.actionSave)
125 | self.menuFile.addAction(self.actionSave_As)
126 | self.menuFile.addSeparator()
127 | self.menuFile.addAction(self.actionClose)
128 | self.menuFile.addSeparator()
129 | self.menuFile.addAction(self.actionQuit)
130 | self.menuEdit.addAction(self.actionUndo)
131 | self.menuEdit.addAction(self.actionRedo)
132 | self.menuEdit.addSeparator()
133 | self.menuEdit.addAction(self.actionCut)
134 | self.menuEdit.addAction(self.actionCopy)
135 | self.menuEdit.addAction(self.actionPaste)
136 | self.menuView.addAction(self.actionAgent)
137 | self.menuView.addAction(self.actionLikelihood)
138 | self.menuView.addSeparator()
139 | self.menuView.addAction(self.actionAcyclical)
140 | self.menuView.addAction(self.actionGround_Truth)
141 | self.menuView.addSeparator()
142 | self.menuView.addAction(self.actionBeliefs)
143 | self.menuTools.addAction(self.actionStep)
144 | self.menuTools.addAction(self.actionScreenshot)
145 | self.menuTools.addAction(self.actionMap)
146 | self.menuTools.addAction(self.actionSubgraphs)
147 | self.menubar.addAction(self.menuFile.menuAction())
148 | self.menubar.addAction(self.menuEdit.menuAction())
149 | self.menubar.addAction(self.menuView.menuAction())
150 | self.menubar.addAction(self.menuTools.menuAction())
151 |
152 | self.retranslateUi(MainWindow)
153 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
154 |
155 | def retranslateUi(self, MainWindow):
156 | _translate = QtCore.QCoreApplication.translate
157 | MainWindow.setWindowTitle(_translate("MainWindow", "PsychSim"))
158 | self.menuFile.setTitle(_translate("MainWindow", "Fi&le"))
159 | self.menuEdit.setTitle(_translate("MainWindow", "Edit"))
160 | self.menuView.setTitle(_translate("MainWindow", "&View"))
161 | self.menuTools.setTitle(_translate("MainWindow", "Tools"))
162 | self.actionNew.setText(_translate("MainWindow", "&New"))
163 | self.actionNew.setShortcut(_translate("MainWindow", "Ctrl+N"))
164 | self.actionOpen.setText(_translate("MainWindow", "&Open"))
165 | self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))
166 | self.actionRecent_Files.setText(_translate("MainWindow", "&Recent Files"))
167 | self.actionUndo.setText(_translate("MainWindow", "&Undo"))
168 | self.actionUndo.setShortcut(_translate("MainWindow", "Ctrl+Z"))
169 | self.actionRedo.setText(_translate("MainWindow", "&Redo"))
170 | self.actionRedo.setShortcut(_translate("MainWindow", "Ctrl+Y"))
171 | self.actionCut.setText(_translate("MainWindow", "&Cut"))
172 | self.actionCut.setShortcut(_translate("MainWindow", "Ctrl+X"))
173 | self.actionCopy.setText(_translate("MainWindow", "C&opy"))
174 | self.actionCopy.setShortcut(_translate("MainWindow", "Ctrl+C"))
175 | self.actionPaste.setText(_translate("MainWindow", "&Paste"))
176 | self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
177 | self.actionSave.setText(_translate("MainWindow", "&Save"))
178 | self.actionSave.setShortcut(_translate("MainWindow", "Ctrl+S"))
179 | self.actionSave_As.setText(_translate("MainWindow", "Sa&ve As"))
180 | self.actionClose.setText(_translate("MainWindow", "&Close"))
181 | self.actionClose.setShortcut(_translate("MainWindow", "Ctrl+W"))
182 | self.actionQuit.setText(_translate("MainWindow", "&Quit"))
183 | self.actionQuit.setToolTip(_translate("MainWindow", "&Quit"))
184 | self.actionQuit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
185 | self.actionAgent.setText(_translate("MainWindow", "&Agent"))
186 | self.actionAgent.setToolTip(_translate("MainWindow", "Color code nodes by agent correspondence"))
187 | self.actionLikelihood.setText(_translate("MainWindow", "&Likelihood"))
188 | self.actionLikelihood.setToolTip(_translate("MainWindow", "Color code nodes by relative likelihood"))
189 | self.actionStep.setText(_translate("MainWindow", "&Step"))
190 | self.actionMap.setText(_translate("MainWindow", "&Map"))
191 | self.actionMap.setToolTip(_translate("MainWindow", "Show map view of scenario"))
192 | self.actionScreenshot.setText(_translate("MainWindow", "S&creenshot"))
193 | self.actionGround_Truth.setText(_translate("MainWindow", "&Cyclical"))
194 | self.actionGround_Truth.setIconText(_translate("MainWindow", "Cyclical"))
195 | self.actionGround_Truth.setToolTip(_translate("MainWindow", "Show cyclical view of graph"))
196 | self.actionAcyclical.setText(_translate("MainWindow", "Ac&yclical"))
197 | self.actionBeliefs.setText(_translate("MainWindow", "&Beliefs"))
198 | self.actionSubgraphs.setText(_translate("MainWindow", "Subgraphs"))
199 |
200 | import psychsim.ui.psychsim_rc
201 |
--------------------------------------------------------------------------------
/psychsim/ui/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 800
10 | 600
11 |
12 |
13 |
14 | PsychSim
15 |
16 |
17 |
18 | :/psychsim.gif:/psychsim.gif
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 | 0
27 | 0
28 |
29 |
30 |
31 | -
32 |
33 |
34 |
35 | 0
36 | 0
37 |
38 |
39 |
40 | QPainter::Antialiasing|QPainter::TextAntialiasing
41 |
42 |
43 | QGraphicsView::RubberBandDrag
44 |
45 |
46 |
47 |
48 |
49 |
110 |
111 |
112 |
113 | false
114 |
115 |
116 | &New
117 |
118 |
119 | Ctrl+N
120 |
121 |
122 |
123 |
124 | &Open
125 |
126 |
127 | Ctrl+O
128 |
129 |
130 |
131 |
132 | false
133 |
134 |
135 | &Recent Files
136 |
137 |
138 |
139 |
140 | false
141 |
142 |
143 | &Undo
144 |
145 |
146 | Ctrl+Z
147 |
148 |
149 |
150 |
151 | false
152 |
153 |
154 | &Redo
155 |
156 |
157 | Ctrl+Y
158 |
159 |
160 |
161 |
162 | false
163 |
164 |
165 | &Cut
166 |
167 |
168 | Ctrl+X
169 |
170 |
171 |
172 |
173 | false
174 |
175 |
176 | C&opy
177 |
178 |
179 | Ctrl+C
180 |
181 |
182 |
183 |
184 | false
185 |
186 |
187 | &Paste
188 |
189 |
190 | Ctrl+V
191 |
192 |
193 |
194 |
195 | false
196 |
197 |
198 | &Save
199 |
200 |
201 | Ctrl+S
202 |
203 |
204 |
205 |
206 | false
207 |
208 |
209 | Sa&ve As
210 |
211 |
212 |
213 |
214 | false
215 |
216 |
217 | &Close
218 |
219 |
220 | Ctrl+W
221 |
222 |
223 |
224 |
225 | &Quit
226 |
227 |
228 | &Quit
229 |
230 |
231 | Ctrl+Q
232 |
233 |
234 |
235 |
236 | true
237 |
238 |
239 | true
240 |
241 |
242 | &Agent
243 |
244 |
245 | Color code nodes by agent correspondence
246 |
247 |
248 |
249 |
250 | true
251 |
252 |
253 | &Likelihood
254 |
255 |
256 | Color code nodes by relative likelihood
257 |
258 |
259 |
260 |
261 | &Step
262 |
263 |
264 |
265 |
266 | false
267 |
268 |
269 | &Map
270 |
271 |
272 | Show map view of scenario
273 |
274 |
275 |
276 |
277 | S&creenshot
278 |
279 |
280 |
281 |
282 | true
283 |
284 |
285 | &Cyclical
286 |
287 |
288 | Cyclical
289 |
290 |
291 | Show cyclical view of graph
292 |
293 |
294 |
295 |
296 | true
297 |
298 |
299 | true
300 |
301 |
302 | Ac&yclical
303 |
304 |
305 |
306 |
307 | true
308 |
309 |
310 | &Beliefs
311 |
312 |
313 |
314 |
315 | Subgraphs
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
--------------------------------------------------------------------------------
/psychsim/ui/mapview.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sys
3 |
4 | from PyQt5.QtCore import *
5 | from PyQt5.QtGui import *
6 | from PyQt5.QtWidgets import *
7 | try:
8 | from PyQt5 import QtSvg
9 | except ImportError:
10 | pass
11 |
12 | class MapView(QGraphicsScene):
13 | images = '/home/david/PsychSim/psychsim/domains/inequality/images'
14 |
15 | nations = {'algeria': {'COUNTRY_ALPHA': 1},
16 | 'benin': {'COUNTRY_ALPHA': 2},
17 | 'botswana': {'COUNTRY_ALPHA': 3},
18 | 'burkinafaso': {'COUNTRY_ALPHA': 4},
19 | 'burundi': {'COUNTRY_ALPHA': 5},
20 | 'cameroon': {'COUNTRY_ALPHA': 6},
21 | 'cotedivoire': {'COUNTRY_ALPHA': 8},
22 | 'egypt': {'COUNTRY_ALPHA': 9},
23 | 'ghana': {'COUNTRY_ALPHA': 11},
24 | 'guinea': {'COUNTRY_ALPHA': 12},
25 | 'kenya': {'COUNTRY_ALPHA': 13},
26 | 'lesotho': {'COUNTRY_ALPHA': 14},
27 | 'liberia': {'COUNTRY_ALPHA': 15},
28 | 'madagascar': {'COUNTRY_ALPHA': 16},
29 | 'malawi': {'COUNTRY_ALPHA': 17},
30 | 'mali': {'COUNTRY_ALPHA': 18},
31 | 'morocco': {'COUNTRY_ALPHA': 20},
32 | 'mozambique': {'COUNTRY_ALPHA': 21},
33 | 'namibia': {'COUNTRY_ALPHA': 22},
34 | 'niger': {'COUNTRY_ALPHA': 23},
35 | 'nigeria': {'COUNTRY_ALPHA': 24},
36 | 'senegal': {'COUNTRY_ALPHA': 25},
37 | 'sierraleone': {'COUNTRY_ALPHA': 26},
38 | 'southafrica': {'COUNTRY_ALPHA': 27},
39 | 'sudan': {'COUNTRY_ALPHA': 28},
40 | 'swaziland': {'COUNTRY_ALPHA': 29},
41 | 'tanzania': {'COUNTRY_ALPHA': 30},
42 | 'togo': {'COUNTRY_ALPHA': 31},
43 | 'tunisia': {'COUNTRY_ALPHA': 32},
44 | 'uganda': {'COUNTRY_ALPHA': 33},
45 | 'zambia': {'COUNTRY_ALPHA': 34},
46 | 'zimbabew': {'COUNTRY_ALPHA': 35},
47 | }
48 |
49 | def __init__(self,parent = None):
50 | super(MapView,self).__init__(parent)
51 | self.africa = QPixmap(os.path.join(self.images,'..','Blank_Map-Africa.png'))
52 | self.addPixmap(self.africa)
53 | for nation in self.nations:
54 | self.nations[nation]['image'] = QPixmap(os.path.join(self.images,'%s.png' % (nation)))
55 | self.colorNation(nation,qRgb(255,204,0))
56 | item = QGraphicsPixmapItem(self.addPixmap(self.nations[nation]['image']))
57 |
58 | def colorNation(self,nation,newColor):
59 | img = self.nations[nation]['image'].toImage()
60 | for x in range(img.width()):
61 | for y in range(img.height()):
62 | if img.pixel(x,y) > 0:
63 | oldColor = QColor(img.pixel(x,y))
64 | delta = abs(oldColor.red()-204)+abs(oldColor.green()-204)+\
65 | abs(oldColor.blue()-204)
66 | if delta < 10:
67 | img.setPixel(x,y,newColor)
68 | self.nations[nation]['image'].convertFromImage(img)
69 |
--------------------------------------------------------------------------------
/psychsim/ui/psychsim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pynadath/psychsim/c7b2b92e6ff8b83b2e832acda02c4baafabdf06f/psychsim/ui/psychsim.gif
--------------------------------------------------------------------------------
/psychsim/ui/psychsim.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | psychsim.gif
4 |
5 |
6 |
--------------------------------------------------------------------------------
/psychsim/ui/psychsim_rc.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | if (sys.version_info > (3, 0)):
4 | import psychsim.ui.psychsim_rc3
5 | else:
6 | import psychsim.ui.psychsim_rc2
7 |
8 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | setuptools.setup(
7 | name="example_pkg",
8 | version="0.0.1",
9 | author="Example Author",
10 | author_email="author@example.com",
11 | description="A small example package",
12 | long_description=long_description,
13 | long_description_content_type="text/markdown",
14 | url="https://github.com/pypa/sampleproject",
15 | packages=setuptools.find_packages(),
16 | classifiers=[
17 | "Programming Language :: Python :: 3",
18 | "License :: OSI Approved :: MIT License",
19 | "Operating System :: OS Independent",
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------