palaestrAI in a Jupyter Notebook¶

This is just the equivalent of a system test for the random action experiment.

Imports¶

In [1]:
import palaestrai

If the above line works without error, at least the import is done. Thats a first good starter. Now let's import the rest…

In [2]:
import os
import pprint
import tempfile
from pathlib import Path

Setting the Runtime Configuration¶

Usually, this is not necessary like this. But because we're running a self-contained test, we want to have a new, fresh database where we want to put it. Which is, in a temporary directory.

In [3]:
store_dir = tempfile.TemporaryDirectory()
store_dir
Out[3]:
<TemporaryDirectory '/tmp/tmpbq1lzzd2'>

We're now going to change palaestrAI's runtime configuration to point to the new directory.

In [4]:
from palaestrai.core import RuntimeConfig

runtime_config = RuntimeConfig()
runtime_config.reset()
runtime_config.load(
    {"store_uri": "sqlite:///%s/palaestrai.db" % store_dir.name}
)
pprint.pprint(runtime_config.to_dict())
{'broker_uri': 'ipc://*',
 'data_path': './_outputs',
 'executor_bus_port': 4242,
 'fork_method': 'spawn',
 'logger_port': 4243,
 'logging': {'filters': {'debug_filter': {'()': 'palaestrai.core.runtime_config.DebugLogFilter'}},
             'formatters': {'debug': {'format': '%(asctime)s '
                                                '%(name)s[%(process)d]: '
                                                '%(levelname)s - %(message)s '
                                                '(%(module)s.%(funcName)s in '
                                                '%(filename)s:%(lineno)d)'},
                            'simple': {'format': '%(asctime)s '
                                                 '%(name)s[%(process)d]: '
                                                 '%(levelname)s - %(message)s'},
                            'terminal': {'class': 'palaestrai.cli.terminal_formatter.TerminalFormatter'}},
             'handlers': {'console': {'class': 'logging.StreamHandler',
                                      'formatter': 'simple',
                                      'level': 'INFO',
                                      'stream': 'ext://sys.stdout'},
                          'console_debug': {'class': 'logging.StreamHandler',
                                            'filters': ['debug_filter'],
                                            'formatter': 'debug',
                                            'level': 'DEBUG',
                                            'stream': 'ext://sys.stdout'}},
             'loggers': {'palaestrai.agent': {'level': 'ERROR'},
                         'palaestrai.agent.agent_conductor': {'level': 'ERROR'},
                         'palaestrai.agent.brain': {'level': 'ERROR'},
                         'palaestrai.agent.muscle': {'level': 'ERROR'},
                         'palaestrai.core': {'level': 'ERROR'},
                         'palaestrai.environment': {'level': 'ERROR'},
                         'palaestrai.experiment': {'level': 'ERROR'},
                         'palaestrai.simulation': {'level': 'ERROR'},
                         'palaestrai.store': {'level': 'ERROR'},
                         'palaestrai.types': {'level': 'ERROR'},
                         'palaestrai.util': {'level': 'ERROR'},
                         'palaestrai.visualization': {'level': 'ERROR'},
                         'sqlalchemy.engine': {'level': 'ERROR'}},
             'root': {'handlers': ['console', 'console_debug'],
                      'level': 'ERROR'},
             'version': 1},
 'major_domo_client_retries': 3,
 'major_domo_client_timeout': 300000,
 'profile': False,
 'public_bind': False,
 'store_buffer_size': 20,
 'store_uri': 'sqlite:////tmp/tmpbq1lzzd2/palaestrai.db',
 'time_series_store_uri': 'influx+localhost:8086'}

Create the Database¶

Next, we create the database at the given URI. It will complain that we're not using TimescaleDB, which is okay.

In [5]:
from palaestrai.store.database_util import setup_database

