├── .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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /psychsim/domains/teamofrivals/risk.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 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 | 50 | 51 | 52 | 0 53 | 0 54 | 800 55 | 25 56 | 57 | 58 | 59 | 60 | Fi&le 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Edit 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | &View 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Tools 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 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 | --------------------------------------------------------------------------------