diff --git a/.github/workflows/update_wiki.yml b/.github/workflows/update_wiki.yml index c7ad2b3..ae23edd 100644 --- a/.github/workflows/update_wiki.yml +++ b/.github/workflows/update_wiki.yml @@ -3,11 +3,8 @@ name: Deploy Wiki on: workflow_dispatch: push: - # Trigger only when wiki directory changes paths: - - 'wiki/pages/**' - - # Trigger only on main + - 'wiki/**' branches: [ main ] jobs: @@ -16,35 +13,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install tools to build whl package - run: | - pip install sdist - - - name: Build whl package - run: | - python setup.py sdist bdist_wheel - env: - SEMANTIC_VERSION: 1.0.0 # not actually 1.0.0, but has no impact on wiki-building - - - name: Install do-calculus package - run: | - pip install -e . - env: - SEMANTIC_VERSION: 1.0.0 # not actually 1.0.0, but has no impact on wiki-building - - - name: Build Pages (replace stubs) - run: | - python ./wiki/build_wiki.py - - name: Push Wiki Changes - uses: Andrew-Chen-Wang/github-wiki-action@v2 + uses: Andrew-Chen-Wang/github-wiki-action@v3 env: - WIKI_DIR: wiki/pages/ + WIKI_DIR: wiki/ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_MAIL: ${{ secrets.EMAIL }} GH_NAME: ${{ github.repository_owner }} diff --git a/README.md b/README.md index 18fd54f..c45da03 100755 --- a/README.md +++ b/README.md @@ -28,3 +28,12 @@ * **Contact**: [braden.dubois@usask.ca](mailto:braden.dubois@usask.ca) See the [wiki](https://github.com/bradendubois/do-calculus/wiki) to get started. + + +## Development Status + +A full overhaul has been completed, and marks important milestones in my life and (consequently) this project's. Development on this project is halted until further notice, barring further changes to my own life, or necessary bug fixes and/or security fixes. + +## Acknowledgements + +This project represents approximately two years of part- and full-time work as part of an indescribably fulfilling undergraduate research project. This project was done under the supervision of Dr. Eric Neufeld from approximately Spring 2020 - Winter 2022. Without Dr. Neufeld's support, guidance, patience, expertise (and of course, funding), this project would never have been started, let alone completed. I cannot overstate my appreciation - you've changed my academic and professional path, and provided me with so many wonderful experiences and memories that I will never forget. Thanks, Eric. diff --git a/wiki/pages/Ancestors.md b/archive/Ancestors.md similarity index 100% rename from wiki/pages/Ancestors.md rename to archive/Ancestors.md diff --git a/wiki/pages/Backdoor Paths.md b/archive/Backdoor Paths.md similarity index 100% rename from wiki/pages/Backdoor Paths.md rename to archive/Backdoor Paths.md diff --git a/wiki/pages/Children.md b/archive/Children.md similarity index 100% rename from wiki/pages/Children.md rename to archive/Children.md diff --git a/wiki/pages/Conditional Independence.md b/archive/Conditional Independence.md similarity index 100% rename from wiki/pages/Conditional Independence.md rename to archive/Conditional Independence.md diff --git a/wiki/pages/Configuration.md b/archive/Configuration.md similarity index 100% rename from wiki/pages/Configuration.md rename to archive/Configuration.md diff --git a/wiki/pages/Deconfounding Sets.md b/archive/Deconfounding Sets.md similarity index 100% rename from wiki/pages/Deconfounding Sets.md rename to archive/Deconfounding Sets.md diff --git a/wiki/pages/Definitions.md b/archive/Definitions.md similarity index 100% rename from wiki/pages/Definitions.md rename to archive/Definitions.md diff --git a/wiki/pages/Descendants.md b/archive/Descendants.md similarity index 100% rename from wiki/pages/Descendants.md rename to archive/Descendants.md diff --git a/wiki/pages/Do API.md b/archive/Do API.md similarity index 100% rename from wiki/pages/Do API.md rename to archive/Do API.md diff --git a/wiki/pages/Exceptions.md b/archive/Exceptions.md similarity index 100% rename from wiki/pages/Exceptions.md rename to archive/Exceptions.md diff --git a/wiki/pages/GitHub.md b/archive/GitHub.md similarity index 100% rename from wiki/pages/GitHub.md rename to archive/GitHub.md diff --git a/wiki/pages/Home.md b/archive/Home.md similarity index 100% rename from wiki/pages/Home.md rename to archive/Home.md diff --git a/wiki/pages/Installation.md b/archive/Installation.md similarity index 100% rename from wiki/pages/Installation.md rename to archive/Installation.md diff --git a/wiki/pages/Joint Distribution Table.md b/archive/Joint Distribution Table.md similarity index 100% rename from wiki/pages/Joint Distribution Table.md rename to archive/Joint Distribution Table.md diff --git a/wiki/pages/Literature.md b/archive/Literature.md similarity index 100% rename from wiki/pages/Literature.md rename to archive/Literature.md diff --git a/wiki/pages/Loading a Model.md b/archive/Loading a Model.md similarity index 100% rename from wiki/pages/Loading a Model.md rename to archive/Loading a Model.md diff --git a/wiki/pages/Markovian Models.md b/archive/Markovian Models.md similarity index 100% rename from wiki/pages/Markovian Models.md rename to archive/Markovian Models.md diff --git a/wiki/pages/Output.md b/archive/Output.md similarity index 100% rename from wiki/pages/Output.md rename to archive/Output.md diff --git a/wiki/pages/Parents.md b/archive/Parents.md similarity index 100% rename from wiki/pages/Parents.md rename to archive/Parents.md diff --git a/wiki/pages/Probability Queries.md b/archive/Probability Queries.md similarity index 100% rename from wiki/pages/Probability Queries.md rename to archive/Probability Queries.md diff --git a/wiki/pages/PyPI.md b/archive/PyPI.md similarity index 100% rename from wiki/pages/PyPI.md rename to archive/PyPI.md diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 0000000..4eea6b1 --- /dev/null +++ b/archive/README.md @@ -0,0 +1,5 @@ +# Archive + +Contained here is some old code that built a (now-outdated) wiki that I don't have the heart to delete. + +Don't rely on it expecting accurate information. diff --git a/wiki/pages/Resources.md b/archive/Resources.md similarity index 100% rename from wiki/pages/Resources.md rename to archive/Resources.md diff --git a/wiki/pages/Roots.md b/archive/Roots.md similarity index 100% rename from wiki/pages/Roots.md rename to archive/Roots.md diff --git a/wiki/pages/Sinks.md b/archive/Sinks.md similarity index 100% rename from wiki/pages/Sinks.md rename to archive/Sinks.md diff --git a/wiki/pages/Standard Paths.md b/archive/Standard Paths.md similarity index 100% rename from wiki/pages/Standard Paths.md rename to archive/Standard Paths.md diff --git a/wiki/pages/Topology.md b/archive/Topology.md similarity index 100% rename from wiki/pages/Topology.md rename to archive/Topology.md diff --git a/wiki/pages/_Sidebar.md b/archive/_Sidebar.md similarity index 100% rename from wiki/pages/_Sidebar.md rename to archive/_Sidebar.md diff --git a/wiki/pages/__init__.md b/archive/__init__.md similarity index 100% rename from wiki/pages/__init__.md rename to archive/__init__.md diff --git a/wiki/__init__.py b/archive/__init__.py similarity index 100% rename from wiki/__init__.py rename to archive/__init__.py diff --git a/wiki/build_wiki.py b/archive/build_wiki.py similarity index 100% rename from wiki/build_wiki.py rename to archive/build_wiki.py diff --git a/do/identification/API.py b/do/identification/API.py index 07c57fc..6ee162d 100644 --- a/do/identification/API.py +++ b/do/identification/API.py @@ -5,7 +5,7 @@ from ..core.Variables import Intervention, Outcome from .LatentGraph import latent_transform -from .Identification import Identification +from .Identification import Identification, simplify_expression from .PExpression import PExpression, TemplateExpression @@ -62,7 +62,6 @@ def _process(current: Union[PExpression, TemplateExpression], known: Mapping[str return t result = _process(expression, {v.name: v.outcome for v in y} | {v.name: v.outcome for v in x}) - return (result, expression.proof()) if include_proof else result def proof(self, y: Set[Outcome], x: Set[Intervention], model: Model) -> str: diff --git a/do/identification/LatentGraph.py b/do/identification/LatentGraph.py index c393680..d36c5b8 100644 --- a/do/identification/LatentGraph.py +++ b/do/identification/LatentGraph.py @@ -179,10 +179,10 @@ def latent_transform(g: Graph, u: Set[str]): reduction = False remove = set() - for u in Un: + for un in Un: - parents = [edge[0] for edge in E if edge[1] == u] # Edges : parent -> u - children = [edge[1] for edge in E if edge[0] == u] # Edges : u -> child + parents = [edge[0] for edge in E if edge[1] == un] # Edges : parent -> u + children = [edge[1] for edge in E if edge[0] == un] # Edges : u -> child # All parents are unobservable, all children are observable, at least one parent if all(x in u for x in parents) and len(parents) > 0 and all(x not in u for x in children): @@ -190,18 +190,18 @@ def latent_transform(g: Graph, u: Set[str]): # Remove edges from parents to u for parent in parents: - E.remove((parent, u)) + E.remove((parent, un)) # Remove edges from u to children for child in children: - E.remove((u, child)) + E.remove((un, child)) # Replace with edge from edge parent to each child for cr in product(parents, children): E.add((cr[0], cr[1])) # U can be removed entirely from graph - remove.add(u) + remove.add(un) V -= remove Un -= remove @@ -229,4 +229,5 @@ def latent_transform(g: Graph, u: Set[str]): a, b = child_edges[i], child_edges[(i + 1) % len(child_edges)] E_Bidirected.add((a[1], b[1])) + print(V, u) return LatentGraph(V, E, E_Bidirected, [x for x in g.topology_sort() if x in V - u]) diff --git a/examples/2-backdoor-paths/deconfound.py b/examples/2-backdoor-paths/deconfound.py new file mode 100644 index 0000000..3d010b7 --- /dev/null +++ b/examples/2-backdoor-paths/deconfound.py @@ -0,0 +1,19 @@ +from pathlib import Path + +from do import API + +api = API() + +file = Path("pearl-3.4.yml") +model = api.instantiate_model(file) + +backdoors = api.backdoors({"Xi"}, {"Xj"}, model.graph()) +assert len(backdoors) > 0, "No backdoor paths detected!" +assert not api.blocks({"Xi"}, {"Xj"}, model.graph(), set()), "This should not block all paths!" + +for path in backdoors: + print("Path:", path) + +backdoors = api.backdoors({"Xi"}, {"Xj"}, model.graph(), {"X1", "X4"}) +assert len(backdoors) == 0, "Expected this to block!" +assert api.blocks({"Xi"}, {"Xj"}, model.graph(), {"X1", "X4"}), "This should block!" diff --git a/examples/2-backdoor-paths/pearl-3.4.yml b/examples/2-backdoor-paths/pearl-3.4.yml new file mode 100644 index 0000000..403319c --- /dev/null +++ b/examples/2-backdoor-paths/pearl-3.4.yml @@ -0,0 +1,116 @@ +name: 'Pearl: Figure 3.4' +endogenous: + X1: + outcomes: + - x1 + - ~x1 + parents: [] + table: [ + [x1, 0.4], + [~x1, 0.6] + ] + X2: + outcomes: + - x2 + - ~x2 + parents: [] + table: [ + [x2, 0.15], + [~x2, 0.85] + ] + X3: + outcomes: + - x3 + - ~x3 + parents: + - X1 + table: [ + [x3, x1, 0.1], + [x3, ~x1, 0.3], + [~x3, x1, 0.9], + [~x3, ~x1, 0.7] + ] + X4: + outcomes: + - x4 + - ~x4 + parents: + - X1 + - X2 + table: [ + [x4, x1, x2, 0.7], + [x4, x1, ~x2, 0.9], + [x4, ~x1, x2, 0.55], + [x4, ~x1, ~x2, 0.15], + [~x4, x1, x2, 0.3], + [~x4, x1, ~x2, 0.1], + [~x4, ~x1, x2, 0.45], + [~x4, ~x1, ~x2, 0.85] + ] + X5: + outcomes: + - x5 + - ~x5 + parents: + - X2 + table: [ + [x5, x2, 0.8], + [x5, ~x2, 0.25], + [~x5, x2, 0.2], + [~x5, ~x2, 0.75] + ] + X6: + outcomes: + - x6 + - ~x6 + parents: + - Xi + table: [ + [x6, xi, 0.9], + [x6, ~xi, 0.25], + [~x6, xi, 0.1], + [~x6, ~xi, 0.75] + ] + Xi: + outcomes: + - xi + - ~xi + parents: + - X3 + - X4 + table: [ + [xi, x3, x4, 0.5], + [xi, x3, ~x4, 0.65], + [xi, ~x3, x4, 0.1], + [xi, ~x3, ~x4, 0.25], + [~xi, x3, x4, 0.5], + [~xi, x3, ~x4, 0.35], + [~xi, ~x3, x4, 0.9], + [~xi, ~x3, ~x4, 0.75] + ] + Xj: + outcomes: + - xj + - ~xj + parents: + - X6 + - X4 + - X5 + table: [ + [xj, x6, x4, x5, 0.0], + [xj, x6, x4, ~x5, 0.25], + [xj, x6, ~x4, x5, 0.7], + [xj, x6, ~x4, ~x5, 0.45], + [xj, ~x6, x4, x5, 0.15], + [xj, ~x6, x4, ~x5, 0.8], + [xj, ~x6, ~x4, x5, 0.95], + [xj, ~x6, ~x4, ~x5, 0.05], + [~xj, x6, x4, x5, 1.0], + [~xj, x6, x4, ~x5, 0.75], + [~xj, x6, ~x4, x5, 0.3], + [~xj, x6, ~x4, ~x5, 0.55], + [~xj, ~x6, x4, x5, 0.85], + [~xj, ~x6, x4, ~x5, 0.2], + [~xj, ~x6, ~x4, x5, 0.05], + [~xj, ~x6, ~x4, ~x5, 0.95] + ] diff --git a/examples/3-latent/3.4-latent.yml b/examples/3-latent/3.4-latent.yml new file mode 100644 index 0000000..4b8d6c4 --- /dev/null +++ b/examples/3-latent/3.4-latent.yml @@ -0,0 +1,90 @@ +name: 'Pearl: Figure 3.4' +endogenous: + X1: + outcomes: + - x1 + - ~x1 + parents: [] + table: [ + [x1, 0.4], + [~x1, 0.6] + ] + X2: + outcomes: + - x2 + - ~x2 + parents: [] + table: [ + [x2, 0.15], + [~x2, 0.85] + ] + X3: + outcomes: + - x3 + - ~x3 + table: [ + [x3, 0.1], + [~x3, 0.9], + ] + X4: + outcomes: + - x4 + - ~x4 + parents: + - Xi + table: [ + [x4, xi, 0.9], + [x4, ~xi, 0.25], + [~x4, xi, 0.1], + [~x4, ~xi, 0.75] + ] + Xi: + outcomes: + - xi + - ~xi + parents: + - X1 + - X2 + table: [ + [xi, x1, x2, 0.5], + [xi, x1, ~x2, 0.65], + [xi, ~x1, x2, 0.1], + [xi, ~x1, ~x2, 0.25], + [~xi, x1, x2, 0.5], + [~xi, x1, ~x2, 0.35], + [~xi, ~x1, x2, 0.9], + [~xi, ~x1, ~x2, 0.75] + ] + Xj: + outcomes: + - xj + - ~xj + parents: + - X2 + - X3 + - X4 + table: [ + [xj, x2, x3, x4, 0.0], + [xj, x2, x3, ~x4, 0.25], + [xj, x2, ~x3, x4, 0.7], + [xj, x2, ~x3, ~x4, 0.45], + [xj, ~x2, x3, x4, 0.15], + [xj, ~x2, x3, ~x4, 0.8], + [xj, ~x2, ~x3, x4, 0.95], + [xj, ~x2, ~x3, ~x4, 0.05], + [~xj, x2, x3, x4, 1.0], + [~xj, x2, x3, ~x4, 0.75], + [~xj, x2, ~x3, x4, 0.3], + [~xj, x2, ~x3, ~x4, 0.55], + [~xj, ~x2, x3, x4, 0.85], + [~xj, ~x2, x3, ~x4, 0.2], + [~xj, ~x2, ~x3, x4, 0.05], + [~xj, ~x2, ~x3, ~x4, 0.95] + ] +exogenous: + U1: + - X1 + - X2 + U2: + - X2 + - X3 diff --git a/examples/3-latent/deconfound.py b/examples/3-latent/deconfound.py new file mode 100644 index 0000000..2e58feb --- /dev/null +++ b/examples/3-latent/deconfound.py @@ -0,0 +1,16 @@ +from pathlib import Path + +from do import API, Expression, Intervention, Outcome + +api = API() + +file = Path("3.4-latent.yml") +model = api.instantiate_model(file) + +xj = Outcome("Xj", "xj") +xi = Intervention("Xi", "xi") +e = Expression(xj, [xi]) + +result, proof = api.identification([xj], [xi], model) +print(result) +print(proof) diff --git a/tests/identification/test_PExpression.py b/tests/identification/test_PExpression.py deleted file mode 100644 index eb1ae45..0000000 --- a/tests/identification/test_PExpression.py +++ /dev/null @@ -1 +0,0 @@ -... diff --git a/wiki/API.md b/wiki/API.md new file mode 100644 index 0000000..9987a2a --- /dev/null +++ b/wiki/API.md @@ -0,0 +1,28 @@ +# API + +Details on the [API](https://en.wikipedia.org/wiki/API) provided in the project. + +This assumes the steps in the [[Installation]] section have been followed, and the project is set up. + +**Note**: For simplicity of import-statements, any examples will *assume* the project was installed as [PyPI](https://pypi.org/project/do-calculus/) package. + +## Importing + +To import the package: + +```python +import do +``` + +**Important**: +- The package name on [PyPI](https://pypi.org/) is [do-calculus](https://pypi.org/project/do-calculus/), but the module to import is called ``do``. + +
+ +To create an instance of the API: + +```python +from do import API + +api = API() +``` diff --git a/wiki/GitHub.md b/wiki/GitHub.md new file mode 100644 index 0000000..7d78b16 --- /dev/null +++ b/wiki/GitHub.md @@ -0,0 +1,54 @@ +Instructions for installing the project from the [source code](https://github.com/bradendubois/do-calculus/wiki). + +## Acquiring a Copy + +To acquire a copy of the source code, one can [**clone the repository**](#clone), [**download a release**](#release), or use the [**GitHub CLI**](#cli). + +After a copy has been acquired, [install the extra dependencies](#extra-dependencies). + +## Clone + +In order to clone the repository, you must have [git](https://git-scm.com/) installed; if you are on [macOS](https://www.apple.com/ca/macos/) or [Linux](https://www.linux.org/), you almost certainly already have this installed. + +You can clone the repository using either the **HTTPS** or **SSH** URL. If you do not know which to choose, or do not intend to commit to the project, use **HTTPS**. + +To clone with the **HTTPS** URL: + +```shell +git clone https://github.com/bradendubois/do-calculus.git +``` + +To clone with the **SSH** URL: +```shell +git clone git@github.com:bradendubois/do-calculus.git +``` + +## Release + +The project's [releases page](https://github.com/bradendubois/do-calculus/releases) shows all tagged version of the project, according to [semantic versioning](https://semver.org/). Both **.zip** and **.tar.gz** archives are available. + +**Releases**: [https://github.com/bradendubois/do-calculus/releases](https://github.com/bradendubois/do-calculus/releases) + +Releases are automatically created, tagged, and versioned using [semantic-release](https://github.com/semantic-release/semantic-release). + +## CLI + +To clone with the [GitHub CLI](https://cli.github.com/). + +```shell +gh repo clone bradendubois/do-calculus +``` + +## Extra Dependencies + +After acquiring a copy from any of the above steps: + +```shell +pip install -r requirements.txt +``` + +The above command will install all dependencies listed in ``requirements.txt``. + +## Further + +An [API](https://en.wikipedia.org/wiki/API) is available and [[details can be found here|Do API]]. diff --git a/wiki/Home.md b/wiki/Home.md new file mode 100644 index 0000000..f22df46 --- /dev/null +++ b/wiki/Home.md @@ -0,0 +1,7 @@ +# do-calculus wiki + +This is a (fairly minimal) wiki detailing usage of the `do-calculus` library. + +There are some examples in the `examples` subdirectory, and the API is fairly well-documented, to be accompanied by prerequisite domain-specific knowledge. + +See the sidebar for relevant links. diff --git a/wiki/Installation.md b/wiki/Installation.md new file mode 100644 index 0000000..41a8e1f --- /dev/null +++ b/wiki/Installation.md @@ -0,0 +1,22 @@ +# Installation + +How to install and set up the software. + +## Table of Contents + +* [Requirements](#requirements) +* [Options](#options) + +## Requirements + +Setup requirements for the project are: +- **[Python 3.8+](https://www.python.org/)** +- [**pip**](https://pip.pypa.io/en/stable/) is used to install required packages. + +**Note**: `pip` will already be installed with any installation of **Python 3.4+**. + +## Options + +There are **two** main ways to install the package: +- [[Install from PyPI|PyPI]] +- [[Install from source|GitHub]] diff --git a/wiki/PyPI.md b/wiki/PyPI.md new file mode 100644 index 0000000..862eeaa --- /dev/null +++ b/wiki/PyPI.md @@ -0,0 +1,29 @@ +Instructions for installing the package through its [PyPI distribution](https://pypi.org/project/do-calculus/). + +## PyPI Package + +The package is published on [PyPI](https://pypi.org/) as [do-calculus](https://pypi.org/project/do-calculus/). + +To install from [PyPI](https://pypi.org/) as a package: + +```shell +pip install do-calculus +``` + +## Upgrade + +To upgrade a local installation of the project (such as when a new version is released), add the ``-U`` flag: + +```shell +pip install -U do-calculus +``` + +## PyPI Release Cycle + +By default, a new package will be automatically uploaded to PyPI on a new [semantically-versioned](https://semver.org/) [release](https://github.com/bradendubois/do-calculus/releases) which is automatically handled by [semantic-release](https://github.com/semantic-release/semantic-release) in a [workflow](https://github.com/bradendubois/do-calculus/actions). + +Releases are generated by [semantic-release](https://github.com/semantic-release/semantic-release) on pushes or merges to the [main](https://github.com/bradendubois/do-calculus/tree/main) and [beta](https://github.com/bradendubois/do-calculus/tree/beta) branches of the project. + +*Only* releases produced from [main](https://github.com/bradendubois/do-calculus/tree/main) will be uploaded to the [PyPI](https://pypi.org/project/do-calculus/) distribution. All development on the project will eventually work its way up to the [PyPI](https://pypi.org/project/do-calculus/) distribution, though it may lag behind [GitHub releases](https://github.com/bradendubois/do-calculus/releases) by anywhere between minutes to a few days. + +See the [[API]] page for importing and using the package once installed. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md new file mode 100644 index 0000000..0fb3f42 --- /dev/null +++ b/wiki/_Sidebar.md @@ -0,0 +1,7 @@ +### [[Home]] + +### [[Installation]] +* [[PyPI]] +* [[GitHub]] + +### [[API]]