Full Code of infer-actively/pymdp for AI

main d493cc0e9479 cached
220 files
9.3 MB
2.4M tokens
900 symbols
1 requests
Download .txt
Showing preview only (9,769K chars total). Download the full file or copy to clipboard to get everything.
Repository: infer-actively/pymdp
Branch: main
Commit: d493cc0e9479
Files: 220
Total size: 9.3 MB

Directory structure:
gitextract_jhy4mwbi/

├── .github/
│   └── workflows/
│       ├── docs.yml
│       ├── lint.yaml
│       ├── manual-branch-nightly.yaml
│       ├── nightly-tests.yaml
│       ├── python-package.yml
│       └── test.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs/
│   ├── Makefile
│   ├── agent.rst
│   ├── algos/
│   │   ├── fpi.rst
│   │   ├── index.rst
│   │   └── mmp.rst
│   ├── conf.py
│   ├── control.rst
│   ├── env.rst
│   ├── index.rst
│   ├── inference.rst
│   ├── installation.rst
│   ├── learning.rst
│   ├── make.bat
│   ├── notebooks/
│   │   ├── active_inference_from_scratch.ipynb
│   │   ├── cue_chaining_demo.ipynb
│   │   ├── free_energy_calculation.ipynb
│   │   ├── pymdp_fundamentals.ipynb
│   │   ├── tmaze_demo.ipynb
│   │   └── using_the_agent_class.ipynb
│   └── requirements.txt
├── docs-mkdocs/
│   ├── agent.html
│   ├── algos/
│   │   └── index.html
│   ├── api/
│   │   ├── agent.md
│   │   ├── algos.md
│   │   ├── control.md
│   │   ├── envs-env.md
│   │   ├── envs-rollout.md
│   │   ├── index.md
│   │   ├── inference.md
│   │   ├── learning.md
│   │   ├── maths.md
│   │   ├── planning-mcts.md
│   │   ├── planning-si.md
│   │   └── utils.md
│   ├── control.html
│   ├── development/
│   │   ├── release-notes.md
│   │   └── viewing-docs.md
│   ├── env.html
│   ├── getting-started/
│   │   ├── installation.md
│   │   └── quickstart-jax.md
│   ├── guides/
│   │   ├── generative-model-structure.md
│   │   ├── pymdp-env.md
│   │   └── rollout-active-inference-loop.md
│   ├── index.md
│   ├── inference.html
│   ├── installation.html
│   ├── javascripts/
│   │   ├── mathjax.js
│   │   └── sidebar-accessibility.js
│   ├── learning.html
│   ├── legacy/
│   │   └── index.md
│   ├── migration/
│   │   └── numpy-to-jax.md
│   ├── notebooks/
│   │   ├── active_inference_from_scratch.html
│   │   ├── cue_chaining_demo.html
│   │   ├── free_energy_calculation.html
│   │   ├── pymdp_fundamentals.html
│   │   ├── tmaze_demo.html
│   │   └── using_the_agent_class.html
│   ├── overrides/
│   │   └── modules/
│   │       └── sidebar.html
│   ├── styles/
│   │   └── crisp-api.css
│   └── tutorials/
│       ├── index.md
│       ├── notebooks/
│       │   ├── index.header.md
│       │   └── index.md
│       └── notebooks.manifest
├── examples/
│   ├── __init__.py
│   ├── advanced/
│   │   ├── complex_action_dependency.ipynb
│   │   ├── infer_states_optimization/
│   │   │   └── methods_test.ipynb
│   │   └── pymdp_with_neural_encoder.ipynb
│   ├── api/
│   │   └── model_construction_tutorial.ipynb
│   ├── envs/
│   │   ├── chained_cue_navigation.py
│   │   ├── cue_chaining_demo.ipynb
│   │   ├── generalized_tmaze_demo.ipynb
│   │   ├── graph_worlds_demo.ipynb
│   │   ├── knapsack_demo.ipynb
│   │   └── tmaze_demo.ipynb
│   ├── experimental/
│   │   └── sophisticated_inference/
│   │       ├── mcts_generalized_tmaze.ipynb
│   │       ├── mcts_graph_world.ipynb
│   │       ├── si_generalized_tmaze.ipynb
│   │       ├── si_graph_world.ipynb
│   │       └── si_tmaze_SIvalidation.ipynb
│   ├── inductive_inference/
│   │   ├── inductive_inference_example.ipynb
│   │   └── inductive_inference_gridworld.ipynb
│   ├── inference_and_learning/
│   │   └── inference_methods_comparison.ipynb
│   ├── learning/
│   │   └── learning_gridworld.ipynb
│   ├── legacy/
│   │   ├── agent_demo.ipynb
│   │   ├── free_energy_calculation.ipynb
│   │   ├── gridworld_tutorial_1.ipynb
│   │   ├── gridworld_tutorial_2.ipynb
│   │   ├── tmaze_demo.ipynb
│   │   └── tmaze_learning_demo.ipynb
│   ├── model_fitting/
│   │   ├── fitting_with_pybefit.ipynb
│   │   └── tmaze_recoverability.py
│   └── sparse/
│       └── sparse_benchmark.ipynb
├── mkdocs.yml
├── nbval_sanitize.cfg
├── paper/
│   ├── paper.bib
│   └── paper.md
├── pymdp/
│   ├── __init__.py
│   ├── agent.py
│   ├── algos.py
│   ├── control.py
│   ├── distribution.py
│   ├── envs/
│   │   ├── __init__.py
│   │   ├── cue_chaining.py
│   │   ├── env.py
│   │   ├── generalized_tmaze.py
│   │   ├── graph_worlds.py
│   │   ├── grid_world.py
│   │   ├── rollout.py
│   │   └── tmaze.py
│   ├── inference.py
│   ├── learning.py
│   ├── legacy/
│   │   ├── __init__.py
│   │   ├── agent.py
│   │   ├── algos/
│   │   │   ├── __init__.py
│   │   │   ├── fpi.py
│   │   │   ├── mmp.py
│   │   │   └── mmp_old.py
│   │   ├── control.py
│   │   ├── default_models.py
│   │   ├── envs/
│   │   │   ├── __init__.py
│   │   │   ├── env.py
│   │   │   ├── grid_worlds.py
│   │   │   ├── tmaze.py
│   │   │   └── visual_foraging.py
│   │   ├── inference.py
│   │   ├── learning.py
│   │   ├── maths.py
│   │   └── utils.py
│   ├── likelihoods.py
│   ├── maths.py
│   ├── planning/
│   │   ├── __init__.py
│   │   ├── mcts.py
│   │   ├── si.py
│   │   └── visualize.py
│   └── utils.py
├── pyproject.toml
├── scripts/
│   ├── docs_build.sh
│   ├── docs_serve.sh
│   ├── docs_sync_and_serve.sh
│   ├── notebook_precommit.py
│   ├── run_notebook_manifest.py
│   └── sync_docs_notebooks.sh
├── setup.cfg
└── test/
    ├── __init__.py
    ├── conftest.py
    ├── matlab_crossval/
    │   ├── generation/
    │   │   ├── bmr_matlab_test_a.m
    │   │   ├── bmr_matlab_test_b.m
    │   │   ├── mmp_matlab_test_a.m
    │   │   ├── mmp_matlab_test_b.m
    │   │   ├── mmp_matlab_test_c.m
    │   │   ├── mmp_matlab_test_d.m
    │   │   ├── run_mmp.m
    │   │   ├── vb_x_matlab_test_1a.m
    │   │   └── vb_x_matlab_test_1b.m
    │   └── output/
    │       ├── bmr_test_a.mat
    │       ├── bmr_test_b.mat
    │       ├── cross_a.mat
    │       ├── cross_b.mat
    │       ├── cross_c.mat
    │       ├── cross_d.mat
    │       ├── cross_e.mat
    │       ├── dot_a.mat
    │       ├── dot_b.mat
    │       ├── dot_c.mat
    │       ├── dot_d.mat
    │       ├── dot_e.mat
    │       ├── mmp_a.mat
    │       ├── mmp_b.mat
    │       ├── mmp_c.mat
    │       ├── mmp_d.mat
    │       ├── vbx_test_1a.mat
    │       ├── wnorm_a.mat
    │       └── wnorm_b.mat
    ├── notebooks/
    │   ├── README.md
    │   ├── ci_notebooks.txt
    │   └── nightly_notebooks.txt
    ├── test_SPM_validation.py
    ├── test_agent.py
    ├── test_agent_jax.py
    ├── test_categorical_observations.py
    ├── test_control.py
    ├── test_control_jax.py
    ├── test_cue_chaining_env.py
    ├── test_demos.py
    ├── test_distribution.py
    ├── test_env.py
    ├── test_fpi.py
    ├── test_grid_world_parity.py
    ├── test_hmm_associative_scan.py
    ├── test_inductive_inference_jax.py
    ├── test_infer_states_optimized.py
    ├── test_inference.py
    ├── test_inference_jax.py
    ├── test_jax_sparse_backend.py
    ├── test_learning.py
    ├── test_learning_jax.py
    ├── test_message_passing_jax.py
    ├── test_mmp.py
    ├── test_param_info_gain_jax.py
    ├── test_pybefit_model_fitting.py
    ├── test_rollout_function.py
    ├── test_sophisticated_inference_jax.py
    ├── test_tmaze_envs.py
    ├── test_tmaze_recoverability.py
    ├── test_utils.py
    ├── test_utils_jax.py
    ├── test_vfe_jax.py
    └── test_wrappers.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/docs.yml
================================================
name: Docs

on:
  push:
    branches:
      - main
    paths:
      - 'examples/**'
      - 'mkdocs.yml'
      - 'docs-mkdocs/**'
      - 'scripts/docs_*.sh'
      - 'scripts/sync_docs_notebooks.sh'
      - 'docs/**'
      - '.readthedocs.yml'
      - 'pyproject.toml'
      - 'pymdp/**'
      - '.github/workflows/docs.yml'
  pull_request:
    paths:
      - 'examples/**'
      - 'mkdocs.yml'
      - 'docs-mkdocs/**'
      - 'scripts/docs_*.sh'
      - 'scripts/sync_docs_notebooks.sh'
      - 'docs/**'
      - '.readthedocs.yml'
      - 'pyproject.toml'
      - 'pymdp/**'
      - '.github/workflows/docs.yml'

jobs:
  build-docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: Install uv
        uses: astral-sh/setup-uv@v6
      - name: Install dependencies
        run: |
          uv sync --no-default-groups --extra docs
      - name: Sync curated notebook docs
        run: |
          ./scripts/sync_docs_notebooks.sh
      - name: Build docs (strict)
        run: |
          uv run --no-default-groups --extra docs mkdocs build --strict


================================================
FILE: .github/workflows/lint.yaml
================================================
name: Lint

on:
  pull_request:
    paths:
      - '**.py'
      - '.github/workflows/lint.yaml'
      - 'pyproject.toml'


jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11"]
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v3
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install ruff
    - name: Analysing the code with ruff
      run: |
        ruff check


================================================
FILE: .github/workflows/manual-branch-nightly.yaml
================================================
name: Manual Branch Nightly

on:
  workflow_dispatch:
    inputs:
      ref:
        description: Branch, tag, or SHA to validate
        required: true
        default: main
        type: string
      python_version:
        description: Python version to use
        required: true
        default: "3.12"
        type: string

concurrency:
  group: manual-branch-nightly-${{ inputs.ref }}
  cancel-in-progress: false

jobs:
  nightly:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.ref }}

      - name: Set up Python ${{ inputs.python_version }}
        uses: actions/setup-python@v3
        with:
          python-version: ${{ inputs.python_version }}

      - name: Install uv
        uses: astral-sh/setup-uv@v6

      - name: Verify nightly validation files exist
        run: |
          test -f scripts/run_notebook_manifest.py
          test -f test/notebooks/nightly_notebooks.txt

      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y ffmpeg graphviz libgraphviz-dev pkg-config build-essential python3-dev

      - name: Install dependencies
        run: |
          uv sync --group test

      - name: Run nightly-marked tests
        run: |
          uv run pytest test -n 2 -m nightly

      - name: Run nightly-tier notebook tests
        run: |
          uv run python scripts/run_notebook_manifest.py test/notebooks/nightly_notebooks.txt


================================================
FILE: .github/workflows/nightly-tests.yaml
================================================
name: Nightly Tests

on:
  schedule:
    - cron: "0 2 * * *"
  workflow_dispatch:

jobs:
  nightly:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.12"]
    steps:
      - uses: actions/checkout@v4
        with:
          ref: main
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v3
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install uv
        uses: astral-sh/setup-uv@v6
      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y ffmpeg graphviz libgraphviz-dev pkg-config build-essential python3-dev
      - name: Install dependencies
        run: |
          uv sync --group test
      - name: Running nightly-marked tests
        run: |
          uv run pytest test -n 2 -m nightly
      - name: Running nightly-tier notebook tests
        run: |
          uv run python scripts/run_notebook_manifest.py test/notebooks/nightly_notebooks.txt


================================================
FILE: .github/workflows/python-package.yml
================================================
name: Python package

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    strategy:
      matrix:
        python-version: ["3.11", "3.12"]

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Build source and wheel distributions
        run: |
          python -m pip install --upgrade pip build
          python -m build --sdist --wheel

      - name: Install built wheel
        run: |
          python -m pip install dist/*.whl

      - name: Import smoke test
        run: |
          cd /tmp
          python - <<'PY'
          import pymdp
          from pymdp.agent import Agent

          print("Imported pymdp from", pymdp.__file__)
          assert "site-packages" in pymdp.__file__
          print("Agent symbol:", Agent.__name__)
          PY


================================================
FILE: .github/workflows/test.yaml
================================================
name: Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.12"]
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v3
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install uv
        uses: astral-sh/setup-uv@v6
      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y ffmpeg graphviz libgraphviz-dev pkg-config build-essential python3-dev
      - name: Install dependencies
        run: |
          uv sync --group test
      - name: Running unit tests with coverage
        run: |
          uv run pytest test -n 2 -m "not nightly" --cov=pymdp --cov-report=xml --cov-report=html --cov-report=term
      - name: Upload coverage reports as artifact
        uses: actions/upload-artifact@v4
        with:
          name: pymdp-coverage
          path: |
            coverage.xml
            htmlcov/
          retention-days: 30
      - name: Running CI-tier notebook tests
        run: |
          uv run python scripts/run_notebook_manifest.py test/notebooks/ci_notebooks.txt


================================================
FILE: .gitignore
================================================
*.pyc
__pycache__
.DS_Store
.ipynb_checkpoints
.rope*
.vscode/
.ipynb_checkpoints/
.pytest_cache
env/
pymdp.egg-info
inferactively_pymdp.egg-info
venv/
.venv
.jax_cache
.ruff_cache
.coverage
coverage.xml
htmlcov/
site/
build/
dist/
uv.lock
docs-mkdocs/tutorials/notebooks/examples/**/*.ipynb


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: local
    hooks:
      - id: sanitize-notebooks
        name: sanitize notebooks by tier
        entry: uv run --group dev python scripts/notebook_precommit.py sanitize
        language: system
        files: ^examples/(?!legacy/).+\.ipynb$
        pass_filenames: true
      - id: validate-notebook-execution-counts
        name: validate manifest notebook execution counts
        entry: uv run --group dev python scripts/notebook_precommit.py validate-counts
        language: system
        files: ^examples/(?!legacy/).+\.ipynb$
        pass_filenames: true


================================================
FILE: .readthedocs.yml
================================================
version: 2

build:
  os: "ubuntu-22.04"
  tools:
    python: "3.11"
  jobs:
    pre_build:
      - ./scripts/sync_docs_notebooks.sh

python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs

mkdocs:
  configuration: mkdocs.yml

formats:
  - htmlzip


================================================
FILE: CONTRIBUTING.md
================================================
# pymdp

*Borrowed below list from [here](https://github.com/netsiphds/netrd)*

Welcome to `pymdp` and thanks for your interest in contributing!
During development please make sure to keep the following checklists handy.
They contain a summary of all the important steps you need to take to contribute
to the package. As a general statement, the more familiar you already are
with git(hub), the less relevant the detailed instructions below will be for you.


## Types of Contribution

There are multiple ways to contribute to `pymdp` (borrowed below list from [here](https://github.com/uzhdag/pathpy/blob/master/CONTRIBUTING.rst)):

#### Report Bugs

To report a bug in the package, open an issue at https://github.com/infer-actively/pymdp/issues.

Please include in your bug report:

* Your operating system name and version.
* Any details about your local setup that might be helpful in troubleshooting.
* Detailed steps to reproduce the bug.

#### Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with "bug" and "help
wanted" is open to whoever wants to implement it.

#### Implement Features or New Methods

Look through the GitHub issues for features. Anything tagged with "enhancement"
and "help wanted" is open to whomever wants to implement it. If you know of a 
method that is implemented in another programming language, feel free to 
translate it into python here. If you don't want to translate it yourself, feel 
free to add an issue at https://github.com/infer-actively/pymdp/issues. If you have 
read through this document and still have questions, also open an issue. When 
in doubt, open an issue.

#### Improve Documentation

Documentation is just as important as the code it documents. Please feel
free to submit PRs that are focused on fixing, improving, correcting, or
refactoring documentation.

#### Submit Feedback

The best way to send feedback is to open an issue.

If you are proposing to implement a function, feature, etc.
see more details below. 

If you are proposing a feature not directly related to implementing a new method:

* Explain in detail why the feature is desirable and how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that your contributions
  are welcome!

##### A Brief Note On Licensing
Often, python code for an algorithm of interest already exists. In the interest of avoiding repeated reinvention of the wheel, we welcome code from other sources being integrated into `pymdp`. If you are doing this, we ask that you be explicit and transparent about where the code came from and which license it is released under. The safest thing to do is copy the license from the original code into the header documentation of your file. For reference, this software is [licensed under MIT](https://github.com/tlarock/netrd/blob/master/LICENSE).

## Setup
Before starting your contribution, you need to complete the following instructions once.
The goal of this process is to fork, download and install the latest version of `pymdp`.

1. Log in to GitHub.

2. Fork this repository by pressing 'Fork' at the top right of this
   page. This will lead you to 'github.com/<your_account>/infer-actively'. We refer
   to this as your personal fork (or just 'your fork'), as opposed to this repository
   (github.com/infer-actively/pymdp), which we refer to as the 'upstream repository'.

3. Clone your fork to your machine by opening a console and doing

   ```
   git clone https://github.com/<your_account>/infer-actively.git
   ```

   Make sure to clone your fork, not the upstream repo. This will create a
   directory called 'infer-actively/'. Navigate to it and execute

   ```
   git remote add upstream https://github.com/infer-actively/pymdp.git
   ```

   In this way, your machine will know of both your fork (which git calls
   `origin`) and the upstream repository (`upstream`).

4. During development, you will probably want to play around with your
   code. For this, you need to install the `pymdp` package and have it
   reflect your changes as you go along. For this, open the console and
   navigate to the `infer-actively/` directory, and execute

	```
	pip install -e .
	```

	From now on, you can open a Jupyter notebook, ipython console, or your
    favorite IDE from anywhere in your computer and type `import pymdp`.


These steps need to be taken only once. Now anything you do in the `infer-actively/`
directory in your machine can be `push`ed into your fork. Once it is in
your fork you can then request one of the organizers to `pull` from your
fork into the upstream repository (by submitting a 'pull request'). More on this later!

## Recommended local hooks

If you edit notebooks, install the local hooks after syncing the dev tooling:

```
uv sync --group dev
uv run --group dev pre-commit install
uv run --group dev pre-commit run --all-files
```

The notebook hook reads the manifests in `test/notebooks/`:

* Manifest-tested notebooks keep saved outputs but also keep canonical execution counts for output-bearing code cells so `nbval` can execute them reliably.
* Nightly-tier notebooks still run through `nbstripout --keep-output`, but the hook restores canonical execution counts afterwards so the saved notebooks remain valid test inputs.


## Before you start coding

Once you have completed the above steps, you are ready to choose an algorithm to implement and begin coding.

1. Choose which algorithm you are interested in working on.

2. Open an issue at https://github.com/infer-actively/pymdp/issues by clicking the "New Issue" button. 

	* Title the issue "Implement XYZ method", where XYZ method is a shorthand name for whatever function / method / environment class you plan to implement.
	* Leave a comment that includes a brief motivation for why you want to see this method in `pymdp`, as well as any key citations.
	* If such an issue already exists for the method you are going to write, it is not necessary to open another. However, it is a good idea to leave a comment letting others know you are going to work on it.

2. In your machine, create the file where your algorithm is going to
   live. If you chose a softmax algorithm, copy
   an existing file, such as `/pymdp/functions.py`, into 
   `/pymdp/<algorithm_name>.py`. Please keep in mind that
   <algorithm_name> will be used inside the code, so try to choose
   something that looks "pythonic". In particular, <algorithm_name> cannot
   include spaces, should not include upper case letters, and should use underscores 
   rather than hyphens.

3. Open the newly created file and edit as follows. At the very top you
   will find a string describing the algorithm. Edit this to describe the algorithm you
   are about to code, and preferably include a citation and link to any relevant papers. 
   Also add your name and email address (optional).

## After you finish coding

1. After updating your local code, the first thing to do is tell git which files
   you have been working on. (This is called staging.) If you worked on a softmax
   function, for example, do

   ```
   git add pymdp/<your_file>.py
   ```

2. Next tell git to commit (or save) your changes:

	```
	git commit -m 'Write a commit message here. This will be public and
	should be descriptive of the work you have done. Please be as explicit
	as possible, but at least make sure to include the name of the method
	you implemented. For example, the commit message may be: add
	implementation of SomeMethod, based on SomeAuthor and/or SomeCode.'
	```

3. Now you have to tell git to do two things. First, `pull` the latest changes from
   the upstream repository (in case someone made changes while you were coding), 
   then `push` your changes and the updated code from your machine to your fork:

	```
	git pull upstream <target_branch>
	git push origin <your_branch>
	```

	NOTE: If you edited already existing files, the `pull` may result in
	conflicts that must be merged. If you run in to trouble here, ask
	for help!

4. Finally, you need to tell this (the upstream) repository to include your
   contributions. For this, we use the GitHub web interface. At the top of
   this page, there is a 'New Pull Request' button. Click on it, and it
   will take you to a page titled 'Compare Changes'. Right below the title,
   click on the blue text that reads 'compare across forks'. This will show
   four buttons. Make sure that the first button reads 'base fork:
   infer-actively/pymdp', the second button reads the branch you intend to
   merge into (for example `v1.0.0_alpha` for release work), the third
   button reads 'head fork: <your_username>/infer-actively', and the fourth button
   reads the branch from your fork that contains your changes. (If everything has gone according to plan, the
   only button you should have to change is the third one - make sure you
   find your username, not someone elses.) After you find your username,
   GitHub will show a rundown of the differences that you are adding to the
   upstream repository, so you will be able to see what changes you are
   contributing. If everything looks correct, press 'Create Pull
   Request'.
	NOTE: Advanced git users may want to develop on branches other
	than the upstream target branch on their fork. That is totally
	fine; just make sure the PR points at the correct upstream branch.


That's it! After you've completed these steps, maintainers will be notified 
and will review your code and changes to make sure that everything is in place. 
Some automated tests will also run in the background to make sure that your 
code can be imported correctly and other sanity checks. Once that is all done, 
one of us will either accept your Pull Request, or leave a message requesting some
changes (you will receive an email either way).


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Conor Heins and Alec Tschantz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================

<p align='center'>
  <a href='https://github.com/infer-actively/pymdp'>
    <img src='.github/pymdp_logo_2-removebg.png' />
  </a> 
</p>

A Python package for simulating Active Inference agents in Markov Decision Process environments.
Please see our companion paper, published in the Journal of Open Source Software: ["pymdp: A Python library for active inference in discrete state spaces"](https://joss.theoj.org/papers/10.21105/joss.04098) for an overview of the package and its motivation. For a more in-depth, tutorial-style introduction to the package and a mathematical overview of active inference in Markov Decision Processes, see the [longer arxiv version](https://arxiv.org/abs/2201.03904) of the paper.

## Citing `pymdp`
If you use `pymdp` in your work or research, please cite:

```
@article{Heins2022,
  doi = {10.21105/joss.04098},
  url = {https://doi.org/10.21105/joss.04098},
  year = {2022},
  publisher = {The Open Journal},
  volume = {7},
  number = {73},
  pages = {4098},
  author = {Conor Heins and Beren Millidge and Daphne Demekas and Brennan Klein and Karl Friston and Iain D. Couzin and Alexander Tschantz},
  title = {pymdp: A Python library for active inference in discrete state spaces},
  journal = {Journal of Open Source Software}
}
```

This package is hosted on the [`infer-actively`](https://github.com/infer-actively) GitHub organization, which was built with the intention of hosting open-source active inference and free-energy-principle related software.

Most of the low-level mathematical operations are [NumPy](https://github.com/numpy/numpy) ports of their equivalent functions from the `SPM` [implementation](https://www.fil.ion.ucl.ac.uk/spm/doc/) in MATLAB. We have benchmarked and validated most of these functions against their SPM counterparts.

## Status

![status](https://img.shields.io/badge/status-active-green)
[![PyPI version](https://img.shields.io/pypi/v/inferactively-pymdp?cacheSeconds=300)](https://pypi.org/project/inferactively-pymdp/1.0.0/)
[![Documentation Status](https://readthedocs.org/projects/pymdp-rtd/badge/?version=latest)](https://pymdp-rtd.readthedocs.io/en/latest/)
[![DOI](https://joss.theoj.org/papers/10.21105/joss.04098/status.svg)](https://doi.org/10.21105/joss.04098)


# ``pymdp`` in action

Here's a visualization of ``pymdp`` agents in action. One of the defining features of active inference agents is the drive to maximize "epistemic value" (i.e. curiosity). Equipped with such a drive in environments with uncertain yet disclosable hidden structure, active inference can ultimately allow agents to simultaneously learn about the environment as well as maximize reward.

The simulation below (see associated JAX notebook [here](https://pymdp-rtd.readthedocs.io/en/latest/tutorials/notebooks/examples/envs/cue_chaining_demo/)) demonstrates what might be called "epistemic chaining," where an agent (here, analogized to a mouse seeking food) forages for a chain of cues, each of which discloses the location of the subsequent cue in the chain. The final cue (here, "Cue 2") reveals the location a hidden reward. This is similar in spirit to "behavior chaining" used in operant conditioning, except that here, each successive action in the behavioral sequence doesn't need to be learned through instrumental conditioning. Rather, active inference agents will naturally forage the sequence of cues based on an intrinsic desire to disclose information. This ultimately leads the agent to the hidden reward source in the fewest number of moves as possible.

You can run the code behind simulating tasks like this one and others in the **Examples** section of the [official documentation](https://pymdp-rtd.readthedocs.io/en/latest/). The GIF generation script used for these animations is available [here](examples/envs/chained_cue_navigation.py).

<!-- 
<p align="center">
  <img src=".github/chained_cue_navigation_v1.gif" width="50%" height="50%"/>
  <img src=".github/chained_cue_navigation_v2.gif" width="50%" height="50%"/>
</p> -->

<!-- ![alt](.github/chained_cue_navigation_v1.gif) | ![alt](.github/chained_cue_navigation_v2.gif) -->

<table><tr>
<td> 
  <p align="center" style="padding: 10px">
    <img src=".github/chained_cue_navigation_v1.gif" width="100%" height="50%"/>
    <br>
    <em style="color: grey">Cue 2 in Location 1, Reward on Top</em>
  </p> 
</td>
<td> 
  <p align="center">
    <img src=".github/chained_cue_navigation_v2.gif" width="100%" height="50%"/>
    <br>
    <em style="color: grey">Cue 2 in Location 3, Reward on Bottom</em>
  </p> 
</td>
</tr></table>

## Quick-start: Installation and Usage

We recommend installing `pymdp` using [`uv`](https://docs.astral.sh/uv/), with an explicit virtual environment:

```bash
uv venv .venv
source .venv/bin/activate
uv pip install inferactively-pymdp
```

If you prefer `pip`, use:

```bash
pip install inferactively-pymdp
```

If `uv sync --group test` or `uv sync --extra nb` fails while building `pygraphviz`, install Graphviz first and then retry. On macOS, install `graphviz` with Homebrew. On Ubuntu/Debian, install `graphviz libgraphviz-dev pkg-config build-essential python3-dev` (or the version-matched `python3.x-dev` package if needed). The full troubleshooting notes live in [`docs-mkdocs/getting-started/installation.md`](docs-mkdocs/getting-started/installation.md).

Once in Python, you can then directly import `pymdp`, its sub-packages, and functions.

```python
from jax import numpy as jnp, random as jr
from pymdp import utils
from pymdp.agent import Agent

key = jr.PRNGKey(0)
keys = jr.split(key, 3)

num_obs = [3, 5]
num_states = [3, 2]
num_controls = [3, 1]

A = utils.random_A_array(keys[0], num_obs, num_states)
B = utils.random_B_array(keys[1], num_states, num_controls)
C = utils.list_array_uniform([[no] for no in num_obs])

agent = Agent(A=A, B=B, C=C, batch_size=1)
observation = [jnp.array([1]), jnp.array([4])]

qs, info = agent.infer_states(observation, empirical_prior=agent.D, return_info=True)
# Optional diagnostic: current variational free energy for each batch element.
vfe = info["vfe"]
q_pi, neg_efe = agent.infer_policies(qs)

action_keys = jr.split(keys[2], agent.batch_size + 1)
action = agent.sample_action(q_pi, rng_key=action_keys[1:])
```

## Getting started / introductory material

We recommend starting with the JAX-first [official documentation](https://pymdp-rtd.readthedocs.io/en/latest/) for the repository, which provides practical guides, curated notebooks, and generated API references.

For new users to `pymdp`, we specifically recommend stepping through following three Jupyter notebooks (can also be used on Google Colab):

- [Quickstart (JAX)](https://pymdp-rtd.readthedocs.io/en/latest/getting-started/quickstart-jax/)
- [NumPy/legacy to JAX migration guide](https://pymdp-rtd.readthedocs.io/en/latest/migration/numpy-to-jax/)
- [`rollout()` active inference loop guide](https://pymdp-rtd.readthedocs.io/en/latest/guides/rollout-active-inference-loop/)

We also have (and are continuing to build) a series of notebooks that walk through active inference agents performing different types of tasks in the [Notebook Gallery](https://pymdp-rtd.readthedocs.io/en/latest/tutorials/notebooks/).

## Contributing

This package is under active development. If you would like to contribute, please refer to [CONTRIBUTING.md](CONTRIBUTING.md).

Recommended local setup:

```bash
cd <path_to_repo_fork>
uv venv .venv
source .venv/bin/activate
uv sync --group test
```

Recommended contributor hooks:

```bash
uv sync --group dev
uv run --group dev pre-commit install
```

Useful variants:

```bash
# tests + contributor hooks
uv sync --group test --group dev

# docs work
uv sync --group test --extra docs

# notebook/media extras
uv sync --group test --extra nb

# model fitting extras
uv sync --group test --extra modelfit
```

Run tests:

```bash
pytest test
```

Build docs locally:

```bash
./scripts/docs_build.sh
```

## Contributors

- Conor Heins [@conorheins](https://github.com/conorheins)
- Tim Verbelen [@tverbele](https://github.com/tverbele)
- Dimitrije Markovic [@dimarkov](https://github.com/dimarkov)
- Riddhi Jain Pitliya [@riddhipits](https://github.com/riddhipits)
- Arun Niranjan [@Arun-Niranjan](https://github.com/Arun-Niranjan)
- Toon Van de Maele [@toonvdm](https://github.com/toonvdm)
- Ozan Catal [@OzanCatalVerses](https://github.com/OzanCatalVerses)
- Tommaso Salvatori [@salvatomm](https://github.com/salvatomm)
- Aswin Paul [@aswinpaul](https://github.com/aswinpaul)
- Lancelot Da Costa [@lancelotdacosta](https://github.com/lancelotdacosta)
- Ran Wei [@ran-weii](https://github.com/ran-weii)
- Alexander Tschantz [@alec-tschantz](https://github.com/alec-tschantz)
- Miguel de Prado [@praesc](https://github.com/praesc)
- Nikola Pižurica [@NIkolaPizurica](https://github.com/NIkolaPizurica)
- Nikola Milović [@nikolamilovic-ft](https://github.com/nikolamilovic-ft)
- Matteo Risso [@matteorisso](https://github.com/matteorisso)
- Christopher Buckley [@clb27](https://github.com/clb27)
- Beren Millidge [@BerenMillidge](https://github.com/BerenMillidge)
- Daphne Demekas [@daphnedemekas](https://github.com/daphnedemekas)
- Cooper Williams [@coopwilliams](https://github.com/coopwilliams)


================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS    ?=
SPHINXBUILD   ?= sphinx-build
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)


================================================
FILE: docs/agent.rst
================================================
Agent class
=================================

.. autoclass:: pymdp.agent.Agent
    :members:


================================================
FILE: docs/algos/fpi.rst
================================================
FPI (Fixed Point Iteration)
=================================

.. automodule:: pymdp.algos.fpi
    :members:



================================================
FILE: docs/algos/index.rst
================================================
Algos
=================================

The ``algos.py`` library contains the functions for implementing message passing algorithms for variational inference on POMDP generative models

Sub-libraries
---------------

.. toctree::
   :maxdepth: 1

   fpi
   mmp

================================================
FILE: docs/algos/mmp.rst
================================================
MMP (Marginal Message Passing)
=================================

.. automodule:: pymdp.algos.mmp
    :members:

================================================
FILE: docs/conf.py
================================================
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('..'))

# -- Project information -----------------------------------------------------

project = 'pymdp'
copyright = '2021, infer-actively'
author = 'infer-actively'

# The full version, including alpha/beta/rc tags
release = '0.0.7.1'

# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc', 
              'sphinx.ext.doctest',
              'sphinx.ext.coverage', 
              'sphinx.ext.napoleon',
              'sphinx.ext.autosummary',
              'myst_nb'
              ]

source_suffix = {
    '.rst': 'restructuredtext',
    '.ipynb': 'myst-nb'
    }

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
html_theme_options = {
    'logo_only': True,
}

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = '_static/pymdp_logo_2-removebg.png'

html_favicon = '_static/pymdp_logo_2-removebg.png'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']


# -- Options for myst ----------------------------------------------
jupyter_execute_notebooks = "cache"
jupyter_cache = "notebooks"


================================================
FILE: docs/control.rst
================================================
Control
=================================

The ``control.py`` module contains the functions for performing inference of policies (sequences of control states) in POMDP generative models,
according to active inference.

.. automodule:: pymdp.control
    :members:

================================================
FILE: docs/env.rst
================================================
Env
========

The OpenAIGym-inspired ``Env`` base class is the main API that represents the environmental dynamics or "generative process" with
which agents exchange observations and actions

Base class
----------
.. autoclass:: pymdp.envs.Env

Specific environment implementations
----------

All of the following dynamics inherit from ``Env`` and have the
same general usage as above.

.. autosummary::
   :nosignatures:

    pymdp.envs.GridWorldEnv
    pymdp.envs.DGridWorldEnv
    pymdp.envs.SceneConstruction
    pymdp.envs.TMazeEnv
    pymdp.envs.TMazeEnvNullOutcome


================================================
FILE: docs/index.rst
================================================
.. pymdp documentation master file, created by
   sphinx-quickstart on Fri Oct 29 13:27:58 2021.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

Welcome to pymdp's documentation!
=================================

``pymdp`` is a Python package for simulating active inference agents in
discrete space and time, using partially-observed Markov Decision Processes
(POMDPs) as a generative model class. The package is designed to be modular and flexible, to
enable users to design and simulate bespoke active inference models with varying levels of
specificity to a given task.

For a theoretical overview of active inference and the motivations for developing this package, 
please see our companion paper_: "pymdp: A Python library for active inference in discrete state spaces".

.. toctree::
   :maxdepth: 1
   :caption: Installation & Usage

   installation
   notebooks/pymdp_fundamentals
   notebooks/active_inference_from_scratch
   notebooks/using_the_agent_class

.. toctree::
   :maxdepth: 1
   :caption: Examples

   notebooks/tmaze_demo
   notebooks/cue_chaining_demo
   
.. toctree::
   :maxdepth: 2
   :caption: Modules

   inference
   control
   learning
   algos/index

.. toctree::
   :maxdepth: 2
   :caption: Agent and environment API

   agent
   env

.. toctree::
   :maxdepth: 1
   :caption: Additional learning materials

   notebooks/free_energy_calculation

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

.. _paper: https://joss.theoj.org/papers/10.21105/joss.04098


================================================
FILE: docs/inference.rst
================================================
Inference
=================================

The ``inference.py`` module contains the functions for performing inference of discrete hidden states (categorical distributions) in POMDP generative models.

.. automodule:: pymdp.inference
    :members:

================================================
FILE: docs/installation.rst
================================================
Installation
=================================

We recommend installing ``pymdp`` using the package installer pip_, which will install the package locally as well as its dependencies.
This can also be done in a virtual environment (e.g. one created using ``venv`` or ``conda``). 

When pip installing ``pymdp``, use the full package name: ``inferactively-pymdp``:

.. code-block:: console

   (.venv) $ pip install inferactively-pymdp
   
.. _pip: https://pip.pypa.io/en/stable/

================================================
FILE: docs/learning.rst
================================================
Learning
=================================

The ``learning.py`` module contains the functions for updating parameters of Dirichlet posteriors (that paramaterise categorical priors and likelihoods) in POMDP generative models.

.. automodule:: pymdp.learning
    :members:

================================================
FILE: docs/make.bat
================================================
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
	set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
	echo.
	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
	echo.installed, then set the SPHINXBUILD environment variable to point
	echo.to the full path of the 'sphinx-build' executable. Alternatively you
	echo.may add the Sphinx directory to PATH.
	echo.
	echo.If you don't have Sphinx installed, grab it from
	echo.https://www.sphinx-doc.org/
	exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd


================================================
FILE: docs/notebooks/active_inference_from_scratch.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Lggo6pjkdPhS"
   },
   "source": [
    "# Tutorial 1: Active inference from scratch\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/infer-actively/pymdp/blob/main/docs/notebooks/active_inference_from_scratch.ipynb)\n",
    "\n",
    "*Author: Conor Heins*\n",
    "\n",
    "This tutorial guides you through the construction of an active inference agent \"from scratch\" in a simple grid-world environment. This tutorial is designed to walk you through the various mathematical operations required for running active inference in discrete state spaces, as well as introduce you to the way discrete probability distributions are represented in `pymdp` (through the use of numpy vectors and matrices). We also show you how to compute the expected free energy in terms of risk and ambiguity, and how to use it to plan sequences of actions.\n",
    "\n",
    "By the end of this tutorial, you should be able to write simple active inference models from scratch using the main components of any POMDP generative model: the `A`, `B`, `C`, and `D` arrays, as well as be able to write the active inference loop for an agent engaged in a perception-action exchange with its environment."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Note*: When running this notebook in Google Colab, you may have to run `!pip install inferactively-pymdp` at the top of the notebook, before you can `import pymdp`. That cell is left commented out below, in case you are running this notebook from Google Colab."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ! pip install inferactively-pymdp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "hk0kXw1RRmTf"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iSS6oO3RWrwp"
   },
   "source": [
    "### Define some auxiliary functions\n",
    "\n",
    "Here are some plotting functions that will come in handy throughout the tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "id": "Y0QiIF8SWxot"
   },
   "outputs": [],
   "source": [
    "def plot_likelihood(matrix, xlabels = list(range(9)), ylabels = list(range(9)), title_str = \"Likelihood distribution (A)\"):\n",
    "    \"\"\"\n",
    "    Plots a 2-D likelihood matrix as a heatmap\n",
    "    \"\"\"\n",
    "\n",
    "    if not np.isclose(matrix.sum(axis=0), 1.0).all():\n",
    "      raise ValueError(\"Distribution not column-normalized! Please normalize (ensure matrix.sum(axis=0) == 1.0 for all columns)\")\n",
    "    \n",
    "    plt.figure(figsize = (6,6))\n",
    "    sns.heatmap(matrix, xticklabels = xlabels, yticklabels = ylabels, cmap = 'gray', cbar = False, vmin = 0.0, vmax = 1.0)\n",
    "    plt.title(title_str)\n",
    "    plt.show()\n",
    "\n",
    "def plot_grid(grid_locations, num_x = 3, num_y = 3 ):\n",
    "    \"\"\"\n",
    "    Plots the spatial coordinates of GridWorld as a heatmap, with each (X, Y) coordinate \n",
    "    labeled with its linear index (its `state id`)\n",
    "    \"\"\"\n",
    "\n",
    "    grid_heatmap = np.zeros((num_x, num_y))\n",
    "    for linear_idx, location in enumerate(grid_locations):\n",
    "      y, x = location\n",
    "      grid_heatmap[y, x] = linear_idx\n",
    "    sns.set(font_scale=1.5)\n",
    "    sns.heatmap(grid_heatmap, annot=True, cbar = False, fmt='.0f', cmap='crest')\n",
    "\n",
    "def plot_point_on_grid(state_vector, grid_locations):\n",
    "    \"\"\"\n",
    "    Plots the current location of the agent on the grid world\n",
    "    \"\"\"\n",
    "    state_index = np.where(state_vector)[0][0]\n",
    "    y, x = grid_locations[state_index]\n",
    "    grid_heatmap = np.zeros((3,3))\n",
    "    grid_heatmap[y,x] = 1.0\n",
    "    sns.heatmap(grid_heatmap, cbar = False, fmt='.0f')\n",
    "\n",
    "def plot_beliefs(belief_dist, title_str=\"\"):\n",
    "    \"\"\"\n",
    "    Plot a categorical distribution or belief distribution, stored in the 1-D numpy vector `belief_dist`\n",
    "    \"\"\"\n",
    "\n",
    "    if not np.isclose(belief_dist.sum(), 1.0):\n",
    "      raise ValueError(\"Distribution not normalized! Please normalize\")\n",
    "\n",
    "    plt.grid(zorder=0)\n",
    "    plt.bar(range(belief_dist.shape[0]), belief_dist, color='r', zorder=3)\n",
    "    plt.xticks(range(belief_dist.shape[0]))\n",
    "    plt.title(title_str)\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5RsP7hVidcfn"
   },
   "source": [
    "## **The Basics: categorical distributions**\n",
    "\n",
    "Let's start by creating a simple categorical distribution $P(x)$. We will represent this distribution numerically with a 1-D `numpy` array (i.e. a `numpy.ndarray` where `ndim == 1`). The discrete entries of this vector can be thought of as the probabilities of seeing each of the $K$ alternative outcomes (the support of the distribution), if we were to sample once from the distribution. We can write the distribution as a vector of probabilities, follows:\n",
    "\n",
    "\n",
    "$$\n",
    "P(x) = \\begin{bmatrix} P(x = 0) \\\\\\ P(x = 1)\\\\\\ \\vdots \\\\\\ P(x = K) \\end{bmatrix}\n",
    "$$ \n",
    "\n",
    "We can use `numpy` and the `norm_dist` function from `utils` library of `pymdp` to quickly create a random categorical distribution.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymdp import utils"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "P0RZ91Xb34GY",
    "outputId": "2a120bcb-82ca-4e6b-8146-a82eb42e6fcd"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.16880278]\n",
      " [0.51728256]\n",
      " [0.31391466]]\n",
      "Integral of the distribution: 1.0\n"
     ]
    }
   ],
   "source": [
    "my_categorical = np.random.rand(3)\n",
    "# my_categorical = np.array([0.5, 0.3, 0.8]) # could also just write in your own numbers\n",
    "my_categorical = utils.norm_dist(my_categorical) # normalizes the distribution so it integrates to 1.0\n",
    "\n",
    "print(my_categorical.reshape(-1,1)) # we reshape it to display it like a column vector\n",
    "print(f'Integral of the distribution: {round(my_categorical.sum(), 2)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "uJmzJ7tFdptc"
   },
   "source": [
    " We can sample a discrete outcome from this distribution using the `sample()` function from the `utils` module"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "1LvlfKJ54ibe",
    "outputId": "491c96b5-4667-4f8c-8adb-0acf8f0fd5b8"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sampled outcome: 1\n"
     ]
    }
   ],
   "source": [
    "sampled_outcome = utils.sample(my_categorical)\n",
    "print(f'Sampled outcome: {sampled_outcome}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "x5co79ug4UVy"
   },
   "source": [
    "Now plot the beliefs using our `plot_beliefs()` function that we defined in the beginning. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "13yT2uB_dzFc"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_beliefs(my_categorical, title_str = \"A random (unconditional) Categorical distribution\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vfDpOZ25dzpG"
   },
   "source": [
    "Now let's move onto _conditional_ categorical distributions or likelihoods, i.e. how we represent the distribution of one discrete random variable X, conditioned on the settings of another discrete random variable Y.\n",
    "\n",
    "We would write these mathematically as:\n",
    "\n",
    "\n",
    "$$ \\begin{align}\n",
    "P(X | Y)\n",
    "\\end{align}\n",
    "$$\n",
    "\n",
    "And can represent them using 2-D `numpy.ndarrays` i.e. matrices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "_PZ8VMWc46kk",
    "outputId": "55e3edb7-8491-4a76-a751-d41a10bda960"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.089 0.739 0.145 0.399]\n",
      " [0.772 0.201 0.026 0.578]\n",
      " [0.181 0.253 0.788 0.844]]\n"
     ]
    }
   ],
   "source": [
    "# initialize it with random numbers\n",
    "p_x_given_y = np.random.rand(3, 4)\n",
    "print(p_x_given_y.round(3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "G8HyWdgE48Ts",
    "outputId": "c5f3fb1f-b23a-4f8d-a62b-9f7968f08bb1"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.086 0.619 0.151 0.219]\n",
      " [0.741 0.168 0.027 0.318]\n",
      " [0.174 0.212 0.821 0.463]]\n"
     ]
    }
   ],
   "source": [
    "# normalize it\n",
    "p_x_given_y = utils.norm_dist(p_x_given_y)\n",
    "print(p_x_given_y.round(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2doBWPTW5Lix"
   },
   "source": [
    "Now print the first column (i.e. $P(X | Y = 0)$) and show that it's a proper categorical distribution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "9mtgjyb1d7ht"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.08555937]\n",
      " [0.74093812]\n",
      " [0.1735025 ]]\n",
      "Integral of P(X|Y=0): 0.9999999999999999\n"
     ]
    }
   ],
   "source": [
    "print(p_x_given_y[:,0].reshape(-1,1))\n",
    "print(f'Integral of P(X|Y=0): {p_x_given_y[:,0].sum()}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fqsar3k5d8lm"
   },
   "source": [
    "So column `i` of the matrix `p_x_given_y` represents the conditional probability of $X$, given the `i`-th level of the random variable $Y$, i.e. $P(X | Y = i)$\n",
    "\n",
    "Next: taking expectations of random variables using matrix-vector products"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "id": "XKkJuevmd-7H"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.75]\n",
      " [0.25]]\n",
      "[[0.6  0.5 ]\n",
      " [0.15 0.41]\n",
      " [0.25 0.09]]\n"
     ]
    }
   ],
   "source": [
    "\"\"\" Create a P(Y) and P(X|Y) using the same numbers from the slides \"\"\"\n",
    "\n",
    "p_y = np.array([0.75, 0.25]) # this is already normalized - you don't need to `utils.norm_dist()` it!\n",
    "\n",
    "# the columns here are already normalized - you don't need to `utils.norm_dist()` it!\n",
    "p_x_given_y = np.array([[0.6, 0.5],\n",
    "                        [0.15, 0.41], \n",
    "                        [0.25, 0.09]])\n",
    "\n",
    "print(p_y.round(3).reshape(-1,1))\n",
    "print(p_x_given_y.round(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iGFLbNSteN5h"
   },
   "source": [
    "Calculate expected value of $X$, given our current _belief_ about $Y$, i.e. $P(Y)$ using a simple matrix-vector product, of the form $\\mathbf{A}\\mathbf{x}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "id": "JMUFrn0FeOMY"
   },
   "outputs": [],
   "source": [
    "\"\"\" Calculate the expectation using numpy's dot product functionality \"\"\"\n",
    "\n",
    "# first version of the dot product (using the method of a numpy array)\n",
    "E_x_wrt_y = p_x_given_y.dot(p_y)\n",
    "\n",
    "# second version of the dot product (using the function np.dot with two arguments)\n",
    "# E_x_wrt_y = np.dot(p_x_given_y, p_y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "XGzohGsF5taF"
   },
   "source": [
    "Now print it and verify the result is a proper categorical distribution (integrates to 1.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "id": "aqq_fV165yqp"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.575 0.215 0.21 ]\n",
      "Integral: 1.0\n"
     ]
    }
   ],
   "source": [
    "print(E_x_wrt_y)\n",
    "print(f'Integral: {E_x_wrt_y.sum().round(3)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YxwopW4heRcb"
   },
   "source": [
    "## **A simple environment: Grid-world**\n",
    "Now that we understand categorical distributions and how to take conditional expectations of random variables, with categorical conditional and prior distributions, let's move onto a worked example of Active Inference, so-called 'Grid-World'. \n",
    "\n",
    "Before we write down the generative model $P(o,s)$ of an active inference agent who will navigate Grid-World, let's start by establishing what exactly this environment is and how we're going to represent it mathematically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "id": "rZYA7vDreRo5"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]\n"
     ]
    }
   ],
   "source": [
    "import itertools\n",
    "\n",
    "\"\"\" Create  the grid locations in the form of a list of (Y, X) tuples -- HINT: use itertools \"\"\"\n",
    "grid_locations = list(itertools.product(range(3), repeat = 2))\n",
    "print(grid_locations)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "id": "HLr-vvmLefyb"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_grid(grid_locations)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PQ1Rm_w23sLO"
   },
   "source": [
    "## **Building the generative model**: $\\mathbf{A}$, $\\mathbf{B}$, $\\mathbf{C}$, and $\\mathbf{D}$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0q32yTQEeiqw"
   },
   "source": [
    "### 1. The **A** matrix or $P(o\\mid s)$. \n",
    "\n",
    "---\n",
    "\n",
    "The generative model's \"prior beliefs\" about how hidden states relate to observations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "uRUbpndA61Lg",
    "outputId": "e02d2149-884c-48a4-d2b3-a4151ec82a07"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dimensionality of hidden states: 9\n",
      "Dimensionality of observations: 9\n"
     ]
    }
   ],
   "source": [
    "\"\"\" Create variables for the storing the dimensionalities of the hidden states and the observations \"\"\"\n",
    "\n",
    "n_states = len(grid_locations)\n",
    "n_observations = len(grid_locations)\n",
    "\n",
    "print(f'Dimensionality of hidden states: {n_states}')\n",
    "print(f'Dimensionality of observations: {n_observations}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "id": "3QLSmiYu67j4"
   },
   "outputs": [],
   "source": [
    "\"\"\" Create the A matrix  \"\"\"\n",
    "\n",
    "A = np.zeros( (n_states, n_observations) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "id": "k-i2_Uxc6_QY"
   },
   "outputs": [],
   "source": [
    "\"\"\" Create an umambiguous or 'noise-less' mapping between hidden states and observations \"\"\"\n",
    "\n",
    "np.fill_diagonal(A, 1.0)\n",
    "\n",
    "# alternative:\n",
    "# A = np.eye(n_observations, n_states)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DuU1UHLTfAHo"
   },
   "source": [
    "Likelihood matrices show relationships between indices of the state space, which are indexed linearly. Remember the (Y, X) coordinates of the grid world don't necessarily neatly map onto to the locations in the matrix here, which has 'unwrapped' the Grid World's (Y, X) locations into columns/rows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "id": "HdmHQ2l-fKhR"
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXUAAAGDCAYAAADUNoDMAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAAAmy0lEQVR4nO3deXhN977H8c8WtCqGGIpqDFVJjiEVKY6WmBLjQdUxRKJVetCi1DGE6qDVgw6K1DU82jqKFi2pISVNa0pdfTTXcIipFUOphESQUEHW/aM3+9pNshORZMev79fzrOexf2vttb7Z2T77t39r5bdslmVZAgAYoYSrCwAAFBxCHQAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqcLBr1y55e3urRYsWSk9Pd3U5WSQlJenq1au5bjdw4EC1b9++CCoqfEePHlWDBg30/fff53sfAwcO1MCBA/P13OjoaDVq1EgnTpzI9/FRdAh1OFi/fr0eeOABpaSk6LvvvnN1OQ62bdumzp07Kzk5Oddthw8frsmTJxdBVfmTmpoqHx8feXt72xc/Pz/17NlTK1eudNh2xowZatq0qZ588kmX1BoYGCgvLy+99957Ljk+7kxJVxeA4iM9PV1RUVHq2bOnNmzYoLVr16pz586uLstu//79unz5cp62dVUA5tXBgwdlWZa6du2qtm3bSpISExO1bNkyvfbaa7LZbOrbt6/27Nmj77//XvPmzXNpvc8884wmTpyoY8eOqX79+i6tBc7RU4fdtm3bdPnyZbVo0UKtWrVSTEyMzp8/7+qyjBQXFydJ6tmzp335xz/+oTlz5kiSNm3aJElasWKFPDw81KZNG5fVKklBQUEqU6aMPv/8c5fWgdwR6rBbv369bDabmjVrpqCgIN28eVNfffVVnp7bvn17vfnmm1q9erU6deokX19f9e7dW/v379f58+c1evRo+fn5qXXr1po1a5YyMjLsz7UsS5999pn+/ve/y8/PT40bN1bnzp21aNEiZc43FxYWpg8//FCS1KFDB/v4cPv27TVlyhRNnjxZvr6+CggIUHJyssOY+vbt2+Xt7a3Ro0c71Pzqq6/K29tb27dvd/qz/fjjjxo0aJD8/Pzk5+enZ555Rrt3787y82dXR04OHDggSWrUqJFDe/Xq1SX9Pjxz8+ZNRUdH64knnlCpUqWy3c+JEycUFham1q1bq1GjRgoKCtLHH3+svM7Tl5qaqvnz56t79+7y9/dX06ZN1bVrV02dOtVhu7Jly8rf31+bN2/O037hOgy/QNLv/7m3bt2qJk2aqEqVKmrTpo1Kly6tiIgIPf/883naR3R0tKKiovTss8/KsizNnz9fo0aNUrly5VS/fn2FhYUpKipKCxcuVN26ddWrVy9J0uzZs7VgwQL16tVLffv2VVpamiIiIvT++++rbNmyCgkJUb9+/ZSamqpvvvlGkyZNchgC2Lhxox555BFNnjxZFy5cUKVKlRzqCggIUK9evbR27Vrt2LFDrVu3VkxMjFatWqX+/fsrICAgx5/p22+/1ciRI1WrVi298MILkqTVq1dr0KBBmjt3rjp06JDnOm4XFxenGjVqqEqVKg7tO3bskCT5+vrq4MGDunr1qnx9fbPdR0xMjEaOHKlq1aopNDRU5cuX19atWzVz5kylpKRo7NixOR5f+n24bcCAATp79qyefvppPfroo7p27ZqOHj2qkydPZtnez89PMTEx+vnnn1WvXj2n+4YLWYBlWV988YXl5eVlffTRR/a2oUOHWl5eXta+fftyfX67du0sb29v6/Dhw/a2mTNnWl5eXtaYMWPsbWlpaVbDhg2tsWPHWpZlWenp6VbTpk2tl19+2WF/V65csRo1amQNGzbM3jZ37lzLy8vLOn36tMNxfXx8rHPnzjk8PzQ01GrXrp39cUpKivXkk09aQUFBVnJystW2bVsrKCjISktLy/FnunHjhhUQEGC1adPGunLlir390qVLVuvWra3WrVtb6enpTuvITmpqquXj42M9//zzVlJSkpWUlGQdO3bM+uSTTyw/Pz/rr3/9q/Xrr7/afyfR0dFZ9nHq1CmrSZMmVnBwsHX16lWHdX369LEaNWrk0B4aGmqFhoY6bBcZGWl5eXlZO3bsyLVmy7KsiIgIy8vLy9q0aVOetodrMPwCSdKGDRsk/T52minz32vWrMnTPmrVqiVvb2/747p162bZ5wMPPKDKlSvbx+pLlSqlnTt36s0333TY18WLF+Xu7p6nyxdr1aqlatWqOd2mQoUKeuONN3Ty5En16dNHCQkJmjFjhh544IEcnxMXF6dz584pJCRE7u7u9vby5csrNDRUCQkJ9mGUvNYhSYcOHVJGRoa2b9+uli1bqmXLlurWrZveeecdNW/eXJ9//rmqV69uH76pUKFCln3Mnz9f165d07Rp01SmTBmHdc2bN1d6errOnj3rtI7Mk8779+93GA7LScWKFSX9flkpii+GX6DExETt2rVLderUkc1m0y+//CJJ8vHxkc1mU2RkpCZPnqzSpUs73U/lypUdHru5uUlSlmEINzc3hzHfUqVKaevWrfr2228VHx+vkydP6tKlS5KUp7HhPx43J4GBgerYsaOioqIUHByspk2bOt0+83XI/HC63SOPPCJJOnv2rPz8/O6ojoMHD0qSJk+erPr168tms8nd3V1169Z1+PCw2WzZPj8jI0PR0dFq0aKFvY7bZb5mfwz7P+rUqZNWrFihOXPmaPny5WrXrp06dOigNm3aqESJnPt7OdWF4oFQhyIjI5WRkaETJ044jBFnunTpkqKjo9W1a1en+ylZMvu3k7MQsCxLL774orZs2SJ/f3/5+fmpX79+atasmZ599tk81Z/54ZGbq1ev2q86iYmJ0dWrV5321J19oGSuu/0EZl7ryKyhR48e8vDwyHG7zA/DlJQUh/Zz587p0qVLevTRR7N93rFjx1ShQgXVqFHDaR0VK1bUmjVrtHPnTm3btk3fffedVq9eLT8/Py1dujTLh3hmHc7OFcD1CHXYr3qZMWOGQ09Rkg4fPqzw8HCtXbs211DPjx9//FFbtmzRiy++6HB1ys2bN5WSkiJPT88CO9asWbN05swZTZgwQe+++65mzZqlKVOm5Lh9zZo1JUnHjx/Psi4+Pl7S/1+tcicOHjyoBx980GmgS7KfDP7jScvMD8nsroj59ddftXPnTvXo0SNPPWo3Nze1bt1arVu31qRJkxQWFqZ169bp8OHDWU7Qnjp1yqEuFE+E+p9cfHy8Dhw4oBYtWuipp57Ksr5Nmzb6/PPP9f333yshISFPY8Z3IrP398de56pVq3Tt2jXdvHnT3pY5JJCXIZk/io2N1fLly9W3b18NGTJEx48f1/Lly9W5c2c9/vjj2T6nYcOGqlq1qj777DMNGDDA/oGXmpqqFStWqGrVqlkuSczNtWvXdPz4cT3xxBO5btugQQO5u7tr3759Du3Vq1eXu7t7lssqf/vtN02YMEElSpTQsGHDnO47OTlZHh4eDsHv5uYmNzc32Wy2bH/Pe/fuVZUqVbId8kHxQaj/yWWeIP373/+e7fpSpUqpd+/eWrBggb766isNHTq0QI/v5+cnd3d3TZ8+XWfOnFGFChX0ww8/KDIyUvfdd5/S0tLs22Z+7V+8eLECAgKyHSrKzvXr1/XKK6+oUqVKGjdunCRp3Lhxio6O1iuvvKJ169bpvvvuy/K8UqVKacqUKXr55ZfVu3dv+2v0xRdfKDExUXPnznU69pydw4cP69atWw4nlHPi5uamjh07Kjo6Wunp6fbhEJvNphdeeEHvvvuuhg0bprZt2+rKlSv68ssvdfbsWc2ePVu1a9d2uu+ZM2cqNjZWHTp0UK1atWRZlmJiYrRlyxYNGTIkS6inpaUpNjZWvXv3vqOfF0WPq1/+5DZs2KBy5cqpY8eOOW7Tt29flShRQmvXri3w41epUkWLFi2Sp6en5s+fr1mzZuns2bOaNWuWBgwYoJ9++kkXLlyQJHXr1k1PPPGE1qxZc0fzkISHhys+Pl4TJ05U+fLlJUkeHh4aP368Tpw4odmzZ+f43M6dO+vjjz/Wgw8+qHnz5mnhwoV6+OGHtXTpUgUGBt7xz5s5np6XUJek4OBgXb58WVu2bHFoHzJkiMaPH6+ff/5Zb7/9tpYuXapGjRppzZo1efqwa9mypR555BF9/fXXmj59uhYsWKBr165p3rx5mjBhQpbto6KidO3aNfXr1y9PdcN1bFZ+vssCKDJDhgzRtWvXtGLFinzvI/MvcD/99NN8Pb9Xr16qWbOm/a96UXzRUweKubCwMO3du1cxMTEuOX50dLSOHTtmH7pC8UZPHfgTuNueOu4d9NQBwCD01AHAIPTUAcAgXKeeDea2AFCcJSZeVtWq5bJdR08dAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCDEOoAYBBCHQAMQqgDgEEIdQAwCKEOAAbJ84ReZ86cUXx8vFJTU1WiRAmVK1dOdevWVfXq1QuzPgDAHcg11KOiojRnzhwdP35cf5x63WazqXbt2hozZow6d+5caEUCAPLGaahHREQoLCxMXbp00ahRo1S7dm2VLVtWkpSamqqTJ09q8+bNevnll3Xjxg117969SIoGAGTP6Z2PunbtqhYtWuj11193upPXX39dsbGx2rBhQ4EX6ArMpw6gOMv3fOpnzpxRYGBgrgcIDAzU6dOn81cdAKDAOA11T09PxcTE5LqTrVu3csIUAIoBp2Pqw4cP1/jx45WYmKiOHTuqbt26cnd3lySlpaXZx9Q3bNigqVOnFknBAICcOR1Tl6TIyEjNnj1bp06dyjLWbFmWHn74YY0YMUK9evUq1EKLEmPqAIozZ2PquYZ6ptOnT+v48eNKTU2VZVn269Rr1apVoMUWB4Q6gOKsQEL9z4RQB1Cc5fvqFwDAvYVQBwCDEOoAYBBCHQAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBnN4koygUxxkRi+PElcXxdQJQ/NBTBwCDEOoAYBBCHQAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGIdQBwCCEOgAYJNebZCQkJNzRDqtVq5bvYgAAdyfXUO/QoYNu3bqV5x0eOnTorgoCAORfrqG+evVqDRs2TOnp6frnP/+pkiVdfgc8AEAOck3ov/zlL/rkk0/Ut29fnT9/Xi+++GJR1AUAyIc8nSitV6+exo4dq8WLFys5ObmwawIA5FOex1L69++v+vXrF2YtAIC7ZLMsy3JpATabKw+fLRe/JNkqjq8TANdITLysqlXLZbuO69QBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCDEOoAYBBCHQAMwm2MslEcZ0Rk5kgAeUFPHQAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGyVOoHz58WN98843i4+OzXX/x4kWtX7++QAsDANw5m+Vk+r+0tDSNGTNGMTExsixLNptNQUFBeuutt1ShQgX7dvv27VP//v116NChOy+Amf7yhFkaAWRKTLysqlXLZbvOaU89PDxc+/fv1/vvv6+IiAiNGDFC27ZtU2hoqC5cuFAoxQIA8s9pqH/77bcaM2aMunbtKh8fH40cOVJLly5VQkKC/vGPfyg1NbWo6gQA5IHTUL9w4YLq1Knj0PbYY49p/vz5On78uEaNGqWbN28WZn0AgDvgNNQ9PT21a9euLO3+/v6aPn26du3apYkTJxLsAFBMOL2dXXBwsKZNm6a0tDR169ZNfn5+9nVdu3ZVQkKCZs6cqX379hV6oQCA3DkN9f79++vKlSv66KOPZLPZHEJdkp577jm5u7vr7bffLtQiAQB54/SSxtulpqbK3d0923XJycnavn27nnrqqTsvgMvi8oRLGgFkcnZJY55DvbAQDHlDqAPIlO/r1AEA9xZCHQAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGcTqfOoqP4jgjIjNHAsUPPXUAMAihDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGCTfE3rt3btXiYmJ8vLyUp06dQqwJABAfuUa6ps2bdKyZct0/fp19evXTz169NDQoUP1ww8/yLIs2Ww29enTR2+++WZR1AsAcMJpqG/YsEHjxo1Ts2bNVL58eb322muKjo5WXFycpk+froYNGyomJkYffPCB6tSpo8GDBxdV3QCA7FhO9OjRw5o5c6b98cKFCy0fHx9r2bJlDtuFh4dbHTt2dLarHEliuUeX4sjVrwkLS1EsiYmXc/w/4PRE6YkTJxQQEGB/3Lt3b1mWJW9vb4ft/P39de7cOWe7AgAUAaehXr16de3fv9/+uHLlypo7d65q1KjhsN2BAwf00EMPFU6FAIA8czqmHhwcrHfffVfnz5/X0KFDVbVqVXXs2NG+/vz581q5cqUWLVqk0aNHF3qxAADnnPbUBw0apFGjRmndunVKSUnJsn7nzp2aN2+e+vTpo0GDBhVSiQCAvLL938klp27evCk3N7csN/W9dOmSMjIy5OHhkf8CuFHwPSsPb50ix/sJfwaJiZdVtWq5bNfl6Y+PSpbMfrMKFSrkvyoAQIFjmgAAMAihDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCD5GmWRiA7xXGa2+I4HbBUPF8rmImeOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCDEOoAYBBCHQAMku9Qj4iI0KVLlwqyFgDAXcpXqN+6dUuTJk3SmTNnCroeAMBdsFk5zFXaqVMnp088efKkatSoodKlS0uSNm/enL8CmJIUBYipd/FnkJh4WVWrlst2XY7zqdevX1/R0dGqUaOG/vrXvzqssyxLJ0+elLe3tzw8PAq2WgBA/llOREREWM2bN7fGjh1rXbx40d5+48YNy9vb2zpw4ICzp+eJJBaWAluKK1e/LixmLYmJl3N8rzkdU+/Zs6fWr1+vK1eu6G9/+5uio6Ml8VUSAIqrXE+UPvjgg1q0aJFGjx6tiRMn6p///KdSUlKKoDQAwJ3K89Uvffr00bp165SUlKTu3bvTWweAYuiOLmmsWbOmlixZohdffFGPP/64ypYtW1h1AQDyIcdLGousAHr8KEAufjvniPc5CpKzSxqZJgAADEKoA4BBCHUAMAihDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAgOd6jFLgXFdfZEIvj7JHF9bXC3aGnDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCDEOoAYJB8zdL43//93zp8+LA8PDzk7+8vT0/Pgq4LAJAPTkO9adOmWrp0qRo1aiRJSktL0wsvvKDdu3fbpxItWbKkQkNDFRYWVvjVAgCcchrqV69eVUZGhv3xzJkzdfDgQb3zzjtq27atfvvtN23cuFHvv/++qlSpoueff77QCwYA5OyOhl82bdqkUaNGqXv37pKkcuXKadCgQUpNTdWqVasIdQBwsTs6UXrjxg37UMzt/P39lZCQUGBFAQDyJ9dQP3HihK5fvy7p9/A+cuRIlm1iY2NVs2bNgq8OAHBHbJaTmyf6+voqPT1dbm5u8vT0VKlSpfTLL79o1apVql+/vk6fPq0VK1bo008/1csvv6whQ4bceQHcJxF/AtyjFAUpMfGyqlYtl+06p2Pqe/bsUXx8vA4fPqxDhw7p8OHDunjxoi5duiRJ2rVrl/79739rwIABGjRoUIEXDgC4M0576rlJTk6WzWaTh4dH/gugt4A/AXrqKEj57qnnplKlSnfzdABAAWOaAAAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGIdQBwCCEOgAYhFAHAIPc1SyNAPKmOE5zy3TAZqKnDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCD5GlCr+vXr+u+++6zP/7ll1909OhR2Ww2NWnSRB4eHoVWIAAg75yG+tWrV/Xqq6/qP//5j6KionT9+nW98cYb+uqrr5SRkfH7DkqW1MCBAzVx4sQiKRgAkDOnoT5r1izt2LFDY8aMkSRNnz5dmzZt0oQJE9SqVSvduHFD3333nRYsWCAPDw8NHTq0KGoGAOTEcqJVq1bW559/bn/s7+9vLVmyJMt2CxcutNq1a+dsVzmSxMLC4oKlOHL1a3KvLImJl3N8DZ2eKL1y5Ypq1aplf3zr1i35+Phk2a5x48ZKSkpytisAQBFwGuoNGjTQypUr7Y/btm2rqKioLNutWbNG9erVK/jqAAB3xPZ/X3mytXv3bg0ePFh+fn4KDg5W+fLlFRYWJn9/fwUEBCg9PV0bN25UbGyswsPD1aFDhzsvgNtXAS7h5L++y5AHeZOYeFlVq5bLdp3TUJekvXv36r333lNsbKykrG+Ehx56SOPGjVPXrl3zVRy/RMA1CPV7112FeqaLFy/q6NGjSk5O1o0bN1S2bFnVqVPnrodd+CUCrkGo37sKJNQLC79EwDUI9XuXs1BnmgAAMAihDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCDOL3xNABzFccZEZk58u7RUwcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGIdQBwCCEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQZyGekREhC5evFhUtQAA7pLTUA8LC1Pv3r31n//8p6jqAQDchVyHX8qUKaP+/ftr2rRpSk1NLYqaAAD5lGuo/+tf/9KECRO0du1atW/fXuHh4UpJSSmC0gAAdyrXULfZbHr22We1efNmde7cWYsXL1ZAQIBGjBihjRs36tdffy2KOgEAeWCznNxqxMfHR6tWrZKvr6+9LTk5WatWrVJUVJTi4uJks9l0//33q2LFitqyZcudF3CP3VUEQOHhzkd5k5h4WVWrlst23R3fzq5SpUoaPny4hg8froSEBO3fv19HjhxRUlLSXRcKALg7d3WP0mrVqikoKEhBQUEFVQ8A4C44HVNfunSp6tWrV1S1AADuktMx9SIpoBiOVwFwDcbU88bZmDrTBACAQQh1ADAIoQ4ABiHUAcAghDoAGIRQBwCDEOoAYBBCHQAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABjkruZTB4CCVBxnRCyOM0eeP38lx3X01AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGIdQBwCCEOgAYhFAHAIMQ6gBgkHxP6PXTTz/p2LFjqlmzpnx9fQuyJgBAPuUa6ps3b9aKFSuUkpKip556Ss8995ymTZum5cuXy7Is2Ww2tWnTRuHh4SpVqlRR1AwAyIHT4Zf169dr9OjRunXrljw9PfX+++9r0qRJ+vLLLzV16lRFRkZq2rRp2r17txYvXlxUNQMAcmI50b17d2vGjBn2x19//bXl4+NjLV682GG7xYsXW126dHG2qxxJYmFhYSm2S3GUmHg5x3VOe+qnT59WQECA/XGrVq1kWZYee+wxh+0aN26sM2fOONsVAKAIOA31GjVqKCYmxv44899xcXEO28XFxaly5cqFUB4A4E44PVE6cOBATZ06VQcPHlTZsmW1fft2PfXUU5o7d67Kly+vpk2bKjY2Vh9++KFCQkKKqmYAQA6chnpwcLBKlCihtWvX6vLly3rrrbfUrVs3nT9/XmFhYbLZbLIsS4GBgRo5cmRR1QwAyIHNsvJ3V9U9e/bo7Nmzqlu3rho0aJD/AorhjWYBIFM+I7JQnT9/RVWrlst2Xb5DvaAQ6gCKs3st1JkmAAAMQqgDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BBCHUAMAihDgAGIdQBwCAun6URAFBw6KkDgEEIdQAwCKEOAAYh1AHAIIQ6ABiEUAcAgxDqAGAQQh0ADEKoA4BB7vlQ37Bhg7p16yZfX1916dJFERERri7JwaFDh9SwYUOdO3fOpXVkZGTos88+U/fu3eXn56fAwEBNnz5dqampLqvJsiwtWbJEnTp1kq+vr3r06KH169e7rJ7sjBw5UkFBQS6t4ebNm/L19ZW3t7fD4ufn59K6JGn37t0KDg7WY489platWumtt95SWlqaS2r54YcfsrxGty9r1651SV2S9Nlnn6lLly5q0qSJunfvrnXr1hXasUoW2p6LQGRkpMaNG6dnn31WrVq1UnR0tCZOnKj7779fnTt3dnV5+vnnnzVs2DDdvHnT1aVo8eLFmj17toYMGaKWLVsqPj5ec+fO1U8//aSPPvrIJTUtXLhQc+fO1ahRo9SkSRNt375d48aNk5ubm7p27eqSmm731Vdf6ZtvvlGtWrVcWkd8fLyuX7+umTNnqk6dOvb2EiVc2yfbu3evnnvuObVv317z58/XyZMnNWvWLCUnJ+uDDz4o8noaNmyolStXOrRZlqVXXnlFV69eVZs2bYq8JklauXKl3njjDQ0ePFitW7fWtm3bNH78eJUqVUpdunQp+ANa97DAwEBrzJgxDm2jR4+2Onfu7KKKfnfjxg1r2bJllp+fn9W8eXPLy8vL+vXXX11WT0ZGhtWsWTPrjTfecGjfuHGj5eXlZcXFxRV5Tenp6VazZs2sN99806E9NDTUCg4OLvJ6/ujcuXNWs2bNrICAACswMNCltaxbt87y8fGxrl696tI6/igkJMQKCQmxMjIy7G3Lli2zOnToUGxqXbJkieXj42Pt3bvXZTX069fPGjhwoEPbgAEDrNDQ0EI53j07/HL69GmdOnVKHTt2dGjv1KmTjh8/rtOnT7uoMik2NlbvvfeeBg8erHHjxrmsjkxpaWnq0aOH/va3vzm0P/LII5KkU6dOFXlNbm5u+vTTTzV06FCH9lKlSun69etFXs8fTZkyRU8++aRatmzp6lJ06NAh1apVS2XKlHF1KXbJycn68ccfFRwcLJvNZm8PCQlRdHR0saj1/PnzmjNnjn14yFWuX7+usmXLOrRVrFhRKSkphXK8ezbUjx8/LkmqW7euQ3vt2rUl/f6V1VXq1aun6OhojRw5Um5ubi6rI5O7u7umTJkif39/h/bo6GhJ0qOPPlrkNZUoUULe3t6qVq2aLMvShQsXtGjRIu3cuVP9+vUr8nput3r1ah08eFCvvvqqS+vIdOTIEZUuXVpDhgyRn5+fmjVrptdee82l50OOHj0qy7JUoUIFjRkzRk2aNJG/v79ef/11/fbbby6r63bh4eEqUaKExowZ49I6nnnmGe3YsUNff/21UlNTtWnTJm3dulU9e/YslOPds2PqV65ckfR7YN0u8xPRlW/4KlWquOzYebVv3z4tWrRIgYGBqlevnktriYqK0ksvvSRJatu2rXr06OGyWs6cOaPp06dr+vTpqlSpksvquN3hw4eVmpqqPn36aPjw4Tpw4IDCw8MVHx+vpUuXOvSUi0pycrIkKSwsTEFBQZo/f76OHDmi2bNn6/r165oxY0aR13S7pKQkRUREaPDgwSpfvrxLa+nWrZt27drl8OHSq1cvPf/884VyvHs21K1cpoF39Umk4iw2NlbDhw/Xww8/rGnTprm6HDVo0EDLli3TkSNHNGfOHA0dOlT//ve/izysLMvS5MmT1aZNG3Xq1KlIj+3MBx98oAoVKsjb21uS1KxZM1WuXFnjx4/Xzp079eSTTxZ5TTdu3JAkNW3aVK+//rokqWXLlrIsSzNnztSIESPk6elZ5HVlWr16tTIyMvTMM8+4rIZML7zwgvbs2aNJkyapQYMG2rdvn/7rv/7L/g26oN2zoV6uXDlJynL5VGYPPXM9HEVGRiosLEx16tTR4sWL5eHh4eqS5OnpKU9PTzVr1kzu7u6aOHGi9uzZo6ZNmxZpHcuXL9eRI0e0fv16+xVLmZ2Hmzdvys3NzSW94ubNm2dpa9u2raTfe/GuCPXMb8QBAQEO7a1atdKMGTN05MgRl4b65s2b1bp1a5d/2/qf//kfxcTEaPr06Xr66acl/f77LF++vF577TX17dtXXl5eBXrMe7Y7mzmW/seTfCdPnnRYj//3ySefaOzYsWrSpImWL1+uBx980GW1pKSkKCIiQgkJCQ7tDRo0kCQlJiYWeU2bN2/WxYsX1apVKzVs2FANGzZURESETp06pYYNG7rkOuekpCStXr06y4n/zHFrV30oZ15amZ6e7tCe2YN3xYdfpoSEBMXFxRXO5YJ36OzZs5KUpYPy+OOPS5J++umnAj/mPRvqtWvX1sMPP6xNmzY5tEdFRalOnTp66KGHXFRZ8bR69WrNmDFDXbp00eLFi13+TSYjI0NhYWFZriv+/vvvJanAey95MXXqVH3xxRcOS7t27VS9enX7v4uazWbTa6+9pmXLljm0R0ZGys3NLcvJ76JSr1491axZU5GRkQ7tW7ZsUcmSJV36h1H79u2TJJe9NrfL7FzGxsY6tO/du1eSVLNmzQI/5j07/CJJI0aM0KRJk1ShQgW1bdtW3377rb7++muX/OFDcZaUlKS3335bNWvWVEhIiOLi4hzW16pVq8i/plaqVEkDBgzQokWLdP/996tx48aKjY3VwoUL1adPH/vllkUpu2NWrFhRpUuXVuPGjYu8Hun31ykkJESffvqp3N3d9fjjjys2NlYLFixQSEiI/Wqvomaz2TRu3DiNHTtW48aN09NPP60DBw5o/vz5Cg0Ndemwx9GjR1WmTJlCCcw71bBhQwUGBurtt9/WlStX9Je//EUHDhzQvHnzFBAQUCiXWt7Tof70008rPT1dH3/8sVavXi1PT0/NnDmzWPw1YnGyY8cOXbt2TWfOnFFISEiW9e+8806hXV7lzKRJk1SjRg198cUXCg8PV/Xq1fXSSy9pyJAhRV5LcTZx4kRVq1ZNX375pRYtWqRq1arppZdeKrSrJ/Kqa9euKl26tObNm6dhw4apcuXKGjFihIYNG+bSui5cuODyK15u98EHH+jDDz/UkiVLlJSUpJo1a2rw4MFZ/kajoNis3C4jAQDcM+7ZMXUAQFaEOgAYhFAHAIMQ6gBgEEIdAAxCqAOAQQh1ADAIoQ4ABiHUAcAg/wuOGdWSFbwmYgAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 432x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_likelihood(A, title_str = \"A matrix or $P(o|s)$\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "xZx08e6YfL7r"
   },
   "source": [
    "How could we introduce uncertainty into this A matrix? I.e. how do we introduce ambiguity or uncertainty into the agent's model of the world? "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "id": "OQycm296fCZN"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\"\"\" Remind yourself of the mapping between linear indices (0 through 8) and grid locations (Y, X) \"\"\"\n",
    "plot_grid(grid_locations)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "id": "MboJWmdqfRX0"
   },
   "outputs": [],
   "source": [
    "A_noisy = A.copy()\n",
    "\n",
    "# this line says: the probability of seeing yourself in location 0, given you're in location 0, is 1/3, AKA P(o == 0 | s == 0) = 0.3333....\n",
    "A_noisy[0,0] = 1 / 3.0 # corresponds to location (0,0)\n",
    "\n",
    "# this line says: the probability of seeing yourself in location 1, given you're in location 0, is 1/3, AKA P(o == 1 | s == 0) = 0.3333....\n",
    "A_noisy[1,0] = 1 / 3.0 # corresponds to one step to the right from (0, 1)\n",
    "\n",
    "# this line says: the probability of seeing yourself in location 3, given you're in location 0, is 1/3, AKA P(o == 3 | s == 0) = 0.3333....\n",
    "A_noisy[3,0] = 1 / 3.0 # corresponds to one step down from (1, 0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "id": "7hh5dxG-fRva"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_likelihood(A_noisy, title_str = 'modified A matrix where location (0,0) is \"blurry\"')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "id": "2duo3znv8SQp"
   },
   "outputs": [],
   "source": [
    "\"\"\" Let's make ake one grid location \"ambiguous\" in the sense that it could be easily confused with neighbouring locations \"\"\"\n",
    "my_A_noisy = A_noisy.copy()\n",
    "\n",
    "# locations 3 and 7 are the nearest neighbours to location 6\n",
    "my_A_noisy[3,6] = 1.0 / 3.0\n",
    "my_A_noisy[6,6] = 1.0 / 3.0\n",
    "my_A_noisy[7,6] = 1.0 / 3.0\n",
    "\n",
    "# Alternatively: you could have the probability spread among locations 3, 4, 6, and 7. This is basically saying, that whole lower-left corner of grid-world is blurry, if you're in location 6\n",
    "# Remember to make sure the A matrix is column normalized. So if you do it this way, with the probabilities spread among 4 perceived locations, then you'll have to make sure the probabilities sum to 1.0\n",
    "# my_A_noisy[3,6] = 1.0 / 4.0\n",
    "# my_A_noisy[4,6] = 1.0 / 4.0\n",
    "# my_A_noisy[6,6] = 1.0 / 4.0\n",
    "# my_A_noisy[7,6] = 1.0 / 4.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "e4eyxN618ZgW"
   },
   "source": [
    "Now plot the new A matrix, that now has two \"blurry\" locations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "id": "CZDtd3u63Qn0"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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HpWy9y6Qnj59isTERLi6ur72xqFEJD5vb2+UL18eq1atwrfffgsdHR00bdoUK1euLPLaGpVd7+01JCIiKlve22tIRERUtrxXp+zyf120KF79ABsRkWjelxNh7CEREZEQGEhERCQEBhIREQmBgUREREJgIBERkRAYSEREJAQGEhERCYGBREREQmAgERGREBhIREQkBAYSEREJgYFERERCKJGbq96+fRvXrl1DRkYGdHR0ULlyZTRs2BB16tQpieaIiKgMkDWQdu/ejYULF+Lq1asF7i6rUCjQoEEDBAUFwc3NTc5miYioDJAtkGJiYjB+/Hh07twZI0aMQIMGDVCpUiUAQEZGBm7cuIH4+HiMHj0aubm58PLykqtpIiIqA2T7xlh3d3c4Ojpi2rRpr51v2rRpSEpKwo4dO966DX4fEhHR2/vgvg/p9u3bcHV1feN8rq6uuHnzplzNEhFRGSFbIBkbGyMhIeGN8x08eJCDG4iIqADZriEFBgZi7NixuH//Pjp27IiGDRvCwMAAAJCZmam+hrRjxw5Mnz5drmaJiKiMkC2QPD09oaOjgwULFiA2NhYKhULjeUmSUL9+fcyaNQvdu3eXq1kiIiojZB327e7uDnd3d9y8eRNXr15FRkYGJElSfw7JxMREzuaIiKgMKZEPxhobG8PY2LgkFk1ERGUUbx1ERERCYCAREZEQGEhERCQEBhIREQmBgUREREJgIBERkRAYSEREJAQGEhERCYGBREREQmAgERGREBhIREQkBAYSEREJgYFERERCKJG7fX9IRPyu+le/i4qI6H3AHhIREQmBgUREREJgIBERkRAYSEREJAQGEhERCYGBREREQmAgERGREBhIREQkBAYSEREJgYFERERCYCAREZEQGEhERCQEBhIREQmBgUREREJgIBERkRAYSEREJARZv6Dv3r17bzV/7dq15WyeiIjeY7IGUvv27fHixYtiz3/+/Hk5mycioveYrIEUHR2NgIAA5OTkYMyYMdDV5TekExFR8ciaGI0bN8ZPP/2Enj174sGDBxg6dKiciyciojJM9kENZmZm+Prrr7F8+XI8evRI7sUTEVEZVSLn1Hr37o1GjRqVxKKJiKiMKpFAKleuHFq0aFESiyYiojKKn0MiIiIhMJCIiEgIDCQiIhICA4mIiITAQCIiIiEwkIiISAgMJCIiEgIDiYiI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",
      "text/plain": [
       "<Figure size 432x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_likelihood(my_A_noisy, title_str = \"Noisy A matrix now with TWO ambiguous locations\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VVh-pexGuwnh"
   },
   "source": [
    "### 2. The **B** matrix or $P(s_{t}\\mid s_{t-1}, u_{t-1})$.\n",
    "\n",
    "\n",
    "---\n",
    "\n",
    "The generative model's \"prior beliefs\" about (controllable) transitions between hidden states over time. Namely, how do hidden states at time $t$ result from hidden states at some previous time $t-1$. These transition dynamics are further conditioned on some past action $u_t$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "id": "ihPWs7X_fVw3"
   },
   "outputs": [],
   "source": [
    "actions = [\"UP\", \"DOWN\", \"LEFT\", \"RIGHT\", \"STAY\"]\n",
    "\n",
    "def create_B_matrix():\n",
    "  B = np.zeros( (len(grid_locations), len(grid_locations), len(actions)) )\n",
    "\n",
    "  for action_id, action_label in enumerate(actions):\n",
    "\n",
    "    for curr_state, grid_location in enumerate(grid_locations):\n",
    "\n",
    "      y, x = grid_location\n",
    "\n",
    "      if action_label == \"UP\":\n",
    "        next_y = y - 1 if y > 0 else y \n",
    "        next_x = x\n",
    "      elif action_label == \"DOWN\":\n",
    "        next_y = y + 1 if y < 2 else y \n",
    "        next_x = x\n",
    "      elif action_label == \"LEFT\":\n",
    "        next_x = x - 1 if x > 0 else x \n",
    "        next_y = y\n",
    "      elif action_label == \"RIGHT\":\n",
    "        next_x = x + 1 if x < 2 else x \n",
    "        next_y = y\n",
    "      elif action_label == \"STAY\":\n",
    "        next_x = x\n",
    "        next_y = y\n",
    "      new_location = (next_y, next_x)\n",
    "      next_state = grid_locations.index(new_location)\n",
    "      B[next_state, curr_state, action_id] = 1.0\n",
    "  return B\n",
    "\n",
    "B = create_B_matrix()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "njx3RFJS-H87"
   },
   "source": [
    "Let's now explore what it looks to \"take\" an action, using matrix-vector product of an action-conditioned \"slice\" of the $\\mathbf{B}$ array and a previous state vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "id": "I8wrEfF5-IR1"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\"\"\" Define a starting location\"\"\" \n",
    "starting_location = (1,0)\n",
    "\n",
    "\"\"\"get the linear index of the state\"\"\"\n",
    "state_index = grid_locations.index(starting_location)\n",
    "\n",
    "\"\"\"  and create a state vector out of it \"\"\"\n",
    "starting_state = utils.onehot(state_index, n_states)\n",
    "\n",
    "plot_point_on_grid(starting_state, grid_locations)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "id": "mX5gdrUrMHgz"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_beliefs(starting_state, \"Categorical distribution over the starting state\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "k1hodf-fI02_"
   },
   "source": [
    "Now let's imagine we're moving **\"RIGHT\"** - write the conditional expectation, that will create the state vector corresponding to the new state after taking a step to the right."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "id": "D8Q_dTRSMSje"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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X3nac5OO+kwRdYCpGS/qviYAIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0ikrFgsFks9AoDp4UodIBFRB0hE1AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRnyFdXV3R1NQU1157bTQ0NERnZ2epJzFFhw8fjquvvjq+/fbbUk9hkn777bd49913o7m5OZYtWxb19fWxbdu2KBQKpZ42a8pLPSCj7u7u2LRpUzzwwANx8803R09PT2zevDnOP//8WLVqVannMQlff/11rFu3LkZHR0s9hSlob2+PnTt3xkMPPRQ33XRT9Pf3x6uvvhpfffVV7Nq1q9TzZoXvfpkBK1asiGuuuSZefvnlU8daW1vjyy+/jD179pRwGRMZHR2N9957L3bs2BHnnntu/PTTT7F///5YsGBBqacxgWKxGMuXL4+mpqZoa2s7dby7uzs2bNgQnZ2dsXTp0hIunB1uv0yzwcHBGBgYiJUrV445ftttt0VfX18MDg6WaBmT0dvbGy+99FI8+OCDsWnTplLPYQpGRkbijjvuiNtvv33M8csvvzwiIgYGBkoxa9a5/TLN+vr6IiKitrZ2zPGampqIiOjv74/q6upZ38Xk1NXVRU9PT1x00UXx/vvvl3oOU1BRURHPPvvsX4739PRERMQVV1wx25NKQtSn2YkTJyLiP//A/mju3LkREf+oBzZ/RxdffHGpJzCNDh06FG+88UbU19dHXV1dqefMCrdfptlEjyjmzPFXDrOht7c3Hn744aiqqootW7aUes6sUZhpVllZGRH/ub/3R79fof9+Hpg53d3dsXbt2rjsssvirbfeigsuuKDUk2aNqE+z3++l//mhzNGjR8ecB2bG7t27Y+PGjXH99dfHO++8E5dcckmpJ80qUZ9mNTU1UVVVFR9++OGY43v37o0lS5bEwoULS7QM8uvo6IgXXnghGhoaor29/R/5P2MPSmdAS0tLPPPMMzFv3ry45ZZbYt++fbFnz54xn1sHptcPP/wQW7dujUWLFsWaNWviiy++GHN+8eLFceGFF5Zo3ewR9Rlwzz33xMmTJ+PNN9+Mjo6OqK6uju3bt0djY2Opp0Fan332Wfz8888xNDQUa9as+cv5F198Me68884SLJtdfqMUIBH31AESEXWAREQdIBFRB0hE1AESEXWAREQdIBFRB0hE1AES+X+Emhlu+AfupgAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\"\"\" Redefine the action here, just for reference \"\"\"\n",
    "actions = [\"UP\", \"DOWN\", \"LEFT\", \"RIGHT\", \"STAY\"]\n",
    "\n",
    "\"\"\" Generate the next state vector, given the starting state and the B matrix\"\"\"\n",
    "right_action_idx = actions.index(\"RIGHT\") \n",
    "next_state = B[:,:, right_action_idx].dot(starting_state) # input the indices to the B matrix\n",
    "\n",
    "\"\"\" Plot the next state, after taking the action \"\"\"\n",
    "plot_point_on_grid(next_state, grid_locations)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0h9yH5ARJI-E"
   },
   "source": [
    "Now let's imagine we're moving **\"DOWN\"** from where we just landed - write the conditional expectation, that will create the state vector corresponding to the new state after taking a step down."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "id": "nXaYlJElNRAO"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\"\"\" Generate the next state vector, given the previous state and the B matrix\"\"\"\n",
    "prev_state = next_state.copy()\n",
    "down_action_index = actions.index(\"DOWN\")\n",
    "next_state = B[:,:,down_action_index].dot(prev_state)\n",
    "\n",
    "\"\"\"  Plot the new state vector, after making the movement \"\"\"\n",
    "plot_point_on_grid(next_state, grid_locations)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YkxsKuonRbjX"
   },
   "source": [
    "### 3. The prior over observations: the $\\mathbf{C}$ vector or $\\tilde{P}(o)$\n",
    "\n",
    "---\n",
    "\n",
    "The (biased) generative model's prior preference for particular observations, encoded in terms of probabilities.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "id": "3jSjRkyoPt1w"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\"\"\" Create an empty vector to store the preferences over observations \"\"\"\n",
    "C = np.zeros(n_observations)\n",
    "\n",
    "\"\"\" Choose an observation index to be the 'desired' rewarding index, and fill out the C vector accordingly \"\"\"\n",
    "desired_location = (2,2) # choose a desired location\n",
    "desired_location_index = grid_locations.index(desired_location) # get the linear index of the grid location, in terms of 0 through 8\n",
    "\n",
    "C[desired_location_index] = 1.0 # set the preference for that location to be 100%, i.e. 1.0\n",
    "\n",
    "\"\"\"  Let's look at the prior preference distribution \"\"\"\n",
    "plot_beliefs(C, title_str = \"Preferences over observations\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "syuovouSRmQe"
   },
   "source": [
    "### 3. The prior over hidden states: the $\\mathbf{D}$ vector or $P(s)$\n",
    "\n",
    "---\n",
    "\n",
    "The generative model's prior belief over hidden states at the first timestep.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "id": "ElcxKHl_QHpv"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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
Download .txt
gitextract_jhy4mwbi/

├── .github/
│   └── workflows/
│       ├── docs.yml
│       ├── lint.yaml
│       ├── manual-branch-nightly.yaml
│       ├── nightly-tests.yaml
│       ├── python-package.yml
│       └── test.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs/
│   ├── Makefile
│   ├── agent.rst
│   ├── algos/
│   │   ├── fpi.rst
│   │   ├── index.rst
│   │   └── mmp.rst
│   ├── conf.py
│   ├── control.rst
│   ├── env.rst
│   ├── index.rst
│   ├── inference.rst
│   ├── installation.rst
│   ├── learning.rst
│   ├── make.bat
│   ├── notebooks/
│   │   ├── active_inference_from_scratch.ipynb
│   │   ├── cue_chaining_demo.ipynb
│   │   ├── free_energy_calculation.ipynb
│   │   ├── pymdp_fundamentals.ipynb
│   │   ├── tmaze_demo.ipynb
│   │   └── using_the_agent_class.ipynb
│   └── requirements.txt
├── docs-mkdocs/
│   ├── agent.html
│   ├── algos/
│   │   └── index.html
│   ├── api/
│   │   ├── agent.md
│   │   ├── algos.md
│   │   ├── control.md
│   │   ├── envs-env.md
│   │   ├── envs-rollout.md
│   │   ├── index.md
│   │   ├── inference.md
│   │   ├── learning.md
│   │   ├── maths.md
│   │   ├── planning-mcts.md
│   │   ├── planning-si.md
│   │   └── utils.md
│   ├── control.html
│   ├── development/
│   │   ├── release-notes.md
│   │   └── viewing-docs.md
│   ├── env.html
│   ├── getting-started/
│   │   ├── installation.md
│   │   └── quickstart-jax.md
│   ├── guides/
│   │   ├── generative-model-structure.md
│   │   ├── pymdp-env.md
│   │   └── rollout-active-inference-loop.md
│   ├── index.md
│   ├── inference.html
│   ├── installation.html
│   ├── javascripts/
│   │   ├── mathjax.js
│   │   └── sidebar-accessibility.js
│   ├── learning.html
│   ├── legacy/
│   │   └── index.md
│   ├── migration/
│   │   └── numpy-to-jax.md
│   ├── notebooks/
│   │   ├── active_inference_from_scratch.html
│   │   ├── cue_chaining_demo.html
│   │   ├── free_energy_calculation.html
│   │   ├── pymdp_fundamentals.html
│   │   ├── tmaze_demo.html
│   │   └── using_the_agent_class.html
│   ├── overrides/
│   │   └── modules/
│   │       └── sidebar.html
│   ├── styles/
│   │   └── crisp-api.css
│   └── tutorials/
│       ├── index.md
│       ├── notebooks/
│       │   ├── index.header.md
│       │   └── index.md
│       └── notebooks.manifest
├── examples/
│   ├── __init__.py
│   ├── advanced/
│   │   ├── complex_action_dependency.ipynb
│   │   ├── infer_states_optimization/
│   │   │   └── methods_test.ipynb
│   │   └── pymdp_with_neural_encoder.ipynb
│   ├── api/
│   │   └── model_construction_tutorial.ipynb
│   ├── envs/
│   │   ├── chained_cue_navigation.py
│   │   ├── cue_chaining_demo.ipynb
│   │   ├── generalized_tmaze_demo.ipynb
│   │   ├── graph_worlds_demo.ipynb
│   │   ├── knapsack_demo.ipynb
│   │   └── tmaze_demo.ipynb
│   ├── experimental/
│   │   └── sophisticated_inference/
│   │       ├── mcts_generalized_tmaze.ipynb
│   │       ├── mcts_graph_world.ipynb
│   │       ├── si_generalized_tmaze.ipynb
│   │       ├── si_graph_world.ipynb
│   │       └── si_tmaze_SIvalidation.ipynb
│   ├── inductive_inference/
│   │   ├── inductive_inference_example.ipynb
│   │   └── inductive_inference_gridworld.ipynb
│   ├── inference_and_learning/
│   │   └── inference_methods_comparison.ipynb
│   ├── learning/
│   │   └── learning_gridworld.ipynb
│   ├── legacy/
│   │   ├── agent_demo.ipynb
│   │   ├── free_energy_calculation.ipynb
│   │   ├── gridworld_tutorial_1.ipynb
│   │   ├── gridworld_tutorial_2.ipynb
│   │   ├── tmaze_demo.ipynb
│   │   └── tmaze_learning_demo.ipynb
│   ├── model_fitting/
│   │   ├── fitting_with_pybefit.ipynb
│   │   └── tmaze_recoverability.py
│   └── sparse/
│       └── sparse_benchmark.ipynb
├── mkdocs.yml
├── nbval_sanitize.cfg
├── paper/
│   ├── paper.bib
│   └── paper.md
├── pymdp/
│   ├── __init__.py
│   ├── agent.py
│   ├── algos.py
│   ├── control.py
│   ├── distribution.py
│   ├── envs/
│   │   ├── __init__.py
│   │   ├── cue_chaining.py
│   │   ├── env.py
│   │   ├── generalized_tmaze.py
│   │   ├── graph_worlds.py
│   │   ├── grid_world.py
│   │   ├── rollout.py
│   │   └── tmaze.py
│   ├── inference.py
│   ├── learning.py
│   ├── legacy/
│   │   ├── __init__.py
│   │   ├── agent.py
│   │   ├── algos/
│   │   │   ├── __init__.py
│   │   │   ├── fpi.py
│   │   │   ├── mmp.py
│   │   │   └── mmp_old.py
│   │   ├── control.py
│   │   ├── default_models.py
│   │   ├── envs/
│   │   │   ├── __init__.py
│   │   │   ├── env.py
│   │   │   ├── grid_worlds.py
│   │   │   ├── tmaze.py
│   │   │   └── visual_foraging.py
│   │   ├── inference.py
│   │   ├── learning.py
│   │   ├── maths.py
│   │   └── utils.py
│   ├── likelihoods.py
│   ├── maths.py
│   ├── planning/
│   │   ├── __init__.py
│   │   ├── mcts.py
│   │   ├── si.py
│   │   └── visualize.py
│   └── utils.py
├── pyproject.toml
├── scripts/
│   ├── docs_build.sh
│   ├── docs_serve.sh
│   ├── docs_sync_and_serve.sh
│   ├── notebook_precommit.py
│   ├── run_notebook_manifest.py
│   └── sync_docs_notebooks.sh
├── setup.cfg
└── test/
    ├── __init__.py
    ├── conftest.py
    ├── matlab_crossval/
    │   ├── generation/
    │   │   ├── bmr_matlab_test_a.m
    │   │   ├── bmr_matlab_test_b.m
    │   │   ├── mmp_matlab_test_a.m
    │   │   ├── mmp_matlab_test_b.m
    │   │   ├── mmp_matlab_test_c.m
    │   │   ├── mmp_matlab_test_d.m
    │   │   ├── run_mmp.m
    │   │   ├── vb_x_matlab_test_1a.m
    │   │   └── vb_x_matlab_test_1b.m
    │   └── output/
    │       ├── bmr_test_a.mat
    │       ├── bmr_test_b.mat
    │       ├── cross_a.mat
    │       ├── cross_b.mat
    │       ├── cross_c.mat
    │       ├── cross_d.mat
    │       ├── cross_e.mat
    │       ├── dot_a.mat
    │       ├── dot_b.mat
    │       ├── dot_c.mat
    │       ├── dot_d.mat
    │       ├── dot_e.mat
    │       ├── mmp_a.mat
    │       ├── mmp_b.mat
    │       ├── mmp_c.mat
    │       ├── mmp_d.mat
    │       ├── vbx_test_1a.mat
    │       ├── wnorm_a.mat
    │       └── wnorm_b.mat
    ├── notebooks/
    │   ├── README.md
    │   ├── ci_notebooks.txt
    │   └── nightly_notebooks.txt
    ├── test_SPM_validation.py
    ├── test_agent.py
    ├── test_agent_jax.py
    ├── test_categorical_observations.py
    ├── test_control.py
    ├── test_control_jax.py
    ├── test_cue_chaining_env.py
    ├── test_demos.py
    ├── test_distribution.py
    ├── test_env.py
    ├── test_fpi.py
    ├── test_grid_world_parity.py
    ├── test_hmm_associative_scan.py
    ├── test_inductive_inference_jax.py
    ├── test_infer_states_optimized.py
    ├── test_inference.py
    ├── test_inference_jax.py
    ├── test_jax_sparse_backend.py
    ├── test_learning.py
    ├── test_learning_jax.py
    ├── test_message_passing_jax.py
    ├── test_mmp.py
    ├── test_param_info_gain_jax.py
    ├── test_pybefit_model_fitting.py
    ├── test_rollout_function.py
    ├── test_sophisticated_inference_jax.py
    ├── test_tmaze_envs.py
    ├── test_tmaze_recoverability.py
    ├── test_utils.py
    ├── test_utils_jax.py
    ├── test_vfe_jax.py
    └── test_wrappers.py
Download .txt
SYMBOL INDEX (900 symbols across 70 files)

FILE: examples/envs/chained_cue_navigation.py
  function build_agent (line 29) | def build_agent(env: CueChainingEnv, policy_len: int = 4) -> Agent:
  function rollout_locations (line 54) | def rollout_locations(
  function interpolate_locations (line 77) | def interpolate_locations(
  function load_assets (line 92) | def load_assets(asset_dir: Path) -> dict[str, np.ndarray]:
  function make_animation (line 106) | def make_animation(
  function main (line 317) | def main():

FILE: examples/model_fitting/tmaze_recoverability.py
  class RecoverabilityConfig (line 40) | class RecoverabilityConfig:
  function _assert_parameterization (line 59) | def _assert_parameterization(parameterization: str) -> None:
  function _build_task (line 65) | def _build_task(cfg: RecoverabilityConfig) -> TMaze:
  function _latent_grid (line 75) | def _latent_grid(cfg: RecoverabilityConfig, num_params: int) -> jnp.ndar...
  function _build_three_param_transform (line 89) | def _build_three_param_transform(task: TMaze):
  function _build_reward_only_transform (line 139) | def _build_reward_only_transform(task: TMaze):
  function _build_model_and_truth (line 192) | def _build_model_and_truth(cfg: RecoverabilityConfig) -> tuple[NumpyroMo...
  function _simulate_measurements (line 215) | def _simulate_measurements(model: NumpyroModel, z_true: jnp.ndarray, see...
  function _fit_svi (line 228) | def _fit_svi(
  function _pearson (line 246) | def _pearson(x: np.ndarray, y: np.ndarray) -> float:
  function _bimodality_score (line 252) | def _bimodality_score(probabilities: np.ndarray) -> float:
  function run_recoverability (line 261) | def run_recoverability(cfg: RecoverabilityConfig) -> dict[str, Any]:
  function _save_scatter (line 289) | def _save_scatter(results: dict[str, Any], path: Path) -> None:
  function _to_jsonable (line 308) | def _to_jsonable(results: dict[str, Any]) -> str:
  function parse_args (line 312) | def parse_args() -> argparse.Namespace:
  function main (line 327) | def main() -> None:

FILE: pymdp/agent.py
  class Agent (line 19) | class Agent(Module):
    method __init__ (line 128) | def __init__(
    method unique_multiactions (line 397) | def unique_multiactions(self) -> Array:
    method _get_num_states_from_B (line 401) | def _get_num_states_from_B(self, B: list[Array], B_dependencies: list[...
    method infer_parameters (line 416) | def infer_parameters(
    method process_obs (line 622) | def process_obs(self, observations: list[Array] | list[int]) -> list[A...
    method make_categorical (line 659) | def make_categorical(self, observations: list[Array] | list[int]) -> l...
    method infer_states (line 675) | def infer_states(
    method update_empirical_prior (line 825) | def update_empirical_prior(self, action: Array, qs: list[Array]) -> li...
    method infer_policies (line 853) | def infer_policies(self, qs: list[Array]) -> tuple[Array, Array]:
    method multiaction_probabilities (line 906) | def multiaction_probabilities(self, q_pi: Array) -> Array:
    method sample_action (line 937) | def sample_action(self, q_pi: Array, rng_key: Array | None = None) -> ...
    method decode_multi_actions (line 967) | def decode_multi_actions(self, action: Array) -> Array:
    method encode_multi_actions (line 993) | def encode_multi_actions(self, action_multi: Array) -> Array:
    method get_model_dimensions (line 1022) | def get_model_dimensions(self) -> dict[str, Any]:
    method _construct_dependencies (line 1055) | def _construct_dependencies(
    method _flatten_B_action_dims (line 1086) | def _flatten_B_action_dims(
    method _construct_flattend_policies (line 1123) | def _construct_flattend_policies(self, policies: Array, action_maps: l...
    method _get_default_params (line 1139) | def _get_default_params(self) -> dict[str, Any] | None:
    method _validate (line 1157) | def _validate(self) -> None:

FILE: pymdp/algos.py
  function add (line 18) | def add(x: Array, y: Array) -> Array:
  function _matmul_high_precision (line 22) | def _matmul_high_precision(lhs: Array, rhs: Array) -> Array:
  function marginal_log_likelihood (line 26) | def marginal_log_likelihood(qs: list[Array], log_likelihood: Array, i: i...
  function all_marginal_log_likelihood (line 30) | def all_marginal_log_likelihood(
  function mll_factors (line 46) | def mll_factors(qs: list[Array], ll_m: Array, factor_list_m: list[int]) ...
  function run_vanilla_fpi (line 53) | def run_vanilla_fpi(
  function run_factorized_fpi (line 88) | def run_factorized_fpi(
  function mirror_gradient_descent_step (line 128) | def mirror_gradient_descent_step(
  function update_marginals (line 141) | def update_marginals(
  function variational_filtering_step (line 212) | def variational_filtering_step(
  function update_variational_filtering (line 243) | def update_variational_filtering(
  function get_vmp_messages (line 283) | def get_vmp_messages(
  function run_vmp (line 363) | def run_vmp(
  function get_mmp_messages (line 403) | def get_mmp_messages(
  function run_mmp (line 483) | def run_mmp(
  function run_online_filtering (line 544) | def run_online_filtering(
  function run_factorized_fpi_hybrid (line 560) | def run_factorized_fpi_hybrid(
  function get_qs_padded (line 584) | def get_qs_padded(qs: list[Array], max_state_dim: int) -> list[Array]:
  function compute_qL_marginals (line 590) | def compute_qL_marginals(
  function qL_flatten (line 635) | def qL_flatten(qL_marginals_padded: list[list[Array]]) -> list[list[Arra...
  function compute_qL_all (line 646) | def compute_qL_all(
  function run_factorized_fpi_end2end_padded (line 658) | def run_factorized_fpi_end2end_padded(
  class FilterMessage (line 689) | class FilterMessage(NamedTuple):
  function _normalize_preserve_zeros (line 696) | def _normalize_preserve_zeros(u: Array,
  function _log_predictive_normalizer (line 712) | def _log_predictive_normalizer(predicted_probs: Array,
  function _condition_on (line 721) | def _condition_on(A: Array,
  function _hmm_filter_scan_row_oriented (line 749) | def _hmm_filter_scan_row_oriented(
  function hmm_filter_scan_rowstoch (line 817) | def hmm_filter_scan_rowstoch(
  function _hmm_smoother_scan_row_oriented (line 845) | def _hmm_smoother_scan_row_oriented(
  function hmm_smoother_scan_rowstoch (line 904) | def hmm_smoother_scan_rowstoch(
  function hmm_filter_scan_colstoch (line 930) | def hmm_filter_scan_colstoch(
  function hmm_smoother_scan_colstoch (line 961) | def hmm_smoother_scan_colstoch(
  function hmm_smoother_from_filtered_colstoch (line 1020) | def hmm_smoother_from_filtered_colstoch(
  function run_exact_single_factor_hmm_scan (line 1103) | def run_exact_single_factor_hmm_scan(
  function sum_prod (line 1183) | def sum_prod(prior: list[Array]) -> Array:

FILE: pymdp/control.py
  class Policies (line 21) | class Policies(eqx.Module):
    method __init__ (line 30) | def __init__(self, policy_arr: Array) -> None:
    method __getitem__ (line 35) | def __getitem__(self, idx: int) -> Array:
    method __len__ (line 38) | def __len__(self) -> int:
  function get_marginals (line 41) | def get_marginals(q_pi: Array, policies: Array, num_controls: Sequence[i...
  function sample_action (line 68) | def sample_action(
  function sample_policy (line 114) | def sample_policy(
  function construct_policies (line 151) | def construct_policies(
  function update_posterior_policies (line 197) | def update_posterior_policies(
  function compute_expected_state (line 290) | def compute_expected_state(
  function compute_expected_state_and_Bs (line 330) | def compute_expected_state_and_Bs(
  function compute_expected_obs (line 359) | def compute_expected_obs(
  function compute_info_gain (line 388) | def compute_info_gain(
  function compute_expected_utility (line 422) | def compute_expected_utility(qo: list[Array], C: list[Array], t: int = 0...
  function calc_negative_pA_info_gain (line 450) | def calc_negative_pA_info_gain(
  function calc_negative_pB_info_gain (line 493) | def calc_negative_pB_info_gain(
  function compute_neg_efe_policy (line 543) | def compute_neg_efe_policy(
  function compute_neg_efe_policy_inductive (line 631) | def compute_neg_efe_policy_inductive(
  function update_posterior_policies_inductive (line 734) | def update_posterior_policies_inductive(
  function generate_I_matrix (line 843) | def generate_I_matrix(H: list[Array], B: list[Array], threshold: float, ...
  function calc_inductive_value_t (line 899) | def calc_inductive_value_t(

FILE: pymdp/distribution.py
  class Distribution (line 6) | class Distribution:
    method __init__ (line 8) | def __init__(self, event: dict, batch: dict = {}, data: np.ndarray | N...
    method data (line 32) | def data(self) -> np.ndarray:
    method data (line 36) | def data(self, value: np.ndarray) -> None:
    method get (line 42) | def get(self, batch: dict | None = None, event: dict | None = None) ->...
    method set (line 49) | def set(
    method _get_slices (line 58) | def _get_slices(self, keys: dict | None, indices: dict, full_indices: ...
    method _get_index (line 74) | def _get_index(self, key: Any, index_map: dict) -> int:
    method _get_index_from_axis (line 80) | def _get_index_from_axis(self, axis: int, element: Any) -> int | slice:
    method __getitem__ (line 91) | def __getitem__(self, indices: Any) -> np.ndarray:
    method __setitem__ (line 99) | def __setitem__(self, indices: Any, value: Any) -> None:
    method normalize (line 107) | def normalize(self) -> None:
    method __repr__ (line 110) | def __repr__(self) -> str:
  class DistributionIndexer (line 114) | class DistributionIndexer(dict):
    method __init__ (line 120) | def __init__(self, distributions: list[Distribution]) -> None:
    method __getitem__ (line 127) | def __getitem__(self, key: str | int) -> Distribution:
    method __iter__ (line 137) | def __iter__(self) -> Iterator[Distribution]:
  class Model (line 141) | class Model(dict):
    method __init__ (line 143) | def __init__(
    method __getattr__ (line 162) | def __getattr__(self, key: str) -> Any:
  function compile_model (line 172) | def compile_model(config: dict[str, Any]) -> Model:
  function get_dependencies (line 315) | def get_dependencies(

FILE: pymdp/envs/cue_chaining.py
  class CueChainingEnv (line 21) | class CueChainingEnv(PymdpEnv):
    method __init__ (line 43) | def __init__(
    method coords_to_index (line 160) | def coords_to_index(self, coord: tuple[int, int]) -> int:
    method index_to_coords (line 166) | def index_to_coords(self, index: int) -> tuple[int, int]:
    method _validate_coord (line 172) | def _validate_coord(self, coord: tuple[int, int]) -> None:
    method _generate_A (line 178) | def _generate_A(self) -> tuple[list[jnp.ndarray], list[list[int]]]:
    method _generate_B (line 214) | def _generate_B(self) -> tuple[list[jnp.ndarray], list[list[int]]]:
    method _generate_D (line 254) | def _generate_D(

FILE: pymdp/envs/env.py
  function _float_to_int_index (line 20) | def _float_to_int_index(x: Array) -> Array:
  function select_probs (line 36) | def select_probs(
  function cat_sample (line 67) | def cat_sample(key: Array, p: Array) -> Array:
  function make (line 91) | def make(
  class Env (line 150) | class Env(ABC):
    method reset (line 154) | def reset(
    method step (line 179) | def step(
    method generate_env_params (line 206) | def generate_env_params(
  class PymdpEnv (line 226) | class PymdpEnv(Env):
    method __init__ (line 236) | def __init__(
    method generate_env_params (line 305) | def generate_env_params(
    method reset (line 333) | def reset(
    method step (line 352) | def step(
    method _sample_obs (line 380) | def _sample_obs(

FILE: pymdp/envs/generalized_tmaze.py
  function get_maze_matrix (line 15) | def get_maze_matrix(small: bool = False) -> np.ndarray:
  function parse_maze (line 71) | def parse_maze(maze: np.ndarray, rng_key: PRNGKeyArray) -> dict[str, Any]:
  function generate_A (line 171) | def generate_A(maze_info: dict[str, Any]) -> tuple[list[jnp.ndarray], li...
  function generate_B (line 253) | def generate_B(maze_info: dict[str, Any]) -> tuple[list[jnp.ndarray], li...
  function generate_D (line 323) | def generate_D(maze_info: dict[str, Any]) -> list[jnp.ndarray]:
  class GeneralizedTMazeEnv (line 355) | class GeneralizedTMazeEnv(PymdpEnv):
    method __init__ (line 361) | def __init__(self, env_info: dict[str, Any], categorical_obs: bool = F...
    method render (line 387) | def render(self, states: list[jnp.ndarray], mode: str = "human") -> jn...

FILE: pymdp/envs/graph_worlds.py
  function generate_connected_clusters (line 8) | def generate_connected_clusters(
  class GraphEnv (line 25) | class GraphEnv(PymdpEnv):
    method __init__ (line 31) | def __init__(
    method generate_A (line 81) | def generate_A(self, graph: nx.Graph) -> tuple[list[jnp.ndarray], list...
    method generate_B (line 113) | def generate_B(self, graph: nx.Graph) -> tuple[list[jnp.ndarray], list...
    method generate_D (line 141) | def generate_D(
    method generate_env_params (line 157) | def generate_env_params(

FILE: pymdp/envs/grid_world.py
  class GridWorld (line 12) | class GridWorld(PymdpEnv):
    method __init__ (line 73) | def __init__(
    method coords_to_index (line 109) | def coords_to_index(shape: Tuple[int, int], coord: Tuple[int, int]) ->...
    method index_to_coords (line 114) | def index_to_coords(shape: Tuple[int, int], idx: int) -> Tuple[int, int]:
  function _flatten_walls (line 123) | def _flatten_walls(shape: Tuple[int, int], walls: Optional[Iterable[Tupl...
  function _generate_A (line 135) | def _generate_A(n_states: int) -> tuple[jnp.ndarray, list[list[int]]]:
  function _neighbors (line 147) | def _neighbors(shape: Tuple[int, int], s: int) -> Tuple[int, int, int, i...
  function _generate_B (line 162) | def _generate_B(
  function _generate_D (line 214) | def _generate_D(

FILE: pymdp/envs/rollout.py
  function _append_to_window (line 27) | def _append_to_window(window: Array, value: Array) -> Array:
  function _resolve_history_len (line 38) | def _resolve_history_len(agent: Agent, num_timesteps: int, use_windowing...
  function default_policy_search (line 58) | def default_policy_search(agent: Agent, qs: list[Array], rng_key: Array)...
  function _resolve_empirical_prior (line 65) | def _resolve_empirical_prior(
  function update_parameters_online (line 85) | def update_parameters_online(
  function _compute_sequence_empirical_prior_next (line 170) | def _compute_sequence_empirical_prior_next(
  function _run_sequence_fixed_window_step (line 195) | def _run_sequence_fixed_window_step(
  function _run_smoothing_fixed_window_step (line 233) | def _run_smoothing_fixed_window_step(
  function _run_non_window_step (line 259) | def _run_non_window_step(
  function _update_window_buffers (line 280) | def _update_window_buffers(
  function _init_observation_history (line 294) | def _init_observation_history(obs: Array, history_len: int, categorical_...
  function _init_windowed_carry (line 307) | def _init_windowed_carry(
  function _init_non_windowed_carry (line 355) | def _init_non_windowed_carry(
  function infer_and_plan (line 370) | def infer_and_plan(
  function rollout (line 468) | def rollout(

FILE: pymdp/envs/tmaze.py
  class BaseTMaze (line 24) | class BaseTMaze(PymdpEnv):
    method __init__ (line 44) | def __init__(
    method _set_reward_outcome (line 141) | def _set_reward_outcome(self, A_reward: jnp.ndarray, loc: int, reward_...
    method generate_A (line 157) | def generate_A(self) -> tuple[list[jnp.ndarray], list[list[int]]]:
    method _generate_A_separate (line 164) | def _generate_A_separate(self) -> tuple[list[jnp.ndarray], list[list[i...
    method _generate_A_embedded (line 204) | def _generate_A_embedded(self) -> tuple[list[jnp.ndarray], list[list[i...
    method _valid_connections (line 240) | def _valid_connections(self) -> list[tuple[int, int]]:
    method generate_B (line 267) | def generate_B(self) -> tuple[list[jnp.ndarray], list[list[int]]]:
    method generate_D (line 302) | def generate_D(self) -> list[jnp.ndarray]:
    method render (line 328) | def render(
  class TMaze (line 494) | class TMaze(BaseTMaze):
    method __init__ (line 499) | def __init__(
  class SimplifiedTMaze (line 522) | class SimplifiedTMaze(BaseTMaze):
    method __init__ (line 527) | def __init__(

FILE: pymdp/inference.py
  class VFEInfo (line 37) | class VFEInfo(TypedDict):
  function _select_current_obs (line 43) | def _select_current_obs(obs: list[Array] | Array, distr_obs: bool) -> li...
  function _truncate_for_horizon (line 56) | def _truncate_for_horizon(
  function _ensure_action_history_shape (line 76) | def _ensure_action_history_shape(past_actions: Array | None, num_factors...
  function _build_sequence_validity_masks (line 101) | def _build_sequence_validity_masks(
  function _condition_transitions_on_actions (line 124) | def _condition_transitions_on_actions(
  function _run_one_step_inference (line 171) | def _run_one_step_inference(
  function _run_sequence_inference (line 194) | def _run_sequence_inference(
  function _update_qs_history (line 274) | def _update_qs_history(
  function _assemble_vfe_kwargs (line 298) | def _assemble_vfe_kwargs(
  function update_posterior_states (line 338) | def update_posterior_states(
  function _joint_dist_factor (line 487) | def _joint_dist_factor(
  function joint_dist_factor (line 524) | def joint_dist_factor(
  function smoothing_ovf (line 559) | def smoothing_ovf(
  function smoothing_exact (line 595) | def smoothing_exact(

FILE: pymdp/learning.py
  function update_obs_likelihood_dirichlet_m (line 11) | def update_obs_likelihood_dirichlet_m(
  function update_obs_likelihood_dirichlet (line 57) | def update_obs_likelihood_dirichlet(
  function update_state_transition_dirichlet_f (line 119) | def update_state_transition_dirichlet_f(
  function update_state_transition_dirichlet (line 159) | def update_state_transition_dirichlet(

FILE: pymdp/legacy/agent.py
  class Agent (line 16) | class Agent(object):
    method __init__ (line 33) | def __init__(
    method _construct_C_prior (line 318) | def _construct_C_prior(self):
    method _construct_D_prior (line 324) | def _construct_D_prior(self):
    method _construct_policies (line 330) | def _construct_policies(self):
    method _construct_num_controls (line 338) | def _construct_num_controls(self):
    method _construct_E_prior (line 345) | def _construct_E_prior(self):
    method reset (line 349) | def reset(self, init_qs=None):
    method step_time (line 396) | def step_time(self):
    method set_latest_beliefs (line 420) | def set_latest_beliefs(self,last_belief=None):
    method get_future_qs (line 455) | def get_future_qs(self):
    method infer_states (line 478) | def infer_states(self, observation, distr_obs=False):
    method _infer_states_test (line 552) | def _infer_states_test(self, observation, distr_obs=False):
    method infer_policies (line 608) | def infer_policies(self):
    method sample_action (line 695) | def sample_action(self):
    method _sample_action_test (line 723) | def _sample_action_test(self):
    method update_A (line 749) | def update_A(self, obs):
    method _update_A_old (line 780) | def _update_A_old(self, obs):
    method update_B (line 810) | def update_B(self, qs_prev):
    method _update_B_old (line 841) | def _update_B_old(self, qs_prev):
    method update_D (line 871) | def update_D(self, qs_t0 = None):
    method _get_default_params (line 923) | def _get_default_params(self):

FILE: pymdp/legacy/algos/fpi.py
  function run_vanilla_fpi (line 10) | def run_vanilla_fpi(A, obs, num_obs, num_states, prior=None, num_iter=10...
  function run_vanilla_fpi_factorized (line 159) | def run_vanilla_fpi_factorized(A, obs, num_obs, num_states, mb_dict, pri...
  function _run_vanilla_fpi_faster (line 324) | def _run_vanilla_fpi_faster(A, obs, n_observations, n_states, prior=None...

FILE: pymdp/legacy/algos/mmp.py
  function run_mmp (line 9) | def run_mmp(
  function run_mmp_factorized (line 133) | def run_mmp_factorized(
  function _run_mmp_testing (line 297) | def _run_mmp_testing(

FILE: pymdp/legacy/algos/mmp_old.py
  function run_mmp_old (line 11) | def run_mmp_old(

FILE: pymdp/legacy/control.py
  function update_posterior_policies_full (line 13) | def update_posterior_policies_full(
  function update_posterior_policies_full_factorized (line 135) | def update_posterior_policies_full_factorized(
  function update_posterior_policies (line 266) | def update_posterior_policies(
  function update_posterior_policies_factorized (line 364) | def update_posterior_policies_factorized(
  function get_expected_states (line 470) | def get_expected_states(qs, B, policy):
  function get_expected_states_interactions (line 505) | def get_expected_states_interactions(qs, B, B_factor_list, policy):
  function get_expected_obs (line 543) | def get_expected_obs(qs_pi, A):
  function get_expected_obs_factorized (line 580) | def get_expected_obs_factorized(qs_pi, A, A_factor_list):
  function calc_expected_utility (line 619) | def calc_expected_utility(qo_pi, C):
  function calc_states_info_gain (line 664) | def calc_states_info_gain(A, qs_pi):
  function calc_states_info_gain_factorized (line 693) | def calc_states_info_gain_factorized(A, qs_pi, A_factor_list):
  function calc_pA_info_gain (line 727) | def calc_pA_info_gain(pA, qo_pi, qs_pi):
  function calc_pA_info_gain_factorized (line 764) | def calc_pA_info_gain_factorized(pA, qo_pi, qs_pi, A_factor_list):
  function calc_pB_info_gain (line 805) | def calc_pB_info_gain(pB, qs_pi, qs_prev, policy):
  function calc_pB_info_gain_interactions (line 856) | def calc_pB_info_gain_interactions(pB, qs_pi, qs_prev, B_factor_list, po...
  function calc_inductive_cost (line 910) | def calc_inductive_cost(qs, qs_pi, I, epsilon=1e-3):
  function construct_policies (line 953) | def construct_policies(num_states, num_controls = None, policy_len=1, co...
  function get_num_controls_from_policies (line 995) | def get_num_controls_from_policies(policies):
  function sample_action (line 1017) | def sample_action(q_pi, policies, num_controls, action_selection="determ...
  function _sample_action_test (line 1068) | def _sample_action_test(q_pi, policies, num_controls, action_selection="...
  function sample_policy (line 1126) | def sample_policy(q_pi, policies, num_controls, action_selection="determ...
  function _sample_policy_test (line 1168) | def _sample_policy_test(q_pi, policies, num_controls, action_selection="...
  function select_highest (line 1215) | def select_highest(options_array):
  function _select_highest_test (line 1236) | def _select_highest_test(options_array, seed=None):
  function backwards_induction (line 1259) | def backwards_induction(H, B, B_factor_list, threshold, depth):
  function calc_ambiguity_factorized (line 1316) | def calc_ambiguity_factorized(qs_pi, A, A_factor_list):
  function sophisticated_inference_search (line 1353) | def sophisticated_inference_search(qs, policies, A, B, C, A_factor_list,...

FILE: pymdp/legacy/default_models.py
  function generate_epistemic_MAB_model (line 5) | def generate_epistemic_MAB_model():
  function generate_grid_world_transitions (line 72) | def generate_grid_world_transitions(action_labels, num_rows = 3, num_col...

FILE: pymdp/legacy/envs/env.py
  class Env (line 11) | class Env(object):
    method reset (line 30) | def reset(self, state=None):
    method step (line 36) | def step(self, action):
    method render (line 52) | def render(self):
    method sample_action (line 58) | def sample_action(self):
    method get_likelihood_dist (line 61) | def get_likelihood_dist(self):
    method get_transition_dist (line 66) | def get_transition_dist(self):
    method get_uniform_posterior (line 71) | def get_uniform_posterior(self):
    method get_rand_likelihood_dist (line 76) | def get_rand_likelihood_dist(self):
    method get_rand_transition_dist (line 81) | def get_rand_transition_dist(self):
    method __str__ (line 86) | def __str__(self):

FILE: pymdp/legacy/envs/grid_worlds.py
  class GridWorldEnv (line 18) | class GridWorldEnv(Env):
    method __init__ (line 29) | def __init__(self, shape=[2, 2], init_state=None):
    method reset (line 52) | def reset(self, init_state=None):
    method set_state (line 71) | def set_state(self, state):
    method step (line 89) | def step(self, action):
    method render (line 108) | def render(self, title=None):
    method set_init_state (line 130) | def set_init_state(self, init_state=None):
    method _build (line 141) | def _build(self):
    method get_init_state_dist (line 167) | def get_init_state_dist(self, init_state=None):
    method get_transition_dist (line 174) | def get_transition_dist(self):
    method get_likelihood_dist (line 182) | def get_likelihood_dist(self):
    method sample_action (line 186) | def sample_action(self):
    method position (line 190) | def position(self):
  class DGridWorldEnv (line 195) | class DGridWorldEnv(object):
    method __init__ (line 204) | def __init__(self, shape=[2, 2], init_state=None):
    method reset (line 215) | def reset(self, init_state=None):
    method set_state (line 220) | def set_state(self, state):
    method step (line 224) | def step(self, action):
    method render (line 230) | def render(self, title=None):
    method set_init_state (line 244) | def set_init_state(self, init_state=None):
    method _build (line 255) | def _build(self):
    method get_init_state_dist (line 277) | def get_init_state_dist(self, init_state=None):
    method get_transition_dist (line 284) | def get_transition_dist(self):
    method get_likelihood_dist (line 292) | def get_likelihood_dist(self):
    method sample_action (line 296) | def sample_action(self):
    method position (line 300) | def position(self):

FILE: pymdp/legacy/envs/tmaze.py
  class TMazeEnv (line 25) | class TMazeEnv(Env):
    method __init__ (line 27) | def __init__(self, reward_probs=None):
    method reset (line 56) | def reset(self, state=None):
    method step (line 71) | def step(self, actions):
    method render (line 79) | def render(self):
    method sample_action (line 82) | def sample_action(self):
    method get_likelihood_dist (line 85) | def get_likelihood_dist(self):
    method get_transition_dist (line 88) | def get_transition_dist(self):
    method get_rand_likelihood_dist (line 92) | def get_rand_likelihood_dist(self):
    method get_rand_transition_dist (line 95) | def get_rand_transition_dist(self):
    method _get_observation (line 98) | def _get_observation(self):
    method _construct_transition_dist (line 105) | def _construct_transition_dist(self):
    method _construct_likelihood_dist (line 119) | def _construct_likelihood_dist(self):
    method _construct_state (line 175) | def _construct_state(self, state_tuple):
    method state (line 184) | def state(self):
    method reward_condition (line 188) | def reward_condition(self):
  class TMazeEnvNullOutcome (line 192) | class TMazeEnvNullOutcome(Env):
    method __init__ (line 196) | def __init__(self, reward_probs=None):
    method reset (line 225) | def reset(self, state=None):
    method step (line 240) | def step(self, actions):
    method sample_action (line 249) | def sample_action(self):
    method get_likelihood_dist (line 252) | def get_likelihood_dist(self):
    method get_transition_dist (line 255) | def get_transition_dist(self):
    method _get_observation (line 258) | def _get_observation(self):
    method _construct_transition_dist (line 265) | def _construct_transition_dist(self):
    method _construct_likelihood_dist (line 279) | def _construct_likelihood_dist(self):
    method _construct_state (line 331) | def _construct_state(self, state_tuple):
    method state (line 341) | def state(self):
    method reward_condition (line 345) | def reward_condition(self):

FILE: pymdp/legacy/envs/visual_foraging.py
  class SceneConstruction (line 28) | class SceneConstruction(Env):
    method __init__ (line 30) | def __init__(self, starting_loc = 'start', scene_name = 'UP_RIGHT', co...
    method step (line 45) | def step(self,action_label):
    method reset (line 72) | def reset(self):
    method _create_visual_array (line 79) | def _create_visual_array(self):
  class RandomDotMotion (line 92) | class RandomDotMotion(Env):
    method __init__ (line 97) | def __init__(self, precision = 1.0, dot_direction = None, sampling_sta...
    method reset (line 121) | def reset(self, dot_direction = None, sampling_state = None):
    method step (line 132) | def step(self, action):
    method _generate_dot_dist (line 138) | def _generate_dot_dist(self):
    method _get_observation (line 150) | def _get_observation(self):
    method _set_sampling_state (line 158) | def _set_sampling_state(self, action):
    method dot_direction (line 163) | def dot_direction(self):
    method num_directions (line 167) | def num_directions(self):
    method precision (line 171) | def precision(self):
    method coherence (line 175) | def coherence(self):
  function create_2x2_array (line 179) | def create_2x2_array(scene_name, config):
  function initialize_scene_construction_GM (line 193) | def initialize_scene_construction_GM(T = 6, reward = 2.0, punishment = -...
  function initialize_RDM_GM (line 284) | def initialize_RDM_GM(T=16, A_precis = 1.0, break_reward = 0.001):

FILE: pymdp/legacy/inference.py
  function update_posterior_states_full (line 18) | def update_posterior_states_full(
  function update_posterior_states_full_factorized (line 89) | def update_posterior_states_full_factorized(
  function _update_posterior_states_full_test (line 169) | def _update_posterior_states_full_test(
  function average_states_over_policies (line 247) | def average_states_over_policies(qs_pi, q_pi):
  function update_posterior_states (line 282) | def update_posterior_states(A, obs, prior=None, **kwargs):
  function update_posterior_states_factorized (line 324) | def update_posterior_states_factorized(A, obs, num_obs, num_states, mb_d...

FILE: pymdp/legacy/learning.py
  function update_obs_likelihood_dirichlet (line 9) | def update_obs_likelihood_dirichlet(pA, A, obs, qs, lr=1.0, modalities="...
  function update_obs_likelihood_dirichlet_factorized (line 60) | def update_obs_likelihood_dirichlet_factorized(pA, A, obs, qs, A_factor_...
  function update_state_likelihood_dirichlet (line 113) | def update_state_likelihood_dirichlet(
  function update_state_likelihood_dirichlet_interactions (line 161) | def update_state_likelihood_dirichlet_interactions(
  function update_state_prior_dirichlet (line 212) | def update_state_prior_dirichlet(
  function _prune_prior (line 251) | def _prune_prior(prior, levels_to_remove, dirichlet = False):
  function _prune_A (line 312) | def _prune_A(A, obs_levels_to_prune, state_levels_to_prune, dirichlet = ...
  function _prune_B (line 383) | def _prune_B(B, state_levels_to_prune, action_levels_to_prune, dirichlet...

FILE: pymdp/legacy/maths.py
  function spm_dot (line 19) | def spm_dot(X, x, dims_to_omit=None):
  function spm_dot_classic (line 59) | def spm_dot_classic(X, x, dims_to_omit=None):
  function factor_dot_flex (line 109) | def factor_dot_flex(M, xs, dims, keep_dims=None):
  function spm_dot_old (line 131) | def spm_dot_old(X, x, dims_to_omit=None, obs_mode=False):
  function spm_cross (line 197) | def spm_cross(x, y=None, *args):
  function dot_likelihood (line 239) | def dot_likelihood(A,obs):
  function get_joint_likelihood (line 255) | def get_joint_likelihood(A, obs, num_states):
  function get_joint_likelihood_seq (line 267) | def get_joint_likelihood_seq(A, obs, num_states):
  function get_joint_likelihood_seq_by_modality (line 273) | def get_joint_likelihood_seq_by_modality(A, obs, num_states):
  function spm_norm (line 291) | def spm_norm(A):
  function spm_log_single (line 300) | def spm_log_single(arr):
  function spm_log_obj_array (line 306) | def spm_log_obj_array(obj_arr):
  function spm_wnorm (line 317) | def spm_wnorm(A):
  function spm_betaln (line 329) | def spm_betaln(z):
  function dirichlet_log_evidence (line 335) | def dirichlet_log_evidence(q_dir, p_dir, r_dir):
  function softmax (line 363) | def softmax(dist):
  function softmax_obj_arr (line 373) | def softmax_obj_arr(arr):
  function compute_accuracy (line 382) | def compute_accuracy(log_likelihood, qs):
  function calc_free_energy (line 396) | def calc_free_energy(qs, prior, n_factors, likelihood=None):
  function spm_calc_qo_entropy (line 412) | def spm_calc_qo_entropy(A, x):
  function spm_calc_neg_ambig (line 464) | def spm_calc_neg_ambig(A, x):
  function spm_MDP_G (line 517) | def spm_MDP_G(A, x):
  function kl_div (line 575) | def kl_div(P,Q):
  function entropy (line 592) | def entropy(A):

FILE: pymdp/legacy/utils.py
  class Dimensions (line 18) | class Dimensions(object):
    method __init__ (line 22) | def __init__(
  function sample (line 39) | def sample(probabilities):
  function sample_obj_array (line 44) | def sample_obj_array(arr):
  function obj_array (line 53) | def obj_array(num_arr):
  function obj_array_zeros (line 59) | def obj_array_zeros(shape_list):
  function initialize_empty_A (line 69) | def initialize_empty_A(num_obs, num_states):
  function initialize_empty_B (line 78) | def initialize_empty_B(num_states, num_controls):
  function obj_array_uniform (line 87) | def obj_array_uniform(shape_list):
  function obj_array_ones (line 98) | def obj_array_ones(shape_list, scale = 1.0):
  function onehot (line 105) | def onehot(value, num_values):
  function random_A_matrix (line 110) | def random_A_matrix(num_obs, num_states, A_factor_list=None):
  function random_B_matrix (line 130) | def random_B_matrix(num_states, num_controls, B_factor_list=None, B_fact...
  function get_combination_index (line 175) | def get_combination_index(x, dims):
  function index_to_combination (line 199) | def index_to_combination(index, dims):
  function random_single_categorical (line 221) | def random_single_categorical(shape_list):
  function construct_controllable_B (line 235) | def construct_controllable_B(num_states, num_controls):
  function dirichlet_like (line 252) | def dirichlet_like(template_categorical, scale = 1.0):
  function get_model_dimensions (line 273) | def get_model_dimensions(A=None, B=None, factorized=False):
  function get_model_dimensions_from_labels (line 303) | def get_model_dimensions_from_labels(model_labels):
  function norm_dist (line 324) | def norm_dist(dist):
  function norm_dist_obj_arr (line 328) | def norm_dist_obj_arr(obj_arr):
  function is_normalized (line 337) | def is_normalized(dist):
  function is_obj_array (line 355) | def is_obj_array(arr):
  function to_obj_array (line 358) | def to_obj_array(arr):
  function obj_array_from_list (line 365) | def obj_array_from_list(list_input):
  function process_observation_seq (line 374) | def process_observation_seq(obs_seq, n_modalities, n_observations):
  function process_observation (line 388) | def process_observation(obs, num_modalities, num_observations):
  function convert_observation_array (line 419) | def convert_observation_array(obs, num_obs):
  function insert_multiple (line 467) | def insert_multiple(s, indices, items):
  function reduce_a_matrix (line 472) | def reduce_a_matrix(A):
  function construct_full_a (line 517) | def construct_full_a(A_reduced, original_factor_idx, num_states):
  function build_xn_vn_array (line 589) | def build_xn_vn_array(xn):
  function plot_beliefs (line 619) | def plot_beliefs(belief_dist, title=""):
  function plot_likelihood (line 632) | def plot_likelihood(A, title=""):

FILE: pymdp/likelihoods.py
  function evolve_trials (line 7) | def evolve_trials(agent: Any, data: Any) -> Any:
  function aif_likelihood (line 29) | def aif_likelihood(Nb: int, Nt: int, Na: int, data: Any, agent: Any) -> ...

FILE: pymdp/maths.py
  function stable_xlogx (line 17) | def stable_xlogx(x: ArrayLike) -> ArrayLike:
  function stable_entropy (line 32) | def stable_entropy(x: ArrayLike) -> ArrayLike:
  function stable_cross_entropy (line 47) | def stable_cross_entropy(x: ArrayLike, y: ArrayLike) -> ArrayLike:
  function log_stable (line 64) | def log_stable(x: ArrayLike) -> ArrayLike:
  function factor_dot (line 83) | def factor_dot(
  function factor_dot (line 110) | def factor_dot(
  function spm_dot_sparse (line 136) | def spm_dot_sparse(
  function factor_dot_flex (line 180) | def factor_dot_flex(
  function compute_log_likelihood_single_modality (line 214) | def compute_log_likelihood_single_modality(
  function compute_log_likelihood (line 241) | def compute_log_likelihood(
  function compute_log_likelihood_per_modality (line 266) | def compute_log_likelihood_per_modality(
  function _to_dense_if_sparse (line 290) | def _to_dense_if_sparse(x: ArrayLike) -> ArrayLike:
  function _expected_log_prob (line 296) | def _expected_log_prob(log_prob: ArrayLike, marginals: list[ArrayLike]) ...
  function _expected_log_prob_tensor (line 302) | def _expected_log_prob_tensor(log_prob: ArrayLike, belief: ArrayLike) ->...
  function _pad_sequence_with_initial_zeros (line 312) | def _pad_sequence_with_initial_zeros(x: ArrayLike) -> ArrayLike:
  function _ensure_vfe_action_history_shape (line 318) | def _ensure_vfe_action_history_shape(
  function _sum_dirichlet_kl (line 348) | def _sum_dirichlet_kl(
  function compute_accuracy (line 369) | def compute_accuracy(
  function dirichlet_kl_divergence (line 408) | def dirichlet_kl_divergence(
  function calc_vfe (line 448) | def calc_vfe(
  function multidimensional_outer (line 763) | def multidimensional_outer(arrs: list[ArrayLike]) -> ArrayLike:
  function _exact_wnorm (line 783) | def _exact_wnorm(A: ArrayLike) -> ArrayLike:
  function spm_wnorm (line 811) | def spm_wnorm(A: ArrayLike, exact_param_info_gain: bool = True) -> Array...
  function dirichlet_expected_value (line 843) | def dirichlet_expected_value(dir_arr: ArrayLike, event_dim: int = 0) -> ...
  function compute_log_likelihoods_padded (line 871) | def compute_log_likelihoods_padded(obs_padded: ArrayLike, A_padded: Arra...
  function deconstruct_lls (line 890) | def deconstruct_lls(
  function compute_log_likelihoods_flat_block_diag_einsum (line 924) | def compute_log_likelihoods_flat_block_diag_einsum(
  function compute_log_likelihoods_flat_block_diag (line 948) | def compute_log_likelihoods_flat_block_diag(A_big: ArrayLike, obs_big: A...
  function deconstruct_log_likelihoods_block_diag (line 969) | def deconstruct_log_likelihoods_block_diag(
  function compute_log_likelihoods_block_diag (line 993) | def compute_log_likelihoods_block_diag(
  function log_stable_sparse (line 1032) | def log_stable_sparse(x: ArrayLike) -> ArrayLike:
  function compute_log_likelihood_per_modality_end2end2_padded (line 1050) | def compute_log_likelihood_per_modality_end2end2_padded(

FILE: pymdp/planning/mcts.py
  function _require_mctx (line 21) | def _require_mctx() -> None:
  function mcts_policy_search (line 30) | def mcts_policy_search(
  function compute_neg_efe (line 84) | def compute_neg_efe(
  function get_prob_single_modality (line 144) | def get_prob_single_modality(o_m: jnp.ndarray, po_m: jnp.ndarray, distr_...
  function make_aif_recurrent_fn (line 149) | def make_aif_recurrent_fn() -> Callable[[Any, jnp.ndarray, jnp.ndarray, ...
  function rollout (line 201) | def rollout(

FILE: pymdp/planning/si.py
  function predict_fn (line 23) | def predict_fn(agent: Any, qs: list[jnp.ndarray]) -> tuple[list[jnp.ndar...
  function infer_fn (line 63) | def infer_fn(agent: Any, obs: list[jnp.ndarray], qs: list[jnp.ndarray]) ...
  function si_policy_search (line 87) | def si_policy_search(
  class Tree (line 208) | class Tree(eqx.Module):
    method __init__ (line 241) | def __init__(
    method __getitem__ (line 273) | def __getitem__(self, index: int) -> dict[str, Any]:
    method root (line 309) | def root(self) -> dict[str, Any]:
  function root_idx (line 319) | def root_idx(tree: Tree) -> jnp.ndarray:
  function _do_nothing (line 342) | def _do_nothing(tree: Tree, idx: jnp.ndarray) -> Tree:
  function _update_node (line 346) | def _update_node(
  function _remove_orphans (line 481) | def _remove_orphans(tree: Tree) -> Tree:
  function _calculate_probabilities (line 548) | def _calculate_probabilities(return_size: int, topk_probs: list[jnp.ndar...
  function _generate_observations (line 580) | def _generate_observations(
  function optimized_tree_search (line 609) | def optimized_tree_search(

FILE: pymdp/planning/visualize.py
  function action_to_string (line 22) | def action_to_string(action: Any, model: Any = None) -> str:
  function observation_to_string (line 50) | def observation_to_string(observation: Any, model: Any = None) -> str:
  function formatting_jax (line 65) | def formatting_jax(value: Any, format_str: str = ".2f") -> str:
  function plot_plan_tree (line 86) | def plot_plan_tree(
  function visualize_plan_tree (line 259) | def visualize_plan_tree(
  function visualize_beliefs (line 343) | def visualize_beliefs(info: dict[str, Any], agent_idx: int = 0, model: A...
  function visualize_env (line 377) | def visualize_env(

FILE: pymdp/utils.py
  function norm_dist (line 27) | def norm_dist(dist: Array) -> Array:
  function list_array_norm_dist (line 42) | def list_array_norm_dist(dist_list: list[Array]) -> list[Array]:
  function resolve_a_dependencies (line 58) | def resolve_a_dependencies(
  function resolve_b_dependencies (line 69) | def resolve_b_dependencies(
  function resolve_b_action_dependencies (line 79) | def resolve_b_action_dependencies(
  function validate_normalization (line 88) | def validate_normalization(tensor: Array, axis: int = 1, tensor_name: st...
  function random_factorized_categorical (line 147) | def random_factorized_categorical(key: Array, dims_per_var: Sequence[int...
  function random_A_array (line 169) | def random_A_array(
  function random_B_array (line 209) | def random_B_array(
  function create_controllable_B (line 262) | def create_controllable_B(
  function list_array_uniform (line 296) | def list_array_uniform(shape_list: Sequence[Sequence[int]]) -> list[Array]:
  function list_array_zeros (line 316) | def list_array_zeros(shape_list: Sequence[Sequence[int]]) -> list[Array]:
  function list_array_scaled (line 335) | def list_array_scaled(
  function get_combination_index (line 359) | def get_combination_index(x: jax.Array | np.ndarray, dims: Sequence[int]...
  function index_to_combination (line 386) | def index_to_combination(index: jax.Array | np.ndarray, dims: Sequence[i...
  function make_A_full (line 410) | def make_A_full(
  function fig2img (line 450) | def fig2img(fig: Any) -> np.ndarray:
  function A_dep_factors_dist (line 479) | def A_dep_factors_dist(num_states: Sequence[int], A_dep_len: int) -> Array:
  function A_dep_len_dist (line 504) | def A_dep_len_dist(choices: Array, curr_sf_dim: int, max_sf_dim: int) ->...
  function A_dep_len_dist_unconditional (line 526) | def A_dep_len_dist_unconditional(choices: Array) -> Array:
  function generate_agent_spec (line 544) | def generate_agent_spec(
  function generate_agent_specs_from_parameter_sets (line 730) | def generate_agent_specs_from_parameter_sets(
  function apply_padding_batched (line 821) | def apply_padding_batched(xs: list[Array]) -> Array:
  function get_sample_obs (line 854) | def get_sample_obs(num_obs: Sequence[int], batch_size: int = 1) -> list[...
  function init_A_and_D_from_spec (line 872) | def init_A_and_D_from_spec(
  function build_block_diag_A (line 943) | def build_block_diag_A(
  function preprocess_A_for_block_diag (line 976) | def preprocess_A_for_block_diag(
  function prepare_obs_for_block_diag (line 994) | def prepare_obs_for_block_diag(obs: list[Array], num_obs: Sequence[int])...
  function concatenate_observations_block_diag (line 1014) | def concatenate_observations_block_diag(obs_list: list[Array]) -> Array:
  function apply_A_end2end_padding_batched (line 1029) | def apply_A_end2end_padding_batched(A: list[Array]) -> Array:
  function apply_obs_end2end_padding_batched (line 1059) | def apply_obs_end2end_padding_batched(obs: list[Array], max_obs_dim: int...

FILE: scripts/notebook_precommit.py
  function load_manifest (line 22) | def load_manifest(path: Path) -> set[str]:
  function strip_top_level_metadata (line 30) | def strip_top_level_metadata(notebook: nbformat.NotebookNode) -> bool:
  function canonicalize_execution_counts (line 39) | def canonicalize_execution_counts(notebook: nbformat.NotebookNode) -> bool:
  function to_repo_relative (line 77) | def to_repo_relative(path_str: str) -> str | None:
  function classify_notebooks (line 88) | def classify_notebooks(path_args: list[str]) -> tuple[list[Path], list[P...
  function report_unclassified_notebooks (line 119) | def report_unclassified_notebooks(paths: list[str]) -> int:
  function sanitize_ci_notebooks (line 133) | def sanitize_ci_notebooks(paths: list[Path]) -> list[str]:
  function sanitize_nightly_notebooks (line 148) | def sanitize_nightly_notebooks(paths: list[Path]) -> None:
  function validate_manifest_notebooks (line 170) | def validate_manifest_notebooks(paths: list[Path]) -> list[str]:
  function run_sanitize (line 201) | def run_sanitize(path_args: list[str]) -> int:
  function run_validate_counts (line 222) | def run_validate_counts(path_args: list[str]) -> int:
  function parse_args (line 241) | def parse_args() -> argparse.Namespace:
  function main (line 254) | def main() -> int:

FILE: scripts/run_notebook_manifest.py
  function load_manifest (line 14) | def load_manifest(path: Path) -> list[str]:
  function parse_args (line 30) | def parse_args() -> argparse.Namespace:
  function has_explicit_numprocesses (line 48) | def has_explicit_numprocesses(pytest_args: list[str]) -> bool:
  function main (line 55) | def main() -> int:

FILE: test/conftest.py
  function pytest_configure (line 4) | def pytest_configure(config):

FILE: test/test_SPM_validation.py
  class TestSPM (line 13) | class TestSPM(unittest.TestCase):
    method test_active_inference_SPM_1a (line 15) | def test_active_inference_SPM_1a(self):
    method test_BMR_SPM_a (line 72) | def test_BMR_SPM_a(self):
    method test_BMR_SPM_b (line 98) | def test_BMR_SPM_b(self):

FILE: test/test_agent.py
  class TestAgent (line 19) | class TestAgent(unittest.TestCase):
    method test_agent_init_without_control_fac_idx (line 21) | def test_agent_init_without_control_fac_idx(self):
    method test_reset_agent_VANILLA (line 39) | def test_reset_agent_VANILLA(self):
    method test_reset_agent_MMP_wBMA (line 57) | def test_reset_agent_MMP_wBMA(self):
    method test_reset_agent_MMP_wPSP (line 75) | def test_reset_agent_MMP_wPSP(self):
    method test_agent_infer_states (line 91) | def test_agent_infer_states(self):
    method test_mmp_active_inference (line 172) | def test_mmp_active_inference(self):
    method test_agent_with_A_learning_vanilla (line 202) | def test_agent_with_A_learning_vanilla(self):
    method test_agent_with_A_learning_vanilla_factorized (line 248) | def test_agent_with_A_learning_vanilla_factorized(self):
    method test_agent_with_B_learning_vanilla (line 293) | def test_agent_with_B_learning_vanilla(self):
    method test_agent_with_D_learning_vanilla (line 343) | def test_agent_with_D_learning_vanilla(self):
    method test_agent_with_D_learning_MMP (line 431) | def test_agent_with_D_learning_MMP(self):
    method test_agent_with_input_alpha (line 502) | def test_agent_with_input_alpha(self):
    method test_agent_with_sampling_mode (line 531) | def test_agent_with_sampling_mode(self):
    method test_agent_with_stochastic_action_unidimensional_control (line 562) | def test_agent_with_stochastic_action_unidimensional_control(self):
    method test_agent_distributional_obs (line 587) | def test_agent_distributional_obs(self):
    method test_agent_with_factorized_inference (line 669) | def test_agent_with_factorized_inference(self):
    method test_agent_with_interactions_in_B (line 704) | def test_agent_with_interactions_in_B(self):
    method test_actinfloop_factorized (line 735) | def test_actinfloop_factorized(self):

FILE: test/test_agent_jax.py
  class TestAgentJax (line 23) | class TestAgentJax(unittest.TestCase):
    method test_no_desired_batch_no_batched_input_construction (line 25) | def test_no_desired_batch_no_batched_input_construction(self):
    method test_desired_batch_no_batched_input_construction (line 105) | def test_desired_batch_no_batched_input_construction(self):
    method test_desired_batch_and_batched_input_construction (line 188) | def test_desired_batch_and_batched_input_construction(self):
    method test_vmappable_agent_methods (line 334) | def test_vmappable_agent_methods(self):
    method test_agent_complex_action (line 372) | def test_agent_complex_action(self):
    method test_infer_policies_neg_efe_sign_convention (line 452) | def test_infer_policies_neg_efe_sign_convention(self):
    method test_agent_validate_normalization_ok (line 480) | def test_agent_validate_normalization_ok(self):
    method test_agent_validate_normalization_raises_on_bad_A (line 506) | def test_agent_validate_normalization_raises_on_bad_A(self):
    method test_agent_validate_normalization_raises_on_bad_B (line 536) | def test_agent_validate_normalization_raises_on_bad_B(self):
    method test_agent_with_A_learning_requires_pA (line 566) | def test_agent_with_A_learning_requires_pA(self):
    method test_agent_construction_jittable (line 581) | def test_agent_construction_jittable(self):
    method test_b_learning_updates_inductive_matrix (line 642) | def test_b_learning_updates_inductive_matrix(self):
    method test_valid_gradients_one_step_ahead (line 696) | def test_valid_gradients_one_step_ahead(self):
    method test_smoothing_ovf_updates_A_when_learn_B_false (line 837) | def test_smoothing_ovf_updates_A_when_learn_B_false(self):

FILE: test/test_categorical_observations.py
  class TestCategoricalObservationsCore (line 13) | class TestCategoricalObservationsCore(unittest.TestCase):
    method test_uncertain_observation_inference (line 16) | def test_uncertain_observation_inference(self):
    method test_categorical_multimodality (line 44) | def test_categorical_multimodality(self):
    method test_multi_factor_categorical (line 66) | def test_multi_factor_categorical(self):
  class TestCategoricalObservationsEdgeCases (line 85) | class TestCategoricalObservationsEdgeCases(unittest.TestCase):
    method test_near_zero_probabilities (line 88) | def test_near_zero_probabilities(self):
    method test_very_peaked_distribution (line 107) | def test_very_peaked_distribution(self):
    method test_uniform_categorical_observation (line 126) | def test_uniform_categorical_observation(self):
  class TestCategoricalObservationsInferenceAlgorithms (line 145) | class TestCategoricalObservationsInferenceAlgorithms(unittest.TestCase):
    method setUp (line 148) | def setUp(self):
    method test_fpi_with_categorical_obs (line 168) | def test_fpi_with_categorical_obs(self):
    method test_fpi_factorized_with_categorical_obs (line 186) | def test_fpi_factorized_with_categorical_obs(self):
    method test_update_posterior_states_with_categorical (line 202) | def test_update_posterior_states_with_categorical(self):
  class TestCategoricalObservationsAgent (line 229) | class TestCategoricalObservationsAgent(unittest.TestCase):
    method test_agent_categorical_flag_false_discrete_obs (line 232) | def test_agent_categorical_flag_false_discrete_obs(self):
    method test_agent_categorical_flag_true_categorical_obs (line 255) | def test_agent_categorical_flag_true_categorical_obs(self):
    method test_agent_categorical_override (line 278) | def test_agent_categorical_override(self):
    method test_agent_preprocess_fn_default_and_warning (line 301) | def test_agent_preprocess_fn_default_and_warning(self):
    method test_agent_full_loop_categorical (line 340) | def test_agent_full_loop_categorical(self):
  class TestCategoricalObservationsControl (line 372) | class TestCategoricalObservationsControl(unittest.TestCase):
    method test_policy_inference_with_categorical_obs (line 375) | def test_policy_inference_with_categorical_obs(self):
    method test_info_gain_with_categorical_obs (line 398) | def test_info_gain_with_categorical_obs(self):
    method test_parameter_info_gain_with_categorical_obs (line 424) | def test_parameter_info_gain_with_categorical_obs(self):
  class TestCategoricalObservationsLearning (line 451) | class TestCategoricalObservationsLearning(unittest.TestCase):
    method test_learning_A_matrix_with_categorical (line 454) | def test_learning_A_matrix_with_categorical(self):
    method test_learning_with_uncertain_observations (line 494) | def test_learning_with_uncertain_observations(self):
  class TestCategoricalObservationsBatched (line 552) | class TestCategoricalObservationsBatched(unittest.TestCase):
    method test_batched_categorical_observations (line 555) | def test_batched_categorical_observations(self):

FILE: test/test_control.py
  class TestControl (line 15) | class TestControl(unittest.TestCase):
    method test_get_expected_states (line 17) | def test_get_expected_states(self):
    method test_get_expected_states_interactions_single_factor (line 101) | def test_get_expected_states_interactions_single_factor(self):
    method test_get_expected_states_interactions_multi_factor (line 120) | def test_get_expected_states_interactions_multi_factor(self):
    method test_get_expected_states_interactions_multi_factor_independent (line 143) | def test_get_expected_states_interactions_multi_factor_independent(self):
    method test_get_expected_obs_factorized (line 166) | def test_get_expected_obs_factorized(self):
    method test_get_expected_states_and_obs (line 213) | def test_get_expected_states_and_obs(self):
    method test_expected_utility (line 320) | def test_expected_utility(self):
    method test_state_info_gain (line 390) | def test_state_info_gain(self):
    method test_state_info_gain_factorized (line 466) | def test_state_info_gain_factorized(self):
    method test_pA_info_gain (line 566) | def test_pA_info_gain(self):
    method test_pB_info_gain (line 618) | def test_pB_info_gain(self):
    method test_update_posterior_policies_utility (line 661) | def test_update_posterior_policies_utility(self):
    method test_temporal_C_matrix (line 809) | def test_temporal_C_matrix(self):
    method test_update_posterior_policies_states_infogain (line 964) | def test_update_posterior_policies_states_infogain(self):
    method test_update_posterior_policies_pA_infogain (line 1094) | def test_update_posterior_policies_pA_infogain(self):
    method test_update_posterior_policies_pB_infogain (line 1230) | def test_update_posterior_policies_pB_infogain(self):
    method test_update_posterior_policies_factorized (line 1363) | def test_update_posterior_policies_factorized(self):
    method test_sample_action (line 1400) | def test_sample_action(self):
    method test_sample_policy (line 1564) | def test_sample_policy(self):
    method test_update_posterior_policies_withE_vector (line 1586) | def test_update_posterior_policies_withE_vector(self):
    method test_stochastic_action_unidimensional_control (line 1627) | def test_stochastic_action_unidimensional_control(self):
    method test_deterministic_action_sampling_equal_value (line 1644) | def test_deterministic_action_sampling_equal_value(self):
    method test_deterministic_policy_selection_equal_value (line 1663) | def test_deterministic_policy_selection_equal_value(self):

FILE: test/test_control_jax.py
  function generate_model_params (line 28) | def generate_model_params():
  class TestControlJax (line 53) | class TestControlJax(unittest.TestCase):
    method test_get_expected_obs_factorized (line 55) | def test_get_expected_obs_factorized(self):
    method test_info_gain_factorized (line 76) | def test_info_gain_factorized(self):
    method test_update_posterior_policies_accepts_partial_param_posteriors (line 146) | def test_update_posterior_policies_accepts_partial_param_posteriors(se...
    method test_update_posterior_policies_requires_param_posterior_when_enabled (line 197) | def test_update_posterior_policies_requires_param_posterior_when_enabl...

FILE: test/test_cue_chaining_env.py
  class TestCueChainingEnv (line 11) | class TestCueChainingEnv(unittest.TestCase):
    method test_shapes_and_dependencies (line 12) | def test_shapes_and_dependencies(self):
    method test_cue_and_reward_likelihood_semantics (line 34) | def test_cue_and_reward_likelihood_semantics(self):
    method test_env_params_broadcast (line 66) | def test_env_params_broadcast(self):
    method test_rollout_smoke (line 75) | def test_rollout_smoke(self):

FILE: test/test_demos.py
  class TestDemos (line 15) | class TestDemos(unittest.TestCase):
    method test_agent_demo (line 17) | def test_agent_demo(self):
    method test_tmaze_demo (line 63) | def test_tmaze_demo(self):
    method test_tmaze_learning_demo (line 117) | def test_tmaze_learning_demo(self):
    method test_gridworld_genmodel_construction (line 187) | def test_gridworld_genmodel_construction(self):
    method test_gridworld_activeinference (line 253) | def test_gridworld_activeinference(self):

FILE: test/test_distribution.py
  class TestDists (line 4) | class TestDists(unittest.TestCase):
    method test_distribution_slice (line 6) | def test_distribution_slice(self):
    method test_distribution_get_set (line 29) | def test_distribution_get_set(self):
    method test_agent_compile (line 74) | def test_agent_compile(self):
    method test_tensor_shape_change_protection (line 115) | def test_tensor_shape_change_protection(self):

FILE: test/test_env.py
  function _stack_params (line 16) | def _stack_params(params_list):
  function _make_deterministic_params (line 20) | def _make_deterministic_params(toggle_action_one=True):
  function _make_stochastic_params (line 37) | def _make_stochastic_params():
  class TestPymdpEnv (line 52) | class TestPymdpEnv(unittest.TestCase):
    method setUp (line 53) | def setUp(self):
    method test_reset_respects_state_override (line 57) | def test_reset_respects_state_override(self):
    method test_step_action_none_keeps_state (line 82) | def test_step_action_none_keeps_state(self):
    method test_env_params_override_defaults (line 107) | def test_env_params_override_defaults(self):
    method test_generate_env_params_batches (line 137) | def test_generate_env_params_batches(self):
    method test_vmap_over_keys_matches_manual (line 157) | def test_vmap_over_keys_matches_manual(self):
    method test_vmap_over_env_params_matches_manual (line 190) | def test_vmap_over_env_params_matches_manual(self):
    method test_vmap_over_state_action_and_keys (line 223) | def test_vmap_over_state_action_and_keys(self):
    method test_make_env_params_respects_input_batch (line 253) | def test_make_env_params_respects_input_batch(self):

FILE: test/test_fpi.py
  class TestFPI (line 15) | class TestFPI(unittest.TestCase):
    method test_factorized_fpi_one_factor_one_modality (line 17) | def test_factorized_fpi_one_factor_one_modality(self):
    method test_factorized_fpi_one_factor_multi_modality (line 43) | def test_factorized_fpi_one_factor_multi_modality(self):
    method test_factorized_fpi_multi_factor_one_modality (line 68) | def test_factorized_fpi_multi_factor_one_modality(self):
    method test_factorized_fpi_multi_factor_multi_modality (line 93) | def test_factorized_fpi_multi_factor_multi_modality(self):
    method test_factorized_fpi_multi_factor_multi_modality_with_condind (line 126) | def test_factorized_fpi_multi_factor_multi_modality_with_condind(self):
    method test_factorized_fpi_multi_factor_single_modality_with_condind (line 162) | def test_factorized_fpi_multi_factor_single_modality_with_condind(self):

FILE: test/test_grid_world_parity.py
  class TestGridWorldParity (line 17) | class TestGridWorldParity(unittest.TestCase):
    method test_jax_matches_legacy_transition_and_observation (line 20) | def test_jax_matches_legacy_transition_and_observation(self):
    method test_step_outputs_match (line 54) | def test_step_outputs_match(self):
    method test_batched_jax_env_matches_series_of_numpy_envs (line 106) | def test_batched_jax_env_matches_series_of_numpy_envs(self):

FILE: test/test_hmm_associative_scan.py
  function _normalize_rows (line 23) | def _normalize_rows(x, axis=-1):
  function _random_simplex (line 28) | def _random_simplex(key, shape):
  function _normalize_cols (line 33) | def _normalize_cols(x, axis=0):
  function _random_col_stochastic (line 38) | def _random_col_stochastic(key, shape):
  function _sparse_absorbing_rowstoch (line 44) | def _sparse_absorbing_rowstoch(K):
  function _near_zero_rowstoch (line 56) | def _near_zero_rowstoch(K, tiny=1e-20):
  function _reference_filter (line 66) | def _reference_filter(initial_probs, transition_mats, log_likelihoods):
  function _reference_smoother (line 111) | def _reference_smoother(initial_probs, transition_mats, log_likelihoods):
  function _reference_filter_col (line 157) | def _reference_filter_col(initial_probs, B_mats, log_likelihoods):
  function _reference_smoother_col (line 200) | def _reference_smoother_col(initial_probs, B_mats, log_likelihoods):
  class TestAssociativeScanHMM (line 243) | class TestAssociativeScanHMM(unittest.TestCase):
    method _make_case (line 244) | def _make_case(self, key, T, K, time_varying=False):
    method _make_case_col (line 254) | def _make_case_col(self, key, T, K, time_varying=False):
    method _make_batch_row (line 264) | def _make_batch_row(self, key, batch_size, T, K, time_varying=False):
    method _make_batch_col (line 271) | def _make_batch_col(self, key, batch_size, T, K, time_varying=False):
    method _assert_close (line 278) | def _assert_close(self, a, b, atol=1e-5, rtol=1e-5):
    method _assert_all_finite_tree (line 281) | def _assert_all_finite_tree(self, tree):
    method test_filter_scan_matches_reference_stationary (line 285) | def test_filter_scan_matches_reference_stationary(self):
    method test_smoother_scan_matches_reference_time_varying (line 302) | def test_smoother_scan_matches_reference_time_varying(self):
    method test_smoother_scan_T1_edge (line 322) | def test_smoother_scan_T1_edge(self):
    method test_filter_scan_colstoch_matches_reference_stationary (line 342) | def test_filter_scan_colstoch_matches_reference_stationary(self):
    method test_smoother_scan_colstoch_matches_reference_time_varying (line 359) | def test_smoother_scan_colstoch_matches_reference_time_varying(self):
    method test_row_col_equivalence (line 379) | def test_row_col_equivalence(self):
    method test_smoother_scan_colstoch_stability_shifted_ll (line 402) | def test_smoother_scan_colstoch_stability_shifted_ll(self):
    method test_vmap_filter_scan_row (line 423) | def test_vmap_filter_scan_row(self):
    method test_vmap_smoother_scan_row (line 435) | def test_vmap_smoother_scan_row(self):
    method test_vmap_filter_scan_col (line 447) | def test_vmap_filter_scan_col(self):
    method test_vmap_smoother_scan_col (line 459) | def test_vmap_smoother_scan_col(self):
    method test_gradients_finite_rowstoch_smoother (line 472) | def test_gradients_finite_rowstoch_smoother(self):
    method test_gradients_finite_colstoch_smoother (line 506) | def test_gradients_finite_colstoch_smoother(self):
    method test_strict_zero_absorbing_transitions_finite_outputs_and_grads (line 542) | def test_strict_zero_absorbing_transitions_finite_outputs_and_grads(se...
    method test_near_zero_transitions_finite_outputs_and_grads (line 592) | def test_near_zero_transitions_finite_outputs_and_grads(self):
    method test_directional_derivative_rowstoch_filter_mll (line 642) | def test_directional_derivative_rowstoch_filter_mll(self):
    method test_update_posterior_states_exact_inference_matches_wrapper (line 679) | def test_update_posterior_states_exact_inference_matches_wrapper(self):
    method test_exact_inference_respects_inference_horizon (line 715) | def test_exact_inference_respects_inference_horizon(self):
    method test_exact_inference_loop_with_policy_inference_and_learning (line 757) | def test_exact_inference_loop_with_policy_inference_and_learning(self):
    method test_exact_inference_accepts_missing_past_actions (line 823) | def test_exact_inference_accepts_missing_past_actions(self):
    method test_exact_inference_rejects_multi_factor_models (line 855) | def test_exact_inference_rejects_multi_factor_models(self):

FILE: test/test_inductive_inference_jax.py
  function _chain_transition (line 15) | def _chain_transition(num_states: int) -> jnp.ndarray:
  function _advance_or_stay_transition (line 24) | def _advance_or_stay_transition() -> jnp.ndarray:
  function _manual_chain_I (line 41) | def _manual_chain_I() -> list[jnp.ndarray]:
  class TestInductiveInferenceJax (line 53) | class TestInductiveInferenceJax(unittest.TestCase):
    method test_generate_I_matrix_rejects_nonpositive_depth (line 55) | def test_generate_I_matrix_rejects_nonpositive_depth(self):
    method test_generate_I_matrix_matches_chain_reachability (line 62) | def test_generate_I_matrix_matches_chain_reachability(self):
    method test_generate_I_matrix_respects_threshold_pruning (line 79) | def test_generate_I_matrix_respects_threshold_pruning(self):
    method test_generate_I_matrix_respects_depth_truncation (line 104) | def test_generate_I_matrix_respects_depth_truncation(self):
    method test_calc_inductive_value_on_path_zero_and_off_path_logeps (line 114) | def test_calc_inductive_value_on_path_zero_and_off_path_logeps(self):
    method test_calc_inductive_value_scales_with_off_path_mass (line 125) | def test_calc_inductive_value_scales_with_off_path_mass(self):
    method test_calc_inductive_value_is_zero_when_goal_unreachable (line 135) | def test_calc_inductive_value_is_zero_when_goal_unreachable(self):
    method test_calc_inductive_value_depends_on_map_current_state (line 153) | def test_calc_inductive_value_depends_on_map_current_state(self):
    method test_one_step_policy_ranking_prefers_goal_directed_action (line 168) | def test_one_step_policy_ranking_prefers_goal_directed_action(self):
    method test_compute_neg_efe_policy_inductive_matches_non_inductive_when_disabled (line 203) | def test_compute_neg_efe_policy_inductive_matches_non_inductive_when_d...
    method test_multistep_inductive_scoring_stays_anchored_to_qs_init (line 244) | def test_multistep_inductive_scoring_stays_anchored_to_qs_init(self):

FILE: test/test_infer_states_optimized.py
  class TestInferStatesComparison (line 49) | class TestInferStatesComparison(unittest.TestCase):
    method setUpClass (line 60) | def setUpClass(cls):
    method should_skip_spec (line 100) | def should_skip_spec(cls, spec):
    method get_specs_subset (line 126) | def get_specs_subset(cls, max_specs=None, filter_fn=None):
    method _compare_results (line 146) | def _compare_results(self, r1, r2, m1, m2, spec):
    method _test_single_spec_with_batch (line 191) | def _test_single_spec_with_batch(self, spec, batch_size=4, A_sparsity_...
    method test_first_spec_with_batch (line 282) | def test_first_spec_with_batch(self):
    method test_small_subset_with_batch (line 286) | def test_small_subset_with_batch(self):
    method test_different_batch_sizes (line 308) | def test_different_batch_sizes(self):
    method test_low_complexity_specs_with_batch (line 316) | def test_low_complexity_specs_with_batch(self):
    method test_sparsity_with_batch (line 344) | def test_sparsity_with_batch(self):
    method test_all_agents_with_batch (line 360) | def test_all_agents_with_batch(self):

FILE: test/test_inference.py
  class TestInference (line 15) | class TestInference(unittest.TestCase):
    method test_update_posterior_states (line 17) | def test_update_posterior_states(self):
    method test_update_posterior_states_factorized_single_factor (line 110) | def test_update_posterior_states_factorized_single_factor(self):
    method test_update_posterior_states_factorized (line 141) | def test_update_posterior_states_factorized(self):
    method test_update_posterior_states_factorized_noVFE_compute (line 175) | def test_update_posterior_states_factorized_noVFE_compute(self):

FILE: test/test_inference_jax.py
  class TestInferenceJax (line 19) | class TestInferenceJax(unittest.TestCase):
    method test_fixed_point_iteration_singlestate_singleobs (line 21) | def test_fixed_point_iteration_singlestate_singleobs(self):
    method test_fixed_point_iteration_singlestate_multiobs (line 62) | def test_fixed_point_iteration_singlestate_multiobs(self):
    method test_fixed_point_iteration_multistate_singleobs (line 104) | def test_fixed_point_iteration_multistate_singleobs(self):
    method test_fixed_point_iteration_multistate_multiobs (line 146) | def test_fixed_point_iteration_multistate_multiobs(self):
    method test_fixed_point_iteration_index_observations (line 191) | def test_fixed_point_iteration_index_observations(self):

FILE: test/test_jax_sparse_backend.py
  function make_model_configs (line 25) | def make_model_configs(source_seed=0, num_models=4) -> Dict:
  class TestJaxSparseOperations (line 77) | class TestJaxSparseOperations(unittest.TestCase):
    method test_sparse_smoothing (line 79) | def test_sparse_smoothing(self):
    method test_sparse_smoothing_with_invalid_actions (line 148) | def test_sparse_smoothing_with_invalid_actions(self):

FILE: test/test_learning.py
  class TestLearning (line 8) | class TestLearning(unittest.TestCase):
    method test_update_pA_single_factor_all (line 10) | def test_update_pA_single_factor_all(self):
    method test_update_pA_single_factor_one_modality (line 47) | def test_update_pA_single_factor_one_modality(self):
    method test_update_pA_single_factor_some_modalities (line 78) | def test_update_pA_single_factor_some_modalities(self):
    method test_update_pA_multi_factor_all (line 107) | def test_update_pA_multi_factor_all(self):
    method test_update_pA_multi_factor_one_modality (line 140) | def test_update_pA_multi_factor_one_modality(self):
    method test_update_pA_multi_factor_some_modalities (line 167) | def test_update_pA_multi_factor_some_modalities(self):
    method test_update_pA_diff_observation_formats (line 194) | def test_update_pA_diff_observation_formats(self):
    method test_update_pA_factorized (line 253) | def test_update_pA_factorized(self):
    method test_update_pB_single_factor_no_actions (line 300) | def test_update_pB_single_factor_no_actions(self):
    method test_update_pB_single_factor_with_actions (line 328) | def test_update_pB_single_factor_with_actions(self):
    method test_update_pB_multi_factor_no_actions_all_factors (line 356) | def test_update_pB_multi_factor_no_actions_all_factors(self):
    method test_update_pB_multi_factor_no_actions_one_factor (line 388) | def test_update_pB_multi_factor_no_actions_one_factor(self):
    method test_update_pB_multi_factor_no_actions_some_factors (line 424) | def test_update_pB_multi_factor_no_actions_some_factors(self):
    method test_update_pB_multi_factor_with_actions_all_factors (line 460) | def test_update_pB_multi_factor_with_actions_all_factors(self):
    method test_update_pB_multi_factor_with_actions_one_factor (line 493) | def test_update_pB_multi_factor_with_actions_one_factor(self):
    method test_update_pB_multi_factor_with_actions_some_factors (line 529) | def test_update_pB_multi_factor_with_actions_some_factors(self):
    method test_update_pB_multi_factor_some_controllable_some_factors (line 565) | def test_update_pB_multi_factor_some_controllable_some_factors(self):
    method test_update_pB_interactions (line 601) | def test_update_pB_interactions(self):
    method test_update_pD (line 665) | def test_update_pD(self):
    method test_prune_prior (line 730) | def test_prune_prior(self):
    method test_prune_likelihoods (line 775) | def test_prune_likelihoods(self):

FILE: test/test_learning_jax.py
  function _to_numpy_list_of_arrs (line 27) | def _to_numpy_list_of_arrs(jax_tree):
  class TestLearningJax (line 31) | class TestLearningJax(unittest.TestCase):
    method test_update_observation_likelihood_fullyconnected (line 33) | def test_update_observation_likelihood_fullyconnected(self):
    method test_update_observation_likelihood_factorized (line 88) | def test_update_observation_likelihood_factorized(self):
    method test_update_state_likelihood_single_factor_no_actions (line 142) | def test_update_state_likelihood_single_factor_no_actions(self):
    method test_update_state_likelihood_single_factor_with_actions (line 182) | def test_update_state_likelihood_single_factor_with_actions(self):
    method test_update_state_likelihood_multi_factor_all_factors_no_actions (line 222) | def test_update_state_likelihood_multi_factor_all_factors_no_actions(s...
    method test_update_state_likelihood_multi_factor_all_factors_with_actions (line 261) | def test_update_state_likelihood_multi_factor_all_factors_with_actions...
    method test_update_state_likelihood_multi_factor_some_factors_no_action (line 298) | def test_update_state_likelihood_multi_factor_some_factors_no_action(s...
    method test_update_state_likelihood_with_interactions (line 342) | def test_update_state_likelihood_with_interactions(self):
    method test_update_state_likelihood_single_factor_sequence_joints (line 389) | def test_update_state_likelihood_single_factor_sequence_joints(self):

FILE: test/test_message_passing_jax.py
  function make_model_configs (line 27) | def make_model_configs(source_seed=0, num_models=3) -> Dict:
  class TestMessagePassing (line 59) | class TestMessagePassing(unittest.TestCase):
    method test_fixed_point_iteration (line 61) | def test_fixed_point_iteration(self):
    method test_fixed_point_iteration_factorized_fullyconnected (line 90) | def test_fixed_point_iteration_factorized_fullyconnected(self):
    method test_fixed_point_iteration_factorized_sparsegraph (line 120) | def test_fixed_point_iteration_factorized_sparsegraph(self):
    method test_marginal_message_passing (line 153) | def test_marginal_message_passing(self):
    method test_variational_message_passing_with_transition_dependencies (line 204) | def test_variational_message_passing_with_transition_dependencies(self):

FILE: test/test_mmp.py
  class MMP (line 22) | class MMP(unittest.TestCase):
    method test_mmp_a (line 24) | def test_mmp_a(self):
    method test_mmp_b (line 64) | def test_mmp_b(self):
    method test_mmp_c (line 97) | def test_mmp_c(self):
    method test_mmp_d (line 131) | def test_mmp_d(self):

FILE: test/test_param_info_gain_jax.py
  function test_exact_wnorm_finite (line 11) | def test_exact_wnorm_finite(scale):
  function test_exact_wnorm_mathematical_correctness (line 30) | def test_exact_wnorm_mathematical_correctness():
  function test_calc_negative_pA_info_gain_precision (line 60) | def test_calc_negative_pA_info_gain_precision(seed):

FILE: test/test_pybefit_model_fitting.py
  function _build_tmaze_agent_transform (line 19) | def _build_tmaze_agent_transform(task):
  function test_pybefit_tmaze_predictive_smoke (line 72) | def test_pybefit_tmaze_predictive_smoke():

FILE: test/test_rollout_function.py
  class TestRolloutFunction (line 28) | class TestRolloutFunction(unittest.TestCase):
    method setUp (line 29) | def setUp(self):
    method build_agent_env (line 37) | def build_agent_env(
    method manual_windowed_rollout_reference (line 105) | def manual_windowed_rollout_reference(self, agent, env, num_steps, rng...
    method test_rollout_collects_time_series (line 254) | def test_rollout_collects_time_series(self):
    method test_rollout_env_state_matches_manual_steps (line 279) | def test_rollout_env_state_matches_manual_steps(self):
    method test_online_learning_updates_A_during_scan (line 347) | def test_online_learning_updates_A_during_scan(self):
    method test_offline_learning_defers_A_update (line 364) | def test_offline_learning_defers_A_update(self):
    method test_online_learning_updates_B (line 380) | def test_online_learning_updates_B(self):
    method test_online_learning_updates_B_for_sequence_inference (line 399) | def test_online_learning_updates_B_for_sequence_inference(self):
    method test_online_learning_updates_for_smoothing_inference_with_horizon (line 426) | def test_online_learning_updates_for_smoothing_inference_with_horizon(...
    method test_online_learning_updates_A_only_for_sequence_inference (line 477) | def test_online_learning_updates_A_only_for_sequence_inference(self):
    method test_sequence_rollout_updates_empirical_prior_with_finite_horizon (line 506) | def test_sequence_rollout_updates_empirical_prior_with_finite_horizon(...
    method test_sequence_rollout_keeps_empirical_prior_fixed_during_warmup (line 527) | def test_sequence_rollout_keeps_empirical_prior_fixed_during_warmup(se...
    method test_rollout_caps_history_without_inference_horizon (line 551) | def test_rollout_caps_history_without_inference_horizon(self):
    method test_rollout_supports_multiple_inference_algorithms (line 570) | def test_rollout_supports_multiple_inference_algorithms(self):
    method test_rollout_categorical_obs_matches_discrete_semantics (line 593) | def test_rollout_categorical_obs_matches_discrete_semantics(self):
    method test_tmaze_rollout_supports_categorical_observations (line 642) | def test_tmaze_rollout_supports_categorical_observations(self):
    method test_sequence_rollout_supports_interacting_transition_dependencies (line 672) | def test_sequence_rollout_supports_interacting_transition_dependencies...
    method test_rollout_modes_for_ovf_and_exact (line 728) | def test_rollout_modes_for_ovf_and_exact(self):
    method _assert_rollout_matches_manual_reference (line 786) | def _assert_rollout_matches_manual_reference(self, algo, seed, include...
    method test_sequence_rollout_matches_manual_window_branch_reference (line 819) | def test_sequence_rollout_matches_manual_window_branch_reference(self):
    method test_smoothing_rollout_matches_manual_window_branch_reference (line 824) | def test_smoothing_rollout_matches_manual_window_branch_reference(self):
    method test_rollout_with_custom_policy_search_and_initial_carry (line 830) | def test_rollout_with_custom_policy_search_and_initial_carry(self):
    method test_offline_B_learning_matches_outer_products (line 874) | def test_offline_B_learning_matches_outer_products(self):

FILE: test/test_sophisticated_inference_jax.py
  function _build_single_cue_model (line 17) | def _build_single_cue_model():
  function _build_dual_cue_model (line 60) | def _build_dual_cue_model():
  class TestSophisticatedInferenceJax (line 116) | class TestSophisticatedInferenceJax(unittest.TestCase):
    method _run_si_search (line 117) | def _run_si_search(self, agent, horizon):
    method _run_vanilla_search (line 132) | def _run_vanilla_search(self, agent):
    method test_si_accepts_costly_informative_cue (line 137) | def test_si_accepts_costly_informative_cue(self):
    method test_si_ignores_irrelevant_distractor_cue (line 200) | def test_si_ignores_irrelevant_distractor_cue(self):

FILE: test/test_tmaze_envs.py
  class TestTMazeVariants (line 8) | class TestTMazeVariants(unittest.TestCase):
    method test_classic_shapes (line 9) | def test_classic_shapes(self):
    method test_simplified_shapes (line 24) | def test_simplified_shapes(self):
    method test_classic_cue_validity (line 38) | def test_classic_cue_validity(self):
    method test_simplified_cue_validity (line 48) | def test_simplified_cue_validity(self):
    method test_reward_outcomes_independent (line 58) | def test_reward_outcomes_independent(self):
    method test_reward_outcomes_dependent (line 72) | def test_reward_outcomes_dependent(self):
    method test_simplified_reward_and_punishment_probabilities (line 86) | def test_simplified_reward_and_punishment_probabilities(self):
    method test_classic_transition_connectivity (line 100) | def test_classic_transition_connectivity(self):
    method test_simplified_transition_connectivity (line 124) | def test_simplified_transition_connectivity(self):
    method test_render_accepts_singleton_discrete_and_categorical_observations (line 135) | def test_render_accepts_singleton_discrete_and_categorical_observation...

FILE: test/test_tmaze_recoverability.py
  function _small_cfg (line 8) | def _small_cfg(parameterization: str) -> RecoverabilityConfig:
  function test_tmaze_recoverability_three_param_smoke (line 21) | def test_tmaze_recoverability_three_param_smoke():
  function test_tmaze_recoverability_reward_only_smoke (line 32) | def test_tmaze_recoverability_reward_only_smoke():

FILE: test/test_utils.py
  class TestUtils (line 15) | class TestUtils(unittest.TestCase):
    method test_obj_array_from_list (line 16) | def test_obj_array_from_list(self):

FILE: test/test_utils_jax.py
  class TestUtils (line 19) | class TestUtils(unittest.TestCase):
    method test_random_factorized_categorical (line 21) | def test_random_factorized_categorical(self):
    method test_random_A_array_shapes_and_normalization (line 39) | def test_random_A_array_shapes_and_normalization(self):
    method test_random_A_array_defaults_to_all_factors (line 63) | def test_random_A_array_defaults_to_all_factors(self):
    method test_random_B_array_shapes_and_normalization (line 80) | def test_random_B_array_shapes_and_normalization(self):
    method test_random_B_array_defaults_to_self_dependencies (line 109) | def test_random_B_array_defaults_to_self_dependencies(self):
    method test_random_B_array_accepts_out_of_order_control_dependency_lists (line 130) | def test_random_B_array_accepts_out_of_order_control_dependency_lists(...
    method test_norm_dist_list_version (line 150) | def test_norm_dist_list_version(self):
    method test_get_combination_index (line 164) | def test_get_combination_index(self):
    method test_index_to_combination (line 184) | def test_index_to_combination(self):
    method test_validate_normalization_ok (line 204) | def test_validate_normalization_ok(self):
    method test_validate_normalization_zero_filled_raises (line 213) | def test_validate_normalization_zero_filled_raises(self):
    method test_validate_normalization_not_normalised_raises (line 221) | def test_validate_normalization_not_normalised_raises(self):
    method test_validate_normalization_axis_argument (line 230) | def test_validate_normalization_axis_argument(self):
    method test_create_controllable_B_matches_legacy (line 242) | def test_create_controllable_B_matches_legacy(self):

FILE: test/test_vfe_jax.py
  function _manual_dirichlet_kl (line 20) | def _manual_dirichlet_kl(q_dir: np.ndarray, p_dir: np.ndarray) -> float:
  function _bruteforce_sequence_vfe (line 34) | def _bruteforce_sequence_vfe(
  function _filtered_history_single_factor (line 57) | def _filtered_history_single_factor(
  function _factorized_fpi_vfe_history (line 86) | def _factorized_fpi_vfe_history(
  class TestCanonicalVFE (line 134) | class TestCanonicalVFE(unittest.TestCase):
    method test_calc_vfe_single_step_matches_legacy_full_model (line 135) | def test_calc_vfe_single_step_matches_legacy_full_model(self):
    method test_calc_vfe_sequence_matches_manual_terms_and_parameter_kls (line 184) | def test_calc_vfe_sequence_matches_manual_terms_and_parameter_kls(self):
    method test_calc_vfe_rejects_multifactor_1d_past_actions (line 261) | def test_calc_vfe_rejects_multifactor_1d_past_actions(self):
    method test_calc_vfe_accepts_multifactor_single_transition_action_history_as_2d (line 293) | def test_calc_vfe_accepts_multifactor_single_transition_action_history...
    method test_calc_vfe_rejects_mismatched_past_action_history_length_when_transitions_used (line 335) | def test_calc_vfe_rejects_mismatched_past_action_history_length_when_t...
    method test_calc_vfe_accepts_mismatched_past_action_history_when_no_transitions_used (line 364) | def test_calc_vfe_accepts_mismatched_past_action_history_when_no_trans...
    method test_update_posterior_states_rejects_multifactor_1d_past_actions (line 384) | def test_update_posterior_states_rejects_multifactor_1d_past_actions(s...
    method test_update_posterior_states_accepts_multifactor_single_transition_action_history_as_2d (line 416) | def test_update_posterior_states_accepts_multifactor_single_transition...
    method test_update_posterior_states_rejects_mismatched_past_action_history_length (line 459) | def test_update_posterior_states_rejects_mismatched_past_action_histor...
    method test_sequence_inference_uses_singleton_control_transitions_without_past_actions (line 488) | def test_sequence_inference_uses_singleton_control_transitions_without...
    method test_sequence_return_info_uses_singleton_control_transitions_without_past_actions (line 516) | def test_sequence_return_info_uses_singleton_control_transitions_witho...
    method test_return_info_requires_prior (line 556) | def test_return_info_requires_prior(self):
    method test_calc_vfe_gradients_are_finite (line 577) | def test_calc_vfe_gradients_are_finite(self):
    method test_calc_vfe_sequence_gradients_are_finite_without_joint_qs (line 595) | def test_calc_vfe_sequence_gradients_are_finite_without_joint_qs(self):
    method test_calc_vfe_sequence_is_jittable_without_joint_qs (line 642) | def test_calc_vfe_sequence_is_jittable_without_joint_qs(self):
    method test_calc_vfe_sequence_gradients_are_finite_with_joint_qs (line 675) | def test_calc_vfe_sequence_gradients_are_finite_with_joint_qs(self):
    method test_calc_vfe_sequence_is_jittable_with_joint_qs (line 732) | def test_calc_vfe_sequence_is_jittable_with_joint_qs(self):
    method test_factorized_fpi_vfe_decreases_monotonically (line 772) | def test_factorized_fpi_vfe_decreases_monotonically(self):
    method test_update_posterior_states_return_info_includes_vfe (line 815) | def test_update_posterior_states_return_info_includes_vfe(self):
    method test_agent_infer_states_return_info_batches_sequence_vfe (line 848) | def test_agent_infer_states_return_info_batches_sequence_vfe(self):
    method test_sequence_return_info_accepts_missing_past_actions (line 906) | def test_sequence_return_info_accepts_missing_past_actions(self):
    method test_sequence_inference_requires_past_actions_for_multiaction_transitions (line 937) | def test_sequence_inference_requires_past_actions_for_multiaction_tran...
    method test_calc_vfe_with_smoothed_exact_posterior_matches_bruteforce_sequence_vfe (line 967) | def test_calc_vfe_with_smoothed_exact_posterior_matches_bruteforce_seq...
    method test_calc_vfe_with_smoothed_ovf_posterior_matches_bruteforce_sequence_vfe (line 1006) | def test_calc_vfe_with_smoothed_ovf_posterior_matches_bruteforce_seque...

FILE: test/test_wrappers.py
  class TestWrappers (line 4) | class TestWrappers(unittest.TestCase):
    method test_get_model_dimensions_from_labels (line 6) | def test_get_model_dimensions_from_labels(self):
Copy disabled (too large) Download .json
Condensed preview — 220 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (10,076K chars).
[
  {
    "path": ".github/workflows/docs.yml",
    "chars": 1203,
    "preview": "name: Docs\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - 'examples/**'\n      - 'mkdocs.yml'\n      - 'docs-m"
  },
  {
    "path": ".github/workflows/lint.yaml",
    "chars": 614,
    "preview": "name: Lint\n\non:\n  pull_request:\n    paths:\n      - '**.py'\n      - '.github/workflows/lint.yaml'\n      - 'pyproject.toml"
  },
  {
    "path": ".github/workflows/manual-branch-nightly.yaml",
    "chars": 1527,
    "preview": "name: Manual Branch Nightly\n\non:\n  workflow_dispatch:\n    inputs:\n      ref:\n        description: Branch, tag, or SHA to"
  },
  {
    "path": ".github/workflows/nightly-tests.yaml",
    "chars": 1027,
    "preview": "name: Nightly Tests\n\non:\n  schedule:\n    - cron: \"0 2 * * *\"\n  workflow_dispatch:\n\njobs:\n  nightly:\n    runs-on: ubuntu-"
  },
  {
    "path": ".github/workflows/python-package.yml",
    "chars": 1031,
    "preview": "name: Python package\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  build:\n    runs-on:"
  },
  {
    "path": ".github/workflows/test.yaml",
    "chars": 1285,
    "preview": "name: Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  build:\n    runs-on: ubuntu-la"
  },
  {
    "path": ".gitignore",
    "chars": 292,
    "preview": "*.pyc\n__pycache__\n.DS_Store\n.ipynb_checkpoints\n.rope*\n.vscode/\n.ipynb_checkpoints/\n.pytest_cache\nenv/\npymdp.egg-info\ninf"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 580,
    "preview": "repos:\n  - repo: local\n    hooks:\n      - id: sanitize-notebooks\n        name: sanitize notebooks by tier\n        entry:"
  },
  {
    "path": ".readthedocs.yml",
    "chars": 284,
    "preview": "version: 2\n\nbuild:\n  os: \"ubuntu-22.04\"\n  tools:\n    python: \"3.11\"\n  jobs:\n    pre_build:\n      - ./scripts/sync_docs_n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 9862,
    "preview": "# pymdp\n\n*Borrowed below list from [here](https://github.com/netsiphds/netrd)*\n\nWelcome to `pymdp` and thanks for your i"
  },
  {
    "path": "LICENSE",
    "chars": 1086,
    "preview": "MIT License\n\nCopyright (c) 2019 Conor Heins and Alec Tschantz\n\nPermission is hereby granted, free of charge, to any pers"
  },
  {
    "path": "README.md",
    "chars": 9237,
    "preview": "\n<p align='center'>\n  <a href='https://github.com/infer-actively/pymdp'>\n    <img src='.github/pymdp_logo_2-removebg.png"
  },
  {
    "path": "docs/Makefile",
    "chars": 634,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
  },
  {
    "path": "docs/agent.rst",
    "chars": 94,
    "preview": "Agent class\n=================================\n\n.. autoclass:: pymdp.agent.Agent\n    :members:\n"
  },
  {
    "path": "docs/algos/fpi.rst",
    "chars": 110,
    "preview": "FPI (Fixed Point Iteration)\n=================================\n\n.. automodule:: pymdp.algos.fpi\n    :members:\n\n"
  },
  {
    "path": "docs/algos/index.rst",
    "chars": 261,
    "preview": "Algos\n=================================\n\nThe ``algos.py`` library contains the functions for implementing message passin"
  },
  {
    "path": "docs/algos/mmp.rst",
    "chars": 111,
    "preview": "MMP (Marginal Message Passing)\n=================================\n\n.. automodule:: pymdp.algos.mmp\n    :members:"
  },
  {
    "path": "docs/conf.py",
    "chars": 2764,
    "preview": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common op"
  },
  {
    "path": "docs/control.rst",
    "chars": 262,
    "preview": "Control\n=================================\n\nThe ``control.py`` module contains the functions for performing inference of "
  },
  {
    "path": "docs/env.rst",
    "chars": 573,
    "preview": "Env\n========\n\nThe OpenAIGym-inspired ``Env`` base class is the main API that represents the environmental dynamics or \"g"
  },
  {
    "path": "docs/index.rst",
    "chars": 1606,
    "preview": ".. pymdp documentation master file, created by\n   sphinx-quickstart on Fri Oct 29 13:27:58 2021.\n   You can adapt this f"
  },
  {
    "path": "docs/inference.rst",
    "chars": 249,
    "preview": "Inference\n=================================\n\nThe ``inference.py`` module contains the functions for performing inference"
  },
  {
    "path": "docs/installation.rst",
    "chars": 478,
    "preview": "Installation\n=================================\n\nWe recommend installing ``pymdp`` using the package installer pip_, whic"
  },
  {
    "path": "docs/learning.rst",
    "chars": 270,
    "preview": "Learning\n=================================\n\nThe ``learning.py`` module contains the functions for updating parameters of"
  },
  {
    "path": "docs/make.bat",
    "chars": 800,
    "preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sp"
  },
  {
    "path": "docs/notebooks/active_inference_from_scratch.ipynb",
    "chars": 625034,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"Lggo6pjkdPhS\"\n   },\n   \"source\": [\n    \"# Tuto"
  },
  {
    "path": "docs/notebooks/cue_chaining_demo.ipynb",
    "chars": 101890,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Active Inference Demo: Epistemic "
  },
  {
    "path": "docs/notebooks/free_energy_calculation.ipynb",
    "chars": 57719,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Computing the variational free en"
  },
  {
    "path": "docs/notebooks/pymdp_fundamentals.ipynb",
    "chars": 32477,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"6DSxgksDRLXj\"\n   },\n   \"source\": [\n    \"# ``py"
  },
  {
    "path": "docs/notebooks/tmaze_demo.ipynb",
    "chars": 139318,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Active Inference Demo: T-Maze Env"
  },
  {
    "path": "docs/notebooks/using_the_agent_class.ipynb",
    "chars": 208523,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Tutorial 2:  the `Agent` API\\n\",\n"
  },
  {
    "path": "docs/requirements.txt",
    "chars": 550,
    "preview": "# sphinx <4 required by myst-nb v0.12.0 (Feb 2021)\n# sphinx >=3 required by sphinx-autodoc-typehints v1.11.1 (Oct 2020)\n"
  },
  {
    "path": "docs-mkdocs/agent.html",
    "chars": 304,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=api/agent/\" "
  },
  {
    "path": "docs-mkdocs/algos/index.html",
    "chars": 304,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=api/algos/\" "
  },
  {
    "path": "docs-mkdocs/api/agent.md",
    "chars": 77,
    "preview": "# `pymdp.agent`\n\n::: pymdp.agent\n    options:\n      members:\n        - Agent\n"
  },
  {
    "path": "docs-mkdocs/api/algos.md",
    "chars": 241,
    "preview": "# `pymdp.algos`\n\n::: pymdp.algos\n    options:\n      members:\n        - run_factorized_fpi\n        - run_mmp\n        - ru"
  },
  {
    "path": "docs-mkdocs/api/control.md",
    "chars": 645,
    "preview": "# `pymdp.control`\n\n::: pymdp.control\n    options:\n      members:\n        - Policies\n        - construct_policies\n       "
  },
  {
    "path": "docs-mkdocs/api/envs-env.md",
    "chars": 115,
    "preview": "# `pymdp.envs.env`\n\n::: pymdp.envs.env\n    options:\n      members:\n        - make\n        - Env\n        - PymdpEnv\n"
  },
  {
    "path": "docs-mkdocs/api/envs-rollout.md",
    "chars": 118,
    "preview": "# `pymdp.envs.rollout`\n\n::: pymdp.envs.rollout\n    options:\n      members:\n        - infer_and_plan\n        - rollout\n"
  },
  {
    "path": "docs-mkdocs/api/index.md",
    "chars": 449,
    "preview": "# API Reference\n\nThis reference is auto-generated from modern modules (`pymdp.*`) using mkdocstrings.\n\nLegacy modules ar"
  },
  {
    "path": "docs-mkdocs/api/inference.md",
    "chars": 181,
    "preview": "# `pymdp.inference`\n\n::: pymdp.inference\n    options:\n      members:\n        - update_posterior_states\n        - joint_d"
  },
  {
    "path": "docs-mkdocs/api/learning.md",
    "chars": 243,
    "preview": "# `pymdp.learning`\n\n::: pymdp.learning\n    options:\n      members:\n        - update_obs_likelihood_dirichlet_m\n        -"
  },
  {
    "path": "docs-mkdocs/api/maths.md",
    "chars": 33,
    "preview": "# `pymdp.maths`\n\n::: pymdp.maths\n"
  },
  {
    "path": "docs-mkdocs/api/planning-mcts.md",
    "chars": 41,
    "preview": "# MCTS Planning\n\n::: pymdp.planning.mcts\n"
  },
  {
    "path": "docs-mkdocs/api/planning-si.md",
    "chars": 58,
    "preview": "# Sophisticated Inference Planning\n\n::: pymdp.planning.si\n"
  },
  {
    "path": "docs-mkdocs/api/utils.md",
    "chars": 33,
    "preview": "# `pymdp.utils`\n\n::: pymdp.utils\n"
  },
  {
    "path": "docs-mkdocs/control.html",
    "chars": 312,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=api/control/"
  },
  {
    "path": "docs-mkdocs/development/release-notes.md",
    "chars": 9535,
    "preview": "# Release Notes\n\n## 1.0.0\n\n`pymdp` 1.0.0 is the first major release since the `0.0.7` line. The small\n`v0.0.7.1` patch w"
  },
  {
    "path": "docs-mkdocs/development/viewing-docs.md",
    "chars": 1073,
    "preview": "# Viewing Docs Locally\n\n## Quick commands\n\nFrom repo root:\n\n```bash\nuv sync --no-default-groups --extra docs\n./scripts/d"
  },
  {
    "path": "docs-mkdocs/env.html",
    "chars": 332,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=api/envs-rol"
  },
  {
    "path": "docs-mkdocs/getting-started/installation.md",
    "chars": 4365,
    "preview": "# Installation\n\n## Recommended workflow: `uv`\n\n### 1) Create and activate a virtual environment\n```bash\n# from repo root"
  },
  {
    "path": "docs-mkdocs/getting-started/quickstart-jax.md",
    "chars": 2450,
    "preview": "# Quickstart (JAX)\n\nHere are a couple of lines to quickly build an active inference agent and run state inference, polic"
  },
  {
    "path": "docs-mkdocs/guides/generative-model-structure.md",
    "chars": 4082,
    "preview": "# Thinking in Generative Models in `pymdp`\n\nThis guide explains how to map Active Inference concepts onto the list-based"
  },
  {
    "path": "docs-mkdocs/guides/pymdp-env.md",
    "chars": 1971,
    "preview": "# `PymdpEnv`: Building JAX Environments from Generative Models\n\n`pymdp` environments implement the `Env` interface:\n\n- `"
  },
  {
    "path": "docs-mkdocs/guides/rollout-active-inference-loop.md",
    "chars": 3164,
    "preview": "# Using `rollout()` for compiled active inference loops\n\n`rollout()` runs repeated inference, planning, action sampling,"
  },
  {
    "path": "docs-mkdocs/index.md",
    "chars": 1461,
    "preview": "# Welcome to pymdp's documentation!\n\n`pymdp` is a Python package for simulating Active Inference agents in\ndiscrete-stat"
  },
  {
    "path": "docs-mkdocs/inference.html",
    "chars": 320,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=api/inferenc"
  },
  {
    "path": "docs-mkdocs/installation.html",
    "chars": 380,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=getting-star"
  },
  {
    "path": "docs-mkdocs/javascripts/mathjax.js",
    "chars": 571,
    "preview": "window.MathJax = {\n  tex: {\n    inlineMath: [[\"\\\\(\", \"\\\\)\"]],\n    displayMath: [[\"\\\\[\", \"\\\\]\"]],\n    processEscapes: tru"
  },
  {
    "path": "docs-mkdocs/javascripts/sidebar-accessibility.js",
    "chars": 723,
    "preview": "document.addEventListener(\"DOMContentLoaded\", () => {\n  const sidebar = document.getElementById(\"sidebar\");\n  const side"
  },
  {
    "path": "docs-mkdocs/learning.html",
    "chars": 316,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=api/learning"
  },
  {
    "path": "docs-mkdocs/legacy/index.md",
    "chars": 618,
    "preview": "# Legacy / NumPy Archive\n\n`pymdp/legacy` is preserved for compatibility and historical context.\n\n## Status\n- Legacy modu"
  },
  {
    "path": "docs-mkdocs/migration/numpy-to-jax.md",
    "chars": 7081,
    "preview": "# NumPy/legacy to JAX Migration\n\nThis guide is for users moving from `pymdp.legacy` (NumPy/object-array style) to the [J"
  },
  {
    "path": "docs-mkdocs/notebooks/active_inference_from_scratch.html",
    "chars": 368,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=../migration"
  },
  {
    "path": "docs-mkdocs/notebooks/cue_chaining_demo.html",
    "chars": 561,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=https://gith"
  },
  {
    "path": "docs-mkdocs/notebooks/free_energy_calculation.html",
    "chars": 368,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=../migration"
  },
  {
    "path": "docs-mkdocs/notebooks/pymdp_fundamentals.html",
    "chars": 368,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=../migration"
  },
  {
    "path": "docs-mkdocs/notebooks/tmaze_demo.html",
    "chars": 476,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=../tutorials"
  },
  {
    "path": "docs-mkdocs/notebooks/using_the_agent_class.html",
    "chars": 400,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"refresh\" content=\"0; url=../getting-s"
  },
  {
    "path": "docs-mkdocs/overrides/modules/sidebar.html",
    "chars": 709,
    "preview": "<nav id=\"sidebar\" class=\"sidebar drac-bg-black\" aria-label=\"Sidebar navigation\">\n    <div class=\"custom-menu\">\n        <"
  },
  {
    "path": "docs-mkdocs/styles/crisp-api.css",
    "chars": 4975,
    "preview": "@import url(\"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;6"
  },
  {
    "path": "docs-mkdocs/tutorials/index.md",
    "chars": 372,
    "preview": "# Tutorials\n\nUse this section for runnable, example-driven materials sourced from Jupyter\nnotebooks under `examples/`.\n\n"
  },
  {
    "path": "docs-mkdocs/tutorials/notebooks/index.header.md",
    "chars": 376,
    "preview": "## For developers\n\nThe notebook listings above are auto-generated by `./scripts/sync_docs_notebooks.sh`.\n\nSource noteboo"
  },
  {
    "path": "docs-mkdocs/tutorials/notebooks/index.md",
    "chars": 2523,
    "preview": "# Notebook Gallery\n\n## Curated notebooks\n\n### Getting started\n- [Generative Model Construction (Model/Distribution APIs)"
  },
  {
    "path": "docs-mkdocs/tutorials/notebooks.manifest",
    "chars": 1138,
    "preview": "# One notebook path per line, relative to repository root.\nexamples/advanced/complex_action_dependency.ipynb\nexamples/ad"
  },
  {
    "path": "examples/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/advanced/complex_action_dependency.ipynb",
    "chars": 12131,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Complex action dependencies\\n\",\n "
  },
  {
    "path": "examples/advanced/infer_states_optimization/methods_test.ipynb",
    "chars": 48464,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"id\": \"d329903e-0caa-4ad9-8b85-92ed348f1e0f\",\n   \""
  },
  {
    "path": "examples/advanced/pymdp_with_neural_encoder.ipynb",
    "chars": 2473070,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"0\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Training a neural n"
  },
  {
    "path": "examples/api/model_construction_tutorial.ipynb",
    "chars": 24997,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Generative Model Construction wit"
  },
  {
    "path": "examples/envs/chained_cue_navigation.py",
    "chars": 11845,
    "preview": "\"\"\"Generate legacy-style chained-cue navigation GIFs with the JAX environment.\n\nThis script recreates:\n- `.github/chaine"
  },
  {
    "path": "examples/envs/cue_chaining_demo.ipynb",
    "chars": 44255,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"b803090f\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Active Infer"
  },
  {
    "path": "examples/envs/generalized_tmaze_demo.ipynb",
    "chars": 165791,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# T-Maze Task with Distractors\\n\",\n"
  },
  {
    "path": "examples/envs/graph_worlds_demo.ipynb",
    "chars": 61672,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Graph worlds\\n\",\n    \"\\n\",\n    \"T"
  },
  {
    "path": "examples/envs/knapsack_demo.ipynb",
    "chars": 12383,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Demo: Knapsack Problem\\n\",\n    \"\\"
  },
  {
    "path": "examples/envs/tmaze_demo.ipynb",
    "chars": 221599,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# T-Maze Demo\\n\",\n    \"\\n\",\n    \"In"
  },
  {
    "path": "examples/experimental/sophisticated_inference/mcts_generalized_tmaze.ipynb",
    "chars": 154285,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Sophisticated inference with Mont"
  },
  {
    "path": "examples/experimental/sophisticated_inference/mcts_graph_world.ipynb",
    "chars": 193635,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"execution\": {\n     \"iopub.execu"
  },
  {
    "path": "examples/experimental/sophisticated_inference/si_generalized_tmaze.ipynb",
    "chars": 175154,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Sophisticated inference on a Gene"
  },
  {
    "path": "examples/experimental/sophisticated_inference/si_graph_world.ipynb",
    "chars": 70957,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Sophisticated inference on Graph "
  },
  {
    "path": "examples/experimental/sophisticated_inference/si_tmaze_SIvalidation.ipynb",
    "chars": 118984,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Validating Sophisticated Inferenc"
  },
  {
    "path": "examples/inductive_inference/inductive_inference_example.ipynb",
    "chars": 4849,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Imports\"\n   ]\n  },\n  {\n   \"cell"
  },
  {
    "path": "examples/inductive_inference/inductive_inference_gridworld.ipynb",
    "chars": 13200,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Imports\"\n   ]\n  },\n  {\n   \"cell"
  },
  {
    "path": "examples/inference_and_learning/inference_methods_comparison.ipynb",
    "chars": 96130,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"execution\": {\n     \"iopub.execu"
  },
  {
    "path": "examples/learning/learning_gridworld.ipynb",
    "chars": 1992790,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Imports\"\n   ]\n  },\n  {\n   \"cell"
  },
  {
    "path": "examples/legacy/agent_demo.ipynb",
    "chars": 23558,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Active Inference Demo: Constructi"
  },
  {
    "path": "examples/legacy/free_energy_calculation.ipynb",
    "chars": 57716,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Computing the variational free en"
  },
  {
    "path": "examples/legacy/gridworld_tutorial_1.ipynb",
    "chars": 28536,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Tutorial on Active Inference with"
  },
  {
    "path": "examples/legacy/gridworld_tutorial_2.ipynb",
    "chars": 43424,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Tutorial Notebook 2. Inference an"
  },
  {
    "path": "examples/legacy/tmaze_demo.ipynb",
    "chars": 136996,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Active Inference Demo: T-Maze Env"
  },
  {
    "path": "examples/legacy/tmaze_learning_demo.ipynb",
    "chars": 204655,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"#  Demo: T-Maze Environment + Learn"
  },
  {
    "path": "examples/model_fitting/fitting_with_pybefit.ipynb",
    "chars": 542090,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"id\": \"0\",\n   \"metadata\": {},\n   \"outputs\": [\n    "
  },
  {
    "path": "examples/model_fitting/tmaze_recoverability.py",
    "chars": 12963,
    "preview": "\"\"\"Fast recoverability diagnostics for pybefit + pymdp TMaze fitting.\n\nThis module provides a lightweight alternative to"
  },
  {
    "path": "examples/sparse/sparse_benchmark.ipynb",
    "chars": 104301,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Sparse Array Benchmarking\"\n   ]\n "
  },
  {
    "path": "mkdocs.yml",
    "chars": 2410,
    "preview": "site_name: pymdp\nsite_description: JAX-first Active Inference in discrete state spaces\nsite_url: https://pymdp-rtd.readt"
  },
  {
    "path": "nbval_sanitize.cfg",
    "chars": 418,
    "preview": "# nbval sanitization configuration\n# This file contains regex patterns to sanitize notebook outputs before comparison\n# "
  },
  {
    "path": "paper/paper.bib",
    "chars": 21634,
    "preview": "@article{friston_reinforcement_2009,\n\ttitle = {Reinforcement learning or active inference?},\n\tauthor = {Friston, Karl J."
  },
  {
    "path": "paper/paper.md",
    "chars": 13601,
    "preview": "---\ntitle: 'pymdp: A Python library for active inference in discrete state spaces'\ntags:\n  - Python\n  - active inference"
  },
  {
    "path": "pymdp/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pymdp/agent.py",
    "chars": 53209,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Agent API for Active Inference with the modern JAX backend.\"\"\"\nimport "
  },
  {
    "path": "pymdp/algos.py",
    "chars": 42186,
    "preview": "\"\"\"Core variational-inference and exact-HMM algorithm implementations.\n\nThis module contains lower-level routines used b"
  },
  {
    "path": "pymdp/control.py",
    "chars": 34099,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n# pylint: disable=not-an-iterable\n\"\"\"Policy co"
  },
  {
    "path": "pymdp/distribution.py",
    "chars": 15620,
    "preview": "import numpy as np\nfrom pymdp.utils import norm_dist\nfrom typing import Any, Iterator, Optional\n\n\nclass Distribution:\n\n "
  },
  {
    "path": "pymdp/envs/__init__.py",
    "chars": 218,
    "preview": "from .tmaze import TMaze, SimplifiedTMaze\nfrom .rollout import rollout\nfrom .env import Env, PymdpEnv, make\nfrom .graph_"
  },
  {
    "path": "pymdp/envs/cue_chaining.py",
    "chars": 12114,
    "preview": "\"\"\"Cue-chaining grid world implemented as a JAX-compatible ``PymdpEnv``.\n\nThis module ports the legacy cue-chaining demo"
  },
  {
    "path": "pymdp/envs/env.py",
    "chars": 13887,
    "preview": "\"\"\"Environment interfaces and POMDP-backed environment utilities.\n\nThis module provides:\n- `Env`: an abstract JAX-compat"
  },
  {
    "path": "pymdp/envs/generalized_tmaze.py",
    "chars": 19734,
    "preview": "from .env import PymdpEnv\nfrom typing import Any\nimport numpy as np\nimport math\nimport jax.numpy as jnp\n\nimport matplotl"
  },
  {
    "path": "pymdp/envs/graph_worlds.py",
    "chars": 8836,
    "preview": "import networkx as nx\nfrom jax import numpy as jnp, random as jr, tree_util as jtu\nfrom typing import Any, Optional, Lis"
  },
  {
    "path": "pymdp/envs/grid_world.py",
    "chars": 9023,
    "preview": "from __future__ import annotations\n\nfrom typing import Iterable, Optional, Tuple\n\nimport jax.numpy as jnp\nimport numpy a"
  },
  {
    "path": "pymdp/envs/rollout.py",
    "chars": 23869,
    "preview": "\"\"\"Utilities for running active-inference loops against environment dynamics.\n\nThe two primary public entry points are:\n"
  },
  {
    "path": "pymdp/envs/tmaze.py",
    "chars": 22210,
    "preview": "import os\nimport math\nfrom typing import Optional\nimport jax.numpy as jnp\nimport matplotlib.pyplot as plt\nimport matplot"
  },
  {
    "path": "pymdp/inference.py",
    "chars": 20735,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n\"\"\"State inference and smoothing utilities for"
  },
  {
    "path": "pymdp/learning.py",
    "chars": 21370,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n\"\"\"Dirichlet-parameter learning updates for mo"
  },
  {
    "path": "pymdp/legacy/__init__.py",
    "chars": 197,
    "preview": "from . import agent\nfrom . import envs\nfrom . import utils\nfrom . import maths\nfrom . import control\nfrom . import infer"
  },
  {
    "path": "pymdp/legacy/agent.py",
    "chars": 40911,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Agent Class\n\n__author__: Conor Heins, Alexander Tschantz, Daphne Deme"
  },
  {
    "path": "pymdp/legacy/algos/__init__.py",
    "chars": 124,
    "preview": "from .fpi import run_vanilla_fpi, run_vanilla_fpi_factorized\nfrom .mmp import run_mmp, run_mmp_factorized, _run_mmp_test"
  },
  {
    "path": "pymdp/legacy/algos/fpi.py",
    "chars": 21392,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n\nimport numpy as np\nfrom pymdp.legacy.maths im"
  },
  {
    "path": "pymdp/legacy/algos/mmp.py",
    "chars": 22520,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport numpy as np\n\nfrom pymdp.legacy.utils import get_model_dimensions, "
  },
  {
    "path": "pymdp/legacy/algos/mmp_old.py",
    "chars": 14251,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n\nimport numpy as np\n\nfrom pymdp.legacy.maths i"
  },
  {
    "path": "pymdp/legacy/control.py",
    "chars": 72605,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n# pylint: disable=not-an-iterable\n\nimport iter"
  },
  {
    "path": "pymdp/legacy/default_models.py",
    "chars": 4155,
    "preview": "\nimport numpy as np\nfrom pymdp.legacy import utils, maths\n\ndef generate_epistemic_MAB_model():\n    '''\n    Create the ge"
  },
  {
    "path": "pymdp/legacy/envs/__init__.py",
    "chars": 240,
    "preview": "from .env import Env\nfrom .grid_worlds import GridWorldEnv, DGridWorldEnv\nfrom .visual_foraging import SceneConstruction"
  },
  {
    "path": "pymdp/legacy/envs/env.py",
    "chars": 2755,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Environment Base Class\n\n__author__: Conor Heins, Alexander Tschantz, "
  },
  {
    "path": "pymdp/legacy/envs/grid_worlds.py",
    "chars": 10660,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Cube world environment\n\n__author__: Conor Heins, Alexander Tschantz, "
  },
  {
    "path": "pymdp/legacy/envs/tmaze.py",
    "chars": 13517,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" T Maze Environment (Factorized)\n\n__author__: Conor Heins, Alexander T"
  },
  {
    "path": "pymdp/legacy/envs/visual_foraging.py",
    "chars": 13963,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Visual Foraging Environment\n\n__author__: Conor Heins, Alexander Tscha"
  },
  {
    "path": "pymdp/legacy/inference.py",
    "chars": 18752,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n\nimport numpy as np\n\nfrom pymdp.legacy import "
  },
  {
    "path": "pymdp/legacy/learning.py",
    "chars": 23014,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n\nimport numpy as np\nfrom pymdp.legacy import u"
  },
  {
    "path": "pymdp/legacy/maths.py",
    "chars": 19890,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n# pylint: disable=no-member\n# pylint: disable=not-an-iterable\n\n\"\"\" Functio"
  },
  {
    "path": "pymdp/legacy/utils.py",
    "chars": 24612,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Utility functions\n\n__author__: Conor Heins, Alexander Tschantz, Brenn"
  },
  {
    "path": "pymdp/likelihoods.py",
    "chars": 1780,
    "preview": "import numpyro.distributions as dist\nfrom jax import lax\nfrom numpyro import plate, sample, deterministic\nfrom numpyro.c"
  },
  {
    "path": "pymdp/maths.py",
    "chars": 35393,
    "preview": "import jax.numpy as jnp\n\nfrom functools import partial\nfrom typing import Optional, Tuple, Sequence\nfrom jax import tree"
  },
  {
    "path": "pymdp/planning/__init__.py",
    "chars": 112,
    "preview": "\"\"\"Planning modules for advanced policy-search algorithms in `pymdp`.\"\"\"\n\n__all__ = [\"si\", \"mcts\", \"visualize\"]\n"
  },
  {
    "path": "pymdp/planning/mcts.py",
    "chars": 9565,
    "preview": "from __future__ import annotations\n\nfrom functools import partial\nfrom typing import Any, Callable\nfrom jax import vmap,"
  },
  {
    "path": "pymdp/planning/si.py",
    "chars": 44127,
    "preview": "import numpy as np\nimport jax\nimport jax.numpy as jnp\nimport jax.tree_util as jtu\nimport jax.lax as lax\nimport jax.rando"
  },
  {
    "path": "pymdp/planning/visualize.py",
    "chars": 14977,
    "preview": "\n\nfrom types import NoneType\nfrom typing import Any, Callable\nimport numpy as np\nimport jax.numpy as jnp\nimport jax.tree"
  },
  {
    "path": "pymdp/utils.py",
    "chars": 36676,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Utility functions for model construction, data shaping, and sampling.\""
  },
  {
    "path": "pyproject.toml",
    "chars": 2800,
    "preview": "[build-system]\nrequires = [\"setuptools>=77\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"inferactively-py"
  },
  {
    "path": "scripts/docs_build.sh",
    "chars": 247,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n./scri"
  },
  {
    "path": "scripts/docs_serve.sh",
    "chars": 238,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n./scri"
  },
  {
    "path": "scripts/docs_sync_and_serve.sh",
    "chars": 138,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\ncd \"$ROOT_DIR\"\n./scri"
  },
  {
    "path": "scripts/notebook_precommit.py",
    "chars": 8241,
    "preview": "#!/usr/bin/env python3\n\"\"\"Tier-aware notebook sanitation hooks for pre-commit.\"\"\"\n\nfrom __future__ import annotations\n\ni"
  },
  {
    "path": "scripts/run_notebook_manifest.py",
    "chars": 2844,
    "preview": "#!/usr/bin/env python3\n\"\"\"Run nbval notebook tests from a manifest file.\"\"\"\n\nfrom __future__ import annotations\n\nimport "
  },
  {
    "path": "scripts/sync_docs_notebooks.sh",
    "chars": 5538,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nROOT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\nMANIFEST=\"$ROOT_DIR/d"
  },
  {
    "path": "setup.cfg",
    "chars": 1264,
    "preview": "[metadata]\nname = inferactively-pymdp\nversion = 1.0.0\ndescription = A Python package for solving Markov Decision Process"
  },
  {
    "path": "test/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/conftest.py",
    "chars": 1208,
    "preview": "import os\nimport warnings\n\ndef pytest_configure(config):\n    \"\"\"\n    Configure JAX memory settings for parallel test exe"
  },
  {
    "path": "test/matlab_crossval/generation/bmr_matlab_test_a.m",
    "chars": 3185,
    "preview": "%% \n\nclear all; close all; clc;\n\ncd .. % this brings you into the 'pymdp/tests/matlab_crossval/' super directory, since "
  },
  {
    "path": "test/matlab_crossval/generation/bmr_matlab_test_b.m",
    "chars": 3187,
    "preview": "%% \n\nclear all; close all; clc;\n\ncd .. % this brings you into the 'pymdp/tests/matlab_crossval/' super directory, since "
  },
  {
    "path": "test/matlab_crossval/generation/mmp_matlab_test_a.m",
    "chars": 10552,
    "preview": "%%% PSEUDO-CODE OVERVIEW OF MESSAGE PASSING SCHEME USED IN SPM_MDP_VB_X.m\nclear all; close all; clc;\n\ncd .. % this bring"
  },
  {
    "path": "test/matlab_crossval/generation/mmp_matlab_test_b.m",
    "chars": 10566,
    "preview": "%%% PSEUDO-CODE OVERVIEW OF MESSAGE PASSING SCHEME USED IN SPM_MDP_VB_X.m\nclear all; close all; clc;\n\ncd .. % this bring"
  },
  {
    "path": "test/matlab_crossval/generation/mmp_matlab_test_c.m",
    "chars": 10567,
    "preview": "%%% PSEUDO-CODE OVERVIEW OF MESSAGE PASSING SCHEME USED IN SPM_MDP_VB_X.m\nclear all; close all; clc;\n\ncd .. % this bring"
  },
  {
    "path": "test/matlab_crossval/generation/mmp_matlab_test_d.m",
    "chars": 10765,
    "preview": "%%% PSEUDO-CODE OVERVIEW OF MESSAGE PASSING SCHEME USED IN SPM_MDP_VB_X.m\nclear all; close all; clc;\n\ncd .. % this bring"
  },
  {
    "path": "test/matlab_crossval/generation/run_mmp.m",
    "chars": 5732,
    "preview": "function [F, G, x, xq, vn, xn] = run_mmp(num_iter, window_len, policy_matrix, t, xq, x, L, D, b, b_t, xn, vn)\n%run_mmp F"
  },
  {
    "path": "test/matlab_crossval/generation/vb_x_matlab_test_1a.m",
    "chars": 20255,
    "preview": "%%% PSEUDO-CODE VERSION OF SPM_MDP_VB_X.m\n\nclear all; close all; cd .. % this brings you into the 'pymdp/tests/matlab_cr"
  },
  {
    "path": "test/matlab_crossval/generation/vb_x_matlab_test_1b.m",
    "chars": 20300,
    "preview": "%%% PSEUDO-CODE VERSION OF SPM_MDP_VB_X.m\n\nclear all; close all; cd .. % this brings you into the 'pymdp/tests/matlab_cr"
  },
  {
    "path": "test/notebooks/README.md",
    "chars": 3222,
    "preview": "# Notebook Test Manifests\n\nThis directory contains the notebook manifests used for `pymdp` release gating.\nThey are the "
  },
  {
    "path": "test/notebooks/ci_notebooks.txt",
    "chars": 897,
    "preview": "examples/advanced/complex_action_dependency.ipynb\nexamples/advanced/infer_states_optimization/methods_test.ipynb\nexample"
  },
  {
    "path": "test/notebooks/nightly_notebooks.txt",
    "chars": 182,
    "preview": "examples/advanced/pymdp_with_neural_encoder.ipynb\nexamples/learning/learning_gridworld.ipynb\nexamples/model_fitting/fitt"
  },
  {
    "path": "test/test_SPM_validation.py",
    "chars": 4556,
    "preview": "import os\nimport unittest\n\nimport numpy as np\nfrom scipy.io import loadmat\n\nfrom pymdp.legacy.agent import Agent\nfrom py"
  },
  {
    "path": "test/test_agent.py",
    "chars": 33389,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Agent Class\n\n__author__: Conor Heins, Alexander Tschantz, Daphne Deme"
  },
  {
    "path": "test/test_agent_jax.py",
    "chars": 37945,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Dimitrije Markovic, Conor Heins\n\"\"\"\n\nimport un"
  },
  {
    "path": "test/test_categorical_observations.py",
    "chars": 20495,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport unittest\nimport warnings\nfrom jax import numpy as jnp, random as j"
  },
  {
    "path": "test/test_control.py",
    "chars": 64145,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Conor Heins, Alexander Tschantz, Daphne Demeka"
  },
  {
    "path": "test/test_control_jax.py",
    "chars": 12082,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Dimitrije Markovic, Conor Heins\n\"\"\"\n\nimport un"
  },
  {
    "path": "test/test_cue_chaining_env.py",
    "chars": 4066,
    "preview": "import unittest\n\nimport jax.numpy as jnp\nimport jax.random as jr\n\nfrom pymdp.agent import Agent\nfrom pymdp.envs.cue_chai"
  },
  {
    "path": "test/test_demos.py",
    "chars": 18657,
    "preview": "import unittest\nimport numpy as np\nimport copy\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\nfrom pymdp.legacy."
  },
  {
    "path": "test/test_distribution.py",
    "chars": 5089,
    "preview": "import unittest\nfrom pymdp import distribution\nimport numpy as np\nclass TestDists(unittest.TestCase):\n\n    def test_dist"
  },
  {
    "path": "test/test_env.py",
    "chars": 11902,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Unit tests for the PymdpEnv.\"\"\"\n\nimport unittest\n\nimport jax.numpy as "
  },
  {
    "path": "test/test_fpi.py",
    "chars": 8518,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests for factorized version of variational fixed point iteratio"
  },
  {
    "path": "test/test_grid_world_parity.py",
    "chars": 6706,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Unit tests comparing GridWorld implementations.\"\"\"\n\nimport unittest\n\ni"
  },
  {
    "path": "test/test_hmm_associative_scan.py",
    "chars": 32122,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Tests for associative-scan HMM filtering/smoothing.\"\"\"\n\nimport unittes"
  },
  {
    "path": "test/test_inductive_inference_jax.py",
    "chars": 9654,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Unit tests for inductive inference.\"\"\"\n\nimport unittest\n\nimport numpy "
  },
  {
    "path": "test/test_infer_states_optimized.py",
    "chars": 15469,
    "preview": "\"\"\"\nUnit tests to compare outputs of different state inference methods.\n\nThis module tests that the following methods pr"
  },
  {
    "path": "test/test_inference.py",
    "chars": 9129,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Conor Heins, Alexander Tschantz, Daphne Demeka"
  },
  {
    "path": "test/test_inference_jax.py",
    "chars": 9651,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Dimitrije Markovic, Conor Heins\n\"\"\"\n\nimport un"
  },
  {
    "path": "test/test_jax_sparse_backend.py",
    "chars": 9248,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Conor Heins, Toon Van de Maele, Ozan Catal\n\"\"\""
  },
  {
    "path": "test/test_learning.py",
    "chars": 37658,
    "preview": "import unittest\n\nimport numpy as np\nfrom pymdp.legacy import utils, maths, learning\n\nfrom copy import deepcopy\n\nclass Te"
  },
  {
    "path": "test/test_learning_jax.py",
    "chars": 19852,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Dimitrije Markovic, Conor Heins\n\"\"\"\n\nimport un"
  },
  {
    "path": "test/test_message_passing_jax.py",
    "chars": 34447,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Tests\n__author__: Dimitrije Markovic, Conor Heins\n\"\"\"\n\nimport un"
  },
  {
    "path": "test/test_mmp.py",
    "chars": 9199,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Unit Test of `run_mmp` function under various parameterisations\n\n__da"
  },
  {
    "path": "test/test_param_info_gain_jax.py",
    "chars": 4359,
    "preview": "import pytest\nimport jax.numpy as jnp\nimport numpy as np\nfrom jax import grad\nfrom jax.scipy.special import digamma\n\nfro"
  },
  {
    "path": "test/test_pybefit_model_fitting.py",
    "chars": 3601,
    "preview": "\"\"\"Smoke tests for pybefit-based model fitting integration.\"\"\"\n\nimport pytest\nfrom jax import nn, random as jr\nfrom jax "
  },
  {
    "path": "test/test_rollout_function.py",
    "chars": 36114,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Unit tests for the jittable rollout loop.\"\"\"\n\nimport unittest\nimport w"
  },
  {
    "path": "test/test_sophisticated_inference_jax.py",
    "chars": 9396,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Unit tests for sophisticated inference planning (JAX).\"\"\"\n\nimport unit"
  },
  {
    "path": "test/test_tmaze_envs.py",
    "chars": 6484,
    "preview": "import unittest\n\nimport jax.numpy as jnp\n\nfrom pymdp.envs.tmaze import TMaze, SimplifiedTMaze\n\n\nclass TestTMazeVariants("
  },
  {
    "path": "test/test_tmaze_recoverability.py",
    "chars": 1365,
    "preview": "\"\"\"Smoke tests for the fast TMaze recoverability diagnostics.\"\"\"\n\nimport math\n\nfrom examples.model_fitting.tmaze_recover"
  },
  {
    "path": "test/test_utils.py",
    "chars": 712,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Agent Class\n\n__author__: Conor Heins, Alexander Tschantz, Daphne Deme"
  },
  {
    "path": "test/test_utils_jax.py",
    "chars": 10402,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\" Agent Class\n\n__author__: Conor Heins, Alexander Tschantz, Daphne Deme"
  },
  {
    "path": "test/test_vfe_jax.py",
    "chars": 33416,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport unittest\nfrom itertools import product\n\nimport numpy as np\nfrom ja"
  }
]

// ... and 20 more files (download for full content)

About this extraction

This page contains the full source code of the infer-actively/pymdp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 220 files (9.3 MB), approximately 2.4M tokens, and a symbol index with 900 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!