setup_database(runtime_config.store_uri)
Importing from 'midas.tools.palaestrai' is deprecated! Use 'midas_palaestrai' instead!
Importing from 'midas.tools.palaestrai' is deprecated! Use 'midas_palaestrai' instead!
Could not create extension timescaledb and create hypertables: (sqlite3.OperationalError) near "EXTENSION": syntax error
[SQL: CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;]
(Background on this error at: https://sqlalche.me/e/14/e3q8). Your database setup might lead to noticeable slowdowns with larger experiment runs. Please upgrade to PostgreSQL with TimescaleDB for the best performance.
In [6]:
assert Path("%s/palaestrai.db" % store_dir.name).is_file()

Run Experiment¶

In this part, we load our dummy experiment and run it.

In [7]:
experiment_file_path = Path().absolute() / ".." / "fixtures" / "dummy_run.yml"
assert experiment_file_path.is_file()
In [8]:
rc = palaestrai.execute(str(experiment_file_path))
assert rc[1].value == 4
rc
Could not dump to <palaestrai.agent.store_brain_dumper.StoreBrainDumper object at 0x7fccbcf8e570>: (sqlite3.OperationalError) database is locked
[SQL: INSERT INTO brain_states (walltime, state, tag, simtime_ticks, simtime_timestamp, agent_id) VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?, ?)]
[parameters: (<memory at 0x7fccbcc7bc40>, None, None, None, 2)]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
Could not dump to <palaestrai.agent.store_brain_dumper.StoreBrainDumper object at 0x7f94056b6570>: (sqlite3.OperationalError) database is locked
[SQL: INSERT INTO brain_states (walltime, state, tag, simtime_ticks, simtime_timestamp, agent_id) VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?, ?)]
[parameters: (<memory at 0x7f9405737c40>, None, None, None, 1)]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
Could not dump to <palaestrai.agent.store_brain_dumper.StoreBrainDumper object at 0x7fc66c81aba0>: (sqlite3.OperationalError) database is locked
[SQL: INSERT INTO brain_states (walltime, state, tag, simtime_ticks, simtime_timestamp, agent_id) VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?, ?)]
[parameters: (<memory at 0x7fc66ca6fc40>, None, None, None, 4)]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
Could not dump to <palaestrai.agent.store_brain_dumper.StoreBrainDumper object at 0x7fba2f67e8d0>: (sqlite3.OperationalError) database is locked
[SQL: INSERT INTO brain_states (walltime, state, tag, simtime_ticks, simtime_timestamp, agent_id) VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?, ?)]
[parameters: (<memory at 0x7fba2f873c40>, None, None, None, 3)]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
Out[8]:
(['Yo-ho, a dummy experiment run for me!'], <ExecutorState.EXITED: 4>)

If you see something like ('Yo-ho, a dummy experiment run for me!', <ExecutorState.EXITED: 4>) as output of the previous line, then congratulations, everything went well! Now onwards to the final step…

Verify Data from the Store¶

Now that an experiment has been run, there should be something in the store. Note that we don't check whether something meaningful is in the store, only that there is something in the store.

In [9]:
from sqlalchemy import select

import palaestrai.store
import palaestrai.store.database_model as dbm
In [10]:
dbh = palaestrai.store.Session()
In [11]:
q = select(dbm.Experiment)
str(q)
Out[11]:
'SELECT experiments.document, experiments.document_json, experiments.id, experiments.name \nFROM experiments'
In [12]:
experiment = dbh.execute(q).first()[dbm.Experiment]
experiment.name
Out[12]:
'Dummy Runs'
In [13]:
our_experiment_run = experiment.experiment_runs[0]
str(our_experiment_run)
Out[13]:
'<ExperimentRun(id=1, uid="Yo-ho, a dummy experiment run for me!", experiment_id=1, document={\'uid\': \'Yo-ho, a dummy experiment run for me!\', \'experiment_uid\': \'Dummy Runs\', \'seed\': 42, \'version\': \'3.5.8\', \'schedule\': [{\'phase_0\': {\'environments\': [{\'environment\': {\'name\': \'palaestrai.environment.dummy_environment:DummyEnvironment\', \'uid\': \'myenv\', \'params\': {\'discrete\': True}}}], \'agents\': [{\'name\': \'mighty_defender\', \'brain\': {\'name\': \'palaestrai.agent.dummy_brain:DummyBrain\', \'params\': {}}, \'muscle\': {\'name\': \'palaestrai.agent.dummy_muscle:DummyMuscle\', \'params\': {}}, \'objective\': {\'name\': \'palaestrai.agent.dummy_objective:DummyObjective\', \'params\': {\'params\': 1}}, \'sensors\': [\'myenv.0\', \'myenv.1\', \'myenv.2\', \'myenv.3\', \'myenv.4\'], \'actuators\': [\'myenv.0\', \'myenv.1\', \'myenv.2\', \'myenv.3\', \'myenv.4\']}, {\'name\': \'evil_attacker\', \'brain\': {\'name\': \'palaestrai.agent.dummy_brain:DummyBrain\', \'params\': {}}, \'muscle\': {\'name\': \'palaestrai.agent.dummy_muscle:DummyMuscle\', \'params\': {}}, \'objective\': {\'name\': \'palaestrai.agent.dummy_objective:DummyObjective\', \'params\': {\'params\': 1}}, \'sensors\': [\'myenv.5\', \'myenv.6\', \'myenv.7\', \'myenv.8\', \'myenv.9\'], \'actuators\': [\'myenv.5\', \'myenv.6\', \'myenv.7\', \'myenv.8\', \'myenv.9\']}], \'simulation\': {\'name\': \'palaestrai.simulation:Vanilla\', \'conditions\': [{\'name\': \'palaestrai.simulation:VanillaSimControllerTerminationCondition\', \'params\': {}}]}, \'phase_config\': {\'mode\': \'train\', \'worker\': 1, \'episodes\': 1}}}, {\'phase_1\': {\'environments\': [{\'environment\': {\'name\': \'palaestrai.environment.dummy_environment:DummyEnvironment\', \'uid\': \'myenv\', \'params\': {\'discrete\': True}}}], \'agents\': [{\'name\': \'mighty_defender\', \'load\': {\'agent\': \'mighty_defender\', \'experiment_run\': \'Yo-ho, a dummy experiment run for me!\', \'phase\': 0}, \'brain\': {\'name\': \'palaestrai.agent.dummy_brain:DummyBrain\', \'params\': {}}, \'muscle\': {\'name\': \'palaestrai.agent.dummy_muscle:DummyMuscle\', \'params\': {}}, \'objective\': {\'name\': \'palaestrai.agent.dummy_objective:DummyObjective\', \'params\': {\'params\': 1}}, \'sensors\': [\'myenv.0\', \'myenv.1\', \'myenv.2\', \'myenv.3\', \'myenv.4\'], \'actuators\': [\'myenv.0\', \'myenv.1\', \'myenv.2\', \'myenv.3\', \'myenv.4\']}, {\'name\': \'evil_attacker\', \'load\': {\'agent\': \'evil_attacker\', \'experiment_run\': \'Yo-ho, a dummy experiment run for me!\', \'phase\': 0}, \'brain\': {\'name\': \'palaestrai.agent.dummy_brain:DummyBrain\', \'params\': {}}, \'muscle\': {\'name\': \'palaestrai.agent.dummy_muscle:DummyMuscle\', \'params\': {}}, \'objective\': {\'name\': \'palaestrai.agent.dummy_objective:DummyObjective\', \'params\': {\'params\': 1}}, \'sensors\': [\'myenv.5\', \'myenv.6\', \'myenv.7\', \'myenv.8\', \'myenv.9\'], \'actuators\': [\'myenv.5\', \'myenv.6\', \'myenv.7\', \'myenv.8\', \'myenv.9\']}], \'simulation\': {\'name\': \'palaestrai.simulation:Vanilla\', \'conditions\': [{\'name\': \'palaestrai.simulation:VanillaSimControllerTerminationCondition\', \'params\': {}}]}, \'phase_config\': {\'mode\': \'train\', \'worker\': 1, \'episodes\': 1}}}], \'run_config\': {\'condition\': {\'name\': \'palaestrai.experiment:VanillaRunGovernorTerminationCondition\', \'params\': {}}}}>'

The document property of the experiment run object should contain the YAML file, and we should be able to de-searialize it. Let's check that.

In [14]:
assert our_experiment_run.document
In [15]:
our_experiment_run._document_json
Out[15]:
{'uid': 'Yo-ho, a dummy experiment run for me!',
 'experiment_uid': 'Dummy Runs',
 'seed': 42,
 'version': '3.5.8',
 'schedule': [{'phase_0': {'environments': [{'environment': {'name': 'palaestrai.environment.dummy_environment:DummyEnvironment',
       'uid': 'myenv',
       'params': {'discrete': True}}}],
    'agents': [{'name': 'mighty_defender',
      'brain': {'name': 'palaestrai.agent.dummy_brain:DummyBrain',
       'params': {}},
      'muscle': {'name': 'palaestrai.agent.dummy_muscle:DummyMuscle',
       'params': {}},
      'objective': {'name': 'palaestrai.agent.dummy_objective:DummyObjective',
       'params': {'params': 1}},
      'sensors': ['myenv.0', 'myenv.1', 'myenv.2', 'myenv.3', 'myenv.4'],
      'actuators': ['myenv.0', 'myenv.1', 'myenv.2', 'myenv.3', 'myenv.4']},
     {'name': 'evil_attacker',
      'brain': {'name': 'palaestrai.agent.dummy_brain:DummyBrain',
       'params': {}},
      'muscle': {'name': 'palaestrai.agent.dummy_muscle:DummyMuscle',
       'params': {}},
      'objective': {'name': 'palaestrai.agent.dummy_objective:DummyObjective',
       'params': {'params': 1}},
      'sensors': ['myenv.5', 'myenv.6', 'myenv.7', 'myenv.8', 'myenv.9'],
      'actuators': ['myenv.5', 'myenv.6', 'myenv.7', 'myenv.8', 'myenv.9']}],
    'simulation': {'name': 'palaestrai.simulation:Vanilla',
     'conditions': [{'name': 'palaestrai.simulation:VanillaSimControllerTerminationCondition',
       'params': {}}]},
    'phase_config': {'mode': 'train', 'worker': 1, 'episodes': 1}}},
  {'phase_1': {'environments': [{'environment': {'name': 'palaestrai.environment.dummy_environment:DummyEnvironment',
       'uid': 'myenv',
       'params': {'discrete': True}}}],
    'agents': [{'name': 'mighty_defender',
      'load': {'agent': 'mighty_defender',
       'experiment_run': 'Yo-ho, a dummy experiment run for me!',
       'phase': 0},
      'brain': {'name': 'palaestrai.agent.dummy_brain:DummyBrain',
       'params': {}},
      'muscle': {'name': 'palaestrai.agent.dummy_muscle:DummyMuscle',
       'params': {}},
      'objective': {'name': 'palaestrai.agent.dummy_objective:DummyObjective',
       'params': {'params': 1}},
      'sensors': ['myenv.0', 'myenv.1', 'myenv.2', 'myenv.3', 'myenv.4'],
      'actuators': ['myenv.0', 'myenv.1', 'myenv.2', 'myenv.3', 'myenv.4']},
     {'name': 'evil_attacker',
      'load': {'agent': 'evil_attacker',
       'experiment_run': 'Yo-ho, a dummy experiment run for me!',
       'phase': 0},
      'brain': {'name': 'palaestrai.agent.dummy_brain:DummyBrain',
       'params': {}},
      'muscle': {'name': 'palaestrai.agent.dummy_muscle:DummyMuscle',
       'params': {}},
      'objective': {'name': 'palaestrai.agent.dummy_objective:DummyObjective',
       'params': {'params': 1}},
      'sensors': ['myenv.5', 'myenv.6', 'myenv.7', 'myenv.8', 'myenv.9'],
      'actuators': ['myenv.5', 'myenv.6', 'myenv.7', 'myenv.8', 'myenv.9']}],
    'simulation': {'name': 'palaestrai.simulation:Vanilla',
     'conditions': [{'name': 'palaestrai.simulation:VanillaSimControllerTerminationCondition',
       'params': {}}]},
    'phase_config': {'mode': 'train', 'worker': 1, 'episodes': 1}}}],
 'run_config': {'condition': {'name': 'palaestrai.experiment:VanillaRunGovernorTerminationCondition',
   'params': {}}}}
In [16]:
our_experiment_run.experiment_run_instances[0].uid
Out[16]:
'8b65835d-2980-4d78-bd48-88e7cd961e8a'

… and so on. For a system test, this is enough.