From 57f9842a45d9252945b9de20904762826ae99104 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 14:23:19 -0700 Subject: [PATCH 01/15] fix: replace pkg_resources with importlib.metadata for Python 3.12+ compat --- pyreason/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pyreason/__init__.py b/pyreason/__init__.py index d0611ae1..5c95ebf9 100755 --- a/pyreason/__init__.py +++ b/pyreason/__init__.py @@ -9,13 +9,11 @@ from pyreason.pyreason import * import yaml -from importlib.metadata import version -from pkg_resources import get_distribution, DistributionNotFound +from importlib.metadata import version, PackageNotFoundError try: - __version__ = get_distribution(__name__).version -except DistributionNotFound: - # package is not installed + __version__ = version(__name__) +except PackageNotFoundError: pass From e05a11c757574fa9e2bd38f84cd62bcbdd9c479d Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 14:43:34 -0700 Subject: [PATCH 02/15] fix: migrate setuptools-scm from setup_requires to pyproject.toml build system --- pyproject.toml | 6 ++++-- setup.py | 16 ++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bf5379f8..5ab49ecf 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,8 @@ [build-system] -requires = ['setuptools>=42'] -build-backend = 'setuptools.build_meta' +requires = ["setuptools>=80", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] [tool.ruff.lint] # Ignore ambiguous variable name errors (E741) in interpretation files diff --git a/setup.py b/setup.py index 0269d25f..f2493198 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ from setuptools import setup, find_packages -# Read the contents of README file from pathlib import Path this_directory = Path(__file__).parent @@ -8,7 +7,6 @@ setup( name='pyreason', - version='3.5.1', author='Dyuman Aditya', author_email='dyuman.aditya@gmail.com', description='An explainable inference software supporting annotated, real valued, graph based and temporal logic', @@ -25,18 +23,16 @@ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent" ], - python_requires='>3.6', + python_requires='>=3.10', install_requires=[ - 'networkx', - 'pyyaml', - 'pandas', - 'numba', - 'numpy', + 'networkx>=3.1', + 'pyyaml>=6.0', + 'pandas>=2.0.0', + 'numba>=0.65.1', + 'numpy>=2.1,<2.5', 'memory_profiler', 'pytest' ], - use_scm_version=True, - setup_requires=['setuptools_scm'], packages=find_packages(), include_package_data=True ) From 2961a5147bba545878f77fca454f7723a4755885 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 14:58:27 -0700 Subject: [PATCH 03/15] fix: update numba, numpy, torch, pandas, networkx version constraints for Python 3.12-3.14 --- requirements.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index aabec6b4..25046fa7 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,11 @@ -networkx -pyyaml -pandas -numba==0.59.1 -numpy==1.26.4 +networkx>=3.1 +pyyaml>=6.0 +pandas>=2.0.0 +numba>=0.65.1 +numpy>=2.1,<2.5 memory_profiler pytest -torch -setuptools_scm +torch>=2.6.0 pytest-cov pre-commit From 311c8b6890212f6cc70ce1901d2954e1d3badbaf Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 15:16:52 -0700 Subject: [PATCH 04/15] ci: expand Python matrix to 3.10-3.14, upgrade Actions to v4 --- .github/workflows/python-package-version-test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-package-version-test.yml b/.github/workflows/python-package-version-test.yml index fb4a0da0..139aee13 100644 --- a/.github/workflows/python-package-version-test.yml +++ b/.github/workflows/python-package-version-test.yml @@ -16,19 +16,19 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install ruff pytest - pip install torch==2.6.0 + pip install "torch>=2.6.0" if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with ruff run: | From 51729a2f3a9025685689d30a61b50f8a5040ff32 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 15:17:39 -0700 Subject: [PATCH 05/15] ci: update ReadTheDocs to Python 3.12 and ubuntu-24.04 --- .readthedocs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 296c7943..659aba0a 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,9 +3,9 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.9" + python: "3.12" sphinx: configuration: docs/source/conf.py From 05b6cf26f5caf0edaf5345643940ae8a9025513a Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 15:17:47 -0700 Subject: [PATCH 06/15] test: remove blanket UserWarning suppression from pytest.ini --- pytest.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest.ini b/pytest.ini index ae7474ce..a026c659 100644 --- a/pytest.ini +++ b/pytest.ini @@ -33,8 +33,6 @@ markers = # Warnings configuration filterwarnings = - # Ignore specific warnings that are expected in test environment - ignore::UserWarning ignore::DeprecationWarning:numba.* ignore::FutureWarning:numba.* ignore::RuntimeWarning:numba.* From 862be9580a56bc047d47ea3f37e2fd8bcff08acf Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 15:45:03 -0700 Subject: [PATCH 07/15] chore: add Python 3.10-3.14 classifiers to setup.py Co-Authored-By: Claude Sonnet 4.6 --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index f2493198..ccf4b65f 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,11 @@ }, classifiers=[ "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent" ], From 6d3ff5ee672ffca56a5fa441fbdea1bc898f0344 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Wed, 13 May 2026 15:57:26 -0700 Subject: [PATCH 08/15] fix: update publish workflow to Actions v4 with fetch-depth and add SCM fallback_version --- .github/workflows/python-publish.yml | 6 ++++-- pyproject.toml | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 479faed3..af6c4ae5 100755 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -21,9 +21,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: '3.x' - name: Install dependencies diff --git a/pyproject.toml b/pyproject.toml index 5ab49ecf..372e03e9 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ requires = ["setuptools>=80", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] +fallback_version = "0.0.0" [tool.ruff.lint] # Ignore ambiguous variable name errors (E741) in interpretation files From 0aa0227eb776a17c2d79ba18aa67f787b2953673 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 09:43:00 -0700 Subject: [PATCH 09/15] build: migrate to PEP 621 pyproject.toml, remove setup.py, adopt uv in CI Co-Authored-By: Claude Sonnet 4.6 --- .../workflows/python-package-version-test.yml | 11 ++-- .readthedocs.yaml | 5 +- pyproject.toml | 55 +++++++++++++++++++ requirements.txt | 8 ++- setup.py | 43 --------------- 5 files changed, 69 insertions(+), 53 deletions(-) delete mode 100644 setup.py diff --git a/.github/workflows/python-package-version-test.yml b/.github/workflows/python-package-version-test.yml index 139aee13..ec4888e4 100644 --- a/.github/workflows/python-package-version-test.yml +++ b/.github/workflows/python-package-version-test.yml @@ -24,15 +24,12 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v3 - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install ruff pytest - pip install "torch>=2.6.0" - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + run: uv pip install --system -e ".[dev,torch]" - name: Lint with ruff - run: | - python -m ruff check pyreason/scripts + run: python -m ruff check pyreason/scripts - name: Pytest Unit Tests with JIT Disabled run: | pytest tests/unit/disable_jit --tb=short -q diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 659aba0a..a4952210 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,4 +12,7 @@ sphinx: python: install: - - requirements: requirements.txt + - method: pip + path: . + extra_requirements: + - docs diff --git a/pyproject.toml b/pyproject.toml index 372e03e9..2eb903a3 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,9 +2,64 @@ requires = ["setuptools>=80", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" +[project] +name = "pyreason" +description = "An explainable inference software supporting annotated, real valued, graph based and temporal logic" +readme = "README.md" +license = { text = "BSD 3-Clause" } +authors = [ + { name = "Dyuman Aditya", email = "dyuman.aditya@gmail.com" } +] +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", +] +requires-python = ">=3.10" +dynamic = ["version"] +dependencies = [ + "networkx>=3.1", + "pyyaml>=6.0", + "pandas>=2.0.0", + "numba>=0.65.1", + "numpy>=2.1,<2.5", + "memory_profiler", +] + +[project.optional-dependencies] +torch = ["torch>=2.6.0"] +dev = [ + "pytest", + "pytest-cov", + "pre-commit", + "ruff", +] +docs = [ + "sphinx", + "sphinx_rtd_theme", + "sphinx-autopackagesummary", + "sphinx-autoapi", +] + +[project.urls] +Homepage = "https://github.com/lab-v2/pyreason" +"Bug Tracker" = "https://github.com/lab-v2/pyreason/issues" +Repository = "https://github.com/lab-v2/pyreason" + [tool.setuptools_scm] fallback_version = "0.0.0" +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages.find] +where = ["."] + [tool.ruff.lint] # Ignore ambiguous variable name errors (E741) in interpretation files [tool.ruff.lint.per-file-ignores] diff --git a/requirements.txt b/requirements.txt index 25046fa7..5593480f 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,20 @@ +# Install all development dependencies: +# uv pip install -e ".[dev,torch,docs]" (recommended) +# pip install -r requirements.txt (legacy fallback) + networkx>=3.1 pyyaml>=6.0 pandas>=2.0.0 numba>=0.65.1 numpy>=2.1,<2.5 memory_profiler -pytest torch>=2.6.0 +pytest pytest-cov pre-commit +ruff sphinx_rtd_theme sphinx sphinx-autopackagesummary sphinx-autoapi -ruff diff --git a/setup.py b/setup.py deleted file mode 100644 index ccf4b65f..00000000 --- a/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -from setuptools import setup, find_packages - -from pathlib import Path - -this_directory = Path(__file__).parent -long_description = (this_directory / "README.md").read_text(encoding='UTF-8') - -setup( - name='pyreason', - author='Dyuman Aditya', - author_email='dyuman.aditya@gmail.com', - description='An explainable inference software supporting annotated, real valued, graph based and temporal logic', - long_description=long_description, - long_description_content_type='text/markdown', - url='https://github.com/lab-v2/pyreason', - license='BSD 3-clause', - project_urls={ - 'Bug Tracker': 'https://github.com/lab-v2/pyreason/issues', - 'Repository': 'https://github.com/lab-v2/pyreason' - }, - classifiers=[ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent" - ], - python_requires='>=3.10', - install_requires=[ - 'networkx>=3.1', - 'pyyaml>=6.0', - 'pandas>=2.0.0', - 'numba>=0.65.1', - 'numpy>=2.1,<2.5', - 'memory_profiler', - 'pytest' - ], - packages=find_packages(), - include_package_data=True -) From 079e50e4e4726ffe333ad195689249c946345b88 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 09:54:42 -0700 Subject: [PATCH 10/15] fix: guard memory_profiler import, move to dev optional dependency Co-Authored-By: Claude Sonnet 4.6 --- pyproject.toml | 2 +- pyreason/pyreason.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2eb903a3..0441e5e1 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ dependencies = [ "pandas>=2.0.0", "numba>=0.65.1", "numpy>=2.1,<2.5", - "memory_profiler", ] [project.optional-dependencies] @@ -38,6 +37,7 @@ dev = [ "pytest-cov", "pre-commit", "ruff", + "memory_profiler", ] docs = [ "sphinx", diff --git a/pyreason/pyreason.py b/pyreason/pyreason.py index 7de69aca..5bbffc88 100755 --- a/pyreason/pyreason.py +++ b/pyreason/pyreason.py @@ -7,7 +7,12 @@ import time import sys import pandas as pd -import memory_profiler as mp +try: + import memory_profiler as mp + _has_memory_profiler = True +except ImportError: + mp = None + _has_memory_profiler = False import warnings from typing import List, Type, Callable, Tuple, Optional @@ -1458,6 +1463,12 @@ def reason(timesteps: int = -1, convergence_threshold: int = -1, convergence_bou if settings.output_to_file: sys.stdout = open(f"./{settings.output_file_name}_{__timestamp}.txt", "a") + if settings.memory_profile and not _has_memory_profiler: + raise ImportError( + "memory_profiler is required for memory profiling. " + "Install it with: pip install memory_profiler" + ) + if not again or __program is None: if settings.memory_profile: start_mem = mp.memory_usage(max_usage=True) From 647d0e5cc0d6038899157e4f419f8e2b9494a75c Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 10:33:13 -0700 Subject: [PATCH 11/15] fix: replace numba.typed.List comprehensions with explicit loops for 3.12+ compat Numba 0.65.x tightened nopython type inference for list comprehensions that construct tuples from separate variables. The three affected cases in get_rule_edge_clause_grounding build edge tuples (source, target) from two string variables; replacing with empty_list + append loop resolves the TypingError on Python 3.12/3.13/3.14. Also bump actions/setup-python v4 -> v5 and astral-sh/setup-uv v3 -> v6 across CI workflows to avoid Node.js 20 deprecation breakage on June 2, 2026. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/python-package-version-test.yml | 4 ++-- .github/workflows/python-publish.yml | 2 +- pyreason/scripts/interpretation/interpretation.py | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-package-version-test.yml b/.github/workflows/python-package-version-test.yml index ec4888e4..bd1695ee 100644 --- a/.github/workflows/python-package-version-test.yml +++ b/.github/workflows/python-package-version-test.yml @@ -21,11 +21,11 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v6 - name: Install dependencies run: uv pip install --system -e ".[dev,torch]" - name: Lint with ruff diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index af6c4ae5..bcfb2de8 100755 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -25,7 +25,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies diff --git a/pyreason/scripts/interpretation/interpretation.py b/pyreason/scripts/interpretation/interpretation.py index c2e1a187..4f9a484e 100755 --- a/pyreason/scripts/interpretation/interpretation.py +++ b/pyreason/scripts/interpretation/interpretation.py @@ -1423,14 +1423,18 @@ def get_rule_edge_clause_grounding(clause_var_1, clause_var_2, groundings, groun # We replace Y by the sources of Z elif clause_var_1 not in groundings and clause_var_2 in groundings: for n in groundings[clause_var_2]: - es = numba.typed.List([(nn, n) for nn in reverse_neighbors[n]]) + es = numba.typed.List.empty_list(edge_type) + for nn in reverse_neighbors[n]: + es.append((nn, n)) edge_groundings.extend(es) # Case 3: # We replace Z by the neighbors of Y elif clause_var_1 in groundings and clause_var_2 not in groundings: for n in groundings[clause_var_1]: - es = numba.typed.List([(n, nn) for nn in neighbors[n]]) + es = numba.typed.List.empty_list(edge_type) + for nn in neighbors[n]: + es.append((n, nn)) edge_groundings.extend(es) # Case 4: @@ -1443,7 +1447,10 @@ def get_rule_edge_clause_grounding(clause_var_1, clause_var_2, groundings, groun else: groundings_clause_var_2_set = set(groundings[clause_var_2]) for n in groundings[clause_var_1]: - es = numba.typed.List([(n, nn) for nn in neighbors[n] if nn in groundings_clause_var_2_set]) + es = numba.typed.List.empty_list(edge_type) + for nn in neighbors[n]: + if nn in groundings_clause_var_2_set: + es.append((n, nn)) edge_groundings.extend(es) return edge_groundings From bb538bcfb86d6f973f6936524f6e59fb1f5bb558 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 10:37:51 -0700 Subject: [PATCH 12/15] fix: apply numba.typed.List loop fix to interpretation_parallel.py The parallel consistency test requires interpretation.py and interpretation_parallel.py to be identical except for parallel=True/False. Apply the same numba.typed.List.empty_list + append loop pattern to the three cases in get_rule_edge_clause_grounding. Co-Authored-By: Claude Sonnet 4.6 --- .../interpretation/interpretation_parallel.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pyreason/scripts/interpretation/interpretation_parallel.py b/pyreason/scripts/interpretation/interpretation_parallel.py index 251713b8..ebb6b83a 100644 --- a/pyreason/scripts/interpretation/interpretation_parallel.py +++ b/pyreason/scripts/interpretation/interpretation_parallel.py @@ -1423,14 +1423,18 @@ def get_rule_edge_clause_grounding(clause_var_1, clause_var_2, groundings, groun # We replace Y by the sources of Z elif clause_var_1 not in groundings and clause_var_2 in groundings: for n in groundings[clause_var_2]: - es = numba.typed.List([(nn, n) for nn in reverse_neighbors[n]]) + es = numba.typed.List.empty_list(edge_type) + for nn in reverse_neighbors[n]: + es.append((nn, n)) edge_groundings.extend(es) # Case 3: # We replace Z by the neighbors of Y elif clause_var_1 in groundings and clause_var_2 not in groundings: for n in groundings[clause_var_1]: - es = numba.typed.List([(n, nn) for nn in neighbors[n]]) + es = numba.typed.List.empty_list(edge_type) + for nn in neighbors[n]: + es.append((n, nn)) edge_groundings.extend(es) # Case 4: @@ -1443,7 +1447,10 @@ def get_rule_edge_clause_grounding(clause_var_1, clause_var_2, groundings, groun else: groundings_clause_var_2_set = set(groundings[clause_var_2]) for n in groundings[clause_var_1]: - es = numba.typed.List([(n, nn) for nn in neighbors[n] if nn in groundings_clause_var_2_set]) + es = numba.typed.List.empty_list(edge_type) + for nn in neighbors[n]: + if nn in groundings_clause_var_2_set: + es.append((n, nn)) edge_groundings.extend(es) return edge_groundings From 5e13bf751cfbbef91d005323ea09f7a56ba7e550 Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 11:36:46 -0700 Subject: [PATCH 13/15] fix: rename integer loop variable i to fi/ri/j in reason() to fix Numba type unification on Python 3.11+ Co-Authored-By: Claude Sonnet 4.6 --- .../scripts/interpretation/interpretation.py | 120 +++++++++--------- .../interpretation/interpretation_parallel.py | 120 +++++++++--------- 2 files changed, 120 insertions(+), 120 deletions(-) diff --git a/pyreason/scripts/interpretation/interpretation.py b/pyreason/scripts/interpretation/interpretation.py index 4f9a484e..4bc3d800 100755 --- a/pyreason/scripts/interpretation/interpretation.py +++ b/pyreason/scripts/interpretation/interpretation.py @@ -282,9 +282,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi facts_to_be_applied_node_new.clear() facts_to_be_applied_node_trace_new.clear() nodes_set = set(nodes) - for i in range(len(facts_to_be_applied_node)): - if facts_to_be_applied_node[i][0] == t: - comp, l, bnd, static, graph_attribute = facts_to_be_applied_node[i][1], facts_to_be_applied_node[i][2], facts_to_be_applied_node[i][3], facts_to_be_applied_node[i][4], facts_to_be_applied_node[i][5] + for fi in range(len(facts_to_be_applied_node)): + if facts_to_be_applied_node[fi][0] == t: + comp, l, bnd, static, graph_attribute = facts_to_be_applied_node[fi][1], facts_to_be_applied_node[fi][2], facts_to_be_applied_node[fi][3], facts_to_be_applied_node[fi][4], facts_to_be_applied_node[fi][5] # If the component is not in the graph, add it if comp not in nodes_set: _add_node(comp, neighbors, reverse_neighbors, nodes, interpretations_node) @@ -295,26 +295,26 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi # Check if we should even store any of the changes to the rule trace etc. # Inverse of this is: if not save_graph_attributes_to_rule_trace and graph_attribute if (save_graph_attributes_to_rule_trace or not graph_attribute) and store_interpretation_changes: - meta_name = facts_to_be_applied_node_trace[i] if atom_trace else '' + meta_name = facts_to_be_applied_node_trace[fi] if atom_trace else '' rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, l, bnd, True, 'Fact', meta_name, '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_node_trace[fi]) for p1, p2 in ipl: if p1==l: rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p2, interpretations_node[comp].world[p2], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p2], facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p2], facts_to_be_applied_node_trace[fi]) elif p2==l: rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p1, interpretations_node[comp].world[p1], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p1], facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p1], facts_to_be_applied_node_trace[fi]) else: # Check for inconsistencies (multiple facts) if check_consistent_node(interpretations_node, comp, (l, bnd)): mode = 'graph-attribute-fact' if graph_attribute else 'fact' override = True if update_mode == 'override' else False - u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, i, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) + u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, fi, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) update = u or update # Update convergence params @@ -326,9 +326,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi else: mode = 'graph-attribute-fact' if graph_attribute else 'fact' if inconsistency_check: - resolve_inconsistency_node(interpretations_node, comp, (l, bnd), ipl, t, fp_cnt, i, atom_trace, rule_trace_node, rule_trace_node_atoms, rules_to_be_applied_node_trace, facts_to_be_applied_node_trace, store_interpretation_changes, mode=mode) + resolve_inconsistency_node(interpretations_node, comp, (l, bnd), ipl, t, fp_cnt, fi, atom_trace, rule_trace_node, rule_trace_node_atoms, rules_to_be_applied_node_trace, facts_to_be_applied_node_trace, store_interpretation_changes, mode=mode) else: - u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, i, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) + u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, fi, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) update = u or update # Update convergence params @@ -338,15 +338,15 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi changes_cnt += changes if static: - facts_to_be_applied_node_new.append((numba.types.uint16(facts_to_be_applied_node[i][0]+1), comp, l, bnd, static, graph_attribute)) + facts_to_be_applied_node_new.append((numba.types.uint16(facts_to_be_applied_node[fi][0]+1), comp, l, bnd, static, graph_attribute)) if atom_trace: - facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[i]) + facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[fi]) # If time doesn't match, fact to be applied later else: - facts_to_be_applied_node_new.append(facts_to_be_applied_node[i]) + facts_to_be_applied_node_new.append(facts_to_be_applied_node[fi]) if atom_trace: - facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[i]) + facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[fi]) # Update list of facts with ones that have not been applied yet (delete applied facts) facts_to_be_applied_node[:] = facts_to_be_applied_node_new.copy() @@ -359,9 +359,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi facts_to_be_applied_edge_new.clear() facts_to_be_applied_edge_trace_new.clear() edges_set = set(edges) - for i in range(len(facts_to_be_applied_edge)): - if facts_to_be_applied_edge[i][0]==t: - comp, l, bnd, static, graph_attribute = facts_to_be_applied_edge[i][1], facts_to_be_applied_edge[i][2], facts_to_be_applied_edge[i][3], facts_to_be_applied_edge[i][4], facts_to_be_applied_edge[i][5] + for fi in range(len(facts_to_be_applied_edge)): + if facts_to_be_applied_edge[fi][0]==t: + comp, l, bnd, static, graph_attribute = facts_to_be_applied_edge[fi][1], facts_to_be_applied_edge[fi][2], facts_to_be_applied_edge[fi][3], facts_to_be_applied_edge[fi][4], facts_to_be_applied_edge[fi][5] # If the component is not in the graph, add it if comp not in edges_set: _add_edge(comp[0], comp[1], neighbors, reverse_neighbors, nodes, edges, label.Label(''), interpretations_node, interpretations_edge, predicate_map_edge, num_ga, t) @@ -371,25 +371,25 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi if l in interpretations_edge[comp].world and interpretations_edge[comp].world[l].is_static(): # Inverse of this is: if not save_graph_attributes_to_rule_trace and graph_attribute if (save_graph_attributes_to_rule_trace or not graph_attribute) and store_interpretation_changes: - meta_name = facts_to_be_applied_edge_trace[i] if atom_trace else '' + meta_name = facts_to_be_applied_edge_trace[fi] if atom_trace else '' rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, l, interpretations_edge[comp].world[l], True, 'Fact', meta_name, '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_edge_trace[fi]) for p1, p2 in ipl: if p1==l: rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p2, interpretations_edge[comp].world[p2], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p2], facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p2], facts_to_be_applied_edge_trace[fi]) elif p2==l: rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p1, interpretations_edge[comp].world[p1], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p1], facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p1], facts_to_be_applied_edge_trace[fi]) else: # Check for inconsistencies if check_consistent_edge(interpretations_edge, comp, (l, bnd)): mode = 'graph-attribute-fact' if graph_attribute else 'fact' override = True if update_mode == 'override' else False - u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, i, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) + u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, fi, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) update = u or update # Update convergence params @@ -401,9 +401,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi else: mode = 'graph-attribute-fact' if graph_attribute else 'fact' if inconsistency_check: - resolve_inconsistency_edge(interpretations_edge, comp, (l, bnd), ipl, t, fp_cnt, i, atom_trace, rule_trace_edge, rule_trace_edge_atoms, rules_to_be_applied_edge_trace, facts_to_be_applied_edge_trace, store_interpretation_changes, mode=mode) + resolve_inconsistency_edge(interpretations_edge, comp, (l, bnd), ipl, t, fp_cnt, fi, atom_trace, rule_trace_edge, rule_trace_edge_atoms, rules_to_be_applied_edge_trace, facts_to_be_applied_edge_trace, store_interpretation_changes, mode=mode) else: - u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, i, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) + u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, fi, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) update = u or update # Update convergence params @@ -413,15 +413,15 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi changes_cnt += changes if static: - facts_to_be_applied_edge_new.append((numba.types.uint16(facts_to_be_applied_edge[i][0]+1), comp, l, bnd, static, graph_attribute)) + facts_to_be_applied_edge_new.append((numba.types.uint16(facts_to_be_applied_edge[fi][0]+1), comp, l, bnd, static, graph_attribute)) if atom_trace: - facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[i]) + facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[fi]) # Time doesn't match, fact to be applied later else: - facts_to_be_applied_edge_new.append(facts_to_be_applied_edge[i]) + facts_to_be_applied_edge_new.append(facts_to_be_applied_edge[fi]) if atom_trace: - facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[i]) + facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[fi]) # Update list of facts with ones that have not been applied yet (delete applied facts) facts_to_be_applied_edge[:] = facts_to_be_applied_edge_new.copy() @@ -470,10 +470,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_remove_idx.add(idx) # Remove from rules to be applied and edges to be applied lists after coming out from loop - rules_to_be_applied_node[:] = numba.typed.List([rules_to_be_applied_node[i] for i in range(len(rules_to_be_applied_node)) if i not in rules_to_remove_idx]) - edges_to_be_added_node_rule[:] = numba.typed.List([edges_to_be_added_node_rule[i] for i in range(len(edges_to_be_added_node_rule)) if i not in rules_to_remove_idx]) + rules_to_be_applied_node[:] = numba.typed.List([rules_to_be_applied_node[j] for j in range(len(rules_to_be_applied_node)) if j not in rules_to_remove_idx]) + edges_to_be_added_node_rule[:] = numba.typed.List([edges_to_be_added_node_rule[j] for j in range(len(edges_to_be_added_node_rule)) if j not in rules_to_remove_idx]) if atom_trace: - rules_to_be_applied_node_trace[:] = numba.typed.List([rules_to_be_applied_node_trace[i] for i in range(len(rules_to_be_applied_node_trace)) if i not in rules_to_remove_idx]) + rules_to_be_applied_node_trace[:] = numba.typed.List([rules_to_be_applied_node_trace[j] for j in range(len(rules_to_be_applied_node_trace)) if j not in rules_to_remove_idx]) # Edges rules_to_remove_idx.clear() @@ -544,10 +544,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_remove_idx.add(idx) # Remove from rules to be applied and edges to be applied lists after coming out from loop - rules_to_be_applied_edge[:] = numba.typed.List([rules_to_be_applied_edge[i] for i in range(len(rules_to_be_applied_edge)) if i not in rules_to_remove_idx]) - edges_to_be_added_edge_rule[:] = numba.typed.List([edges_to_be_added_edge_rule[i] for i in range(len(edges_to_be_added_edge_rule)) if i not in rules_to_remove_idx]) + rules_to_be_applied_edge[:] = numba.typed.List([rules_to_be_applied_edge[j] for j in range(len(rules_to_be_applied_edge)) if j not in rules_to_remove_idx]) + edges_to_be_added_edge_rule[:] = numba.typed.List([edges_to_be_added_edge_rule[j] for j in range(len(edges_to_be_added_edge_rule)) if j not in rules_to_remove_idx]) if atom_trace: - rules_to_be_applied_edge_trace[:] = numba.typed.List([rules_to_be_applied_edge_trace[i] for i in range(len(rules_to_be_applied_edge_trace)) if i not in rules_to_remove_idx]) + rules_to_be_applied_edge_trace[:] = numba.typed.List([rules_to_be_applied_edge_trace[j] for j in range(len(rules_to_be_applied_edge_trace)) if j not in rules_to_remove_idx]) # Fixed point if update: @@ -568,13 +568,13 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi in_loop_threadsafe.append(False) update_threadsafe.append(True) - for i in prange(len(rules)): - rule = rules[i] + for ri in prange(len(rules)): + rule = rules[ri] # Only go through if the rule can be applied within the given timesteps, or we're running until convergence delta_t = rule.get_delta() if t + delta_t <= tmax or tmax == -1 or again: - applicable_node_rules, applicable_edge_rules = _ground_rule(rule, interpretations_node, interpretations_edge, predicate_map_node, predicate_map_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, extended_ann_fn_flags[i], allow_ground_rules, num_ga, t, head_functions, closed_world_predicates) + applicable_node_rules, applicable_edge_rules = _ground_rule(rule, interpretations_node, interpretations_edge, predicate_map_node, predicate_map_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, extended_ann_fn_flags[ri], allow_ground_rules, num_ga, t, head_functions, closed_world_predicates) # Loop through applicable rules and add them to the rules to be applied for later or next fp operation for applicable_rule in applicable_node_rules: @@ -592,14 +592,14 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bnd_u = min(max(bnd[1], 0), 1) bnd = interval.closed(bnd_l, bnd_u) max_rules_time = max(max_rules_time, t + delta_t) - rules_to_be_applied_node_threadsafe[i].append((numba.types.uint16(t + delta_t), n, rule.get_target(), bnd, rule.is_static_rule())) + rules_to_be_applied_node_threadsafe[ri].append((numba.types.uint16(t + delta_t), n, rule.get_target(), bnd, rule.is_static_rule())) if atom_trace: - rules_to_be_applied_node_trace_threadsafe[i].append((qualified_nodes, qualified_edges, rule.get_name())) + rules_to_be_applied_node_trace_threadsafe[ri].append((qualified_nodes, qualified_edges, rule.get_name())) # If delta_t is zero we apply the rules and check if more are applicable if delta_t == 0: - in_loop_threadsafe[i] = True - update_threadsafe[i] = False + in_loop_threadsafe[ri] = True + update_threadsafe[ri] = False for applicable_rule in applicable_edge_rules: e, annotations, qualified_nodes, qualified_edges, edges_to_add, clause_labels, clause_variables = applicable_rule @@ -617,38 +617,38 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bnd = interval.closed(bnd_l, bnd_u) max_rules_time = max(max_rules_time, t+delta_t) # edges_to_be_added_edge_rule.append(edges_to_add) - edges_to_be_added_edge_rule_threadsafe[i].append(edges_to_add) - rules_to_be_applied_edge_threadsafe[i].append((numba.types.uint16(t+delta_t), e, rule.get_target(), bnd, rule.is_static_rule())) + edges_to_be_added_edge_rule_threadsafe[ri].append(edges_to_add) + rules_to_be_applied_edge_threadsafe[ri].append((numba.types.uint16(t+delta_t), e, rule.get_target(), bnd, rule.is_static_rule())) if atom_trace: # rules_to_be_applied_edge_trace.append((qualified_nodes, qualified_edges, rule.get_name())) - rules_to_be_applied_edge_trace_threadsafe[i].append((qualified_nodes, qualified_edges, rule.get_name())) + rules_to_be_applied_edge_trace_threadsafe[ri].append((qualified_nodes, qualified_edges, rule.get_name())) # If delta_t is zero we apply the rules and check if more are applicable if delta_t == 0: - in_loop_threadsafe[i] = True - update_threadsafe[i] = False + in_loop_threadsafe[ri] = True + update_threadsafe[ri] = False # Update lists after parallel run - for i in range(len(rules)): - if len(rules_to_be_applied_node_threadsafe[i]) > 0: - rules_to_be_applied_node.extend(rules_to_be_applied_node_threadsafe[i]) - if len(rules_to_be_applied_edge_threadsafe[i]) > 0: - rules_to_be_applied_edge.extend(rules_to_be_applied_edge_threadsafe[i]) + for ri in range(len(rules)): + if len(rules_to_be_applied_node_threadsafe[ri]) > 0: + rules_to_be_applied_node.extend(rules_to_be_applied_node_threadsafe[ri]) + if len(rules_to_be_applied_edge_threadsafe[ri]) > 0: + rules_to_be_applied_edge.extend(rules_to_be_applied_edge_threadsafe[ri]) if atom_trace: - if len(rules_to_be_applied_node_trace_threadsafe[i]) > 0: - rules_to_be_applied_node_trace.extend(rules_to_be_applied_node_trace_threadsafe[i]) - if len(rules_to_be_applied_edge_trace_threadsafe[i]) > 0: - rules_to_be_applied_edge_trace.extend(rules_to_be_applied_edge_trace_threadsafe[i]) - if len(edges_to_be_added_edge_rule_threadsafe[i]) > 0: - edges_to_be_added_edge_rule.extend(edges_to_be_added_edge_rule_threadsafe[i]) + if len(rules_to_be_applied_node_trace_threadsafe[ri]) > 0: + rules_to_be_applied_node_trace.extend(rules_to_be_applied_node_trace_threadsafe[ri]) + if len(rules_to_be_applied_edge_trace_threadsafe[ri]) > 0: + rules_to_be_applied_edge_trace.extend(rules_to_be_applied_edge_trace_threadsafe[ri]) + if len(edges_to_be_added_edge_rule_threadsafe[ri]) > 0: + edges_to_be_added_edge_rule.extend(edges_to_be_added_edge_rule_threadsafe[ri]) # Merge threadsafe flags for in_loop and update in_loop = in_loop update = update - for i in range(len(rules)): - if in_loop_threadsafe[i]: + for ri in range(len(rules)): + if in_loop_threadsafe[ri]: in_loop = True - if not update_threadsafe[i]: + if not update_threadsafe[ri]: update = False # Check for convergence after each timestep (perfect convergence or convergence specified by user) diff --git a/pyreason/scripts/interpretation/interpretation_parallel.py b/pyreason/scripts/interpretation/interpretation_parallel.py index ebb6b83a..190ab7a4 100644 --- a/pyreason/scripts/interpretation/interpretation_parallel.py +++ b/pyreason/scripts/interpretation/interpretation_parallel.py @@ -282,9 +282,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi facts_to_be_applied_node_new.clear() facts_to_be_applied_node_trace_new.clear() nodes_set = set(nodes) - for i in range(len(facts_to_be_applied_node)): - if facts_to_be_applied_node[i][0] == t: - comp, l, bnd, static, graph_attribute = facts_to_be_applied_node[i][1], facts_to_be_applied_node[i][2], facts_to_be_applied_node[i][3], facts_to_be_applied_node[i][4], facts_to_be_applied_node[i][5] + for fi in range(len(facts_to_be_applied_node)): + if facts_to_be_applied_node[fi][0] == t: + comp, l, bnd, static, graph_attribute = facts_to_be_applied_node[fi][1], facts_to_be_applied_node[fi][2], facts_to_be_applied_node[fi][3], facts_to_be_applied_node[fi][4], facts_to_be_applied_node[fi][5] # If the component is not in the graph, add it if comp not in nodes_set: _add_node(comp, neighbors, reverse_neighbors, nodes, interpretations_node) @@ -295,26 +295,26 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi # Check if we should even store any of the changes to the rule trace etc. # Inverse of this is: if not save_graph_attributes_to_rule_trace and graph_attribute if (save_graph_attributes_to_rule_trace or not graph_attribute) and store_interpretation_changes: - meta_name = facts_to_be_applied_node_trace[i] if atom_trace else '' + meta_name = facts_to_be_applied_node_trace[fi] if atom_trace else '' rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, l, bnd, True, 'Fact', meta_name, '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_node_trace[fi]) for p1, p2 in ipl: if p1==l: rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p2, interpretations_node[comp].world[p2], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p2], facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p2], facts_to_be_applied_node_trace[fi]) elif p2==l: rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p1, interpretations_node[comp].world[p1], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p1], facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[comp].world[p1], facts_to_be_applied_node_trace[fi]) else: # Check for inconsistencies (multiple facts) if check_consistent_node(interpretations_node, comp, (l, bnd)): mode = 'graph-attribute-fact' if graph_attribute else 'fact' override = True if update_mode == 'override' else False - u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, i, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) + u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, fi, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) update = u or update # Update convergence params @@ -326,9 +326,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi else: mode = 'graph-attribute-fact' if graph_attribute else 'fact' if inconsistency_check: - resolve_inconsistency_node(interpretations_node, comp, (l, bnd), ipl, t, fp_cnt, i, atom_trace, rule_trace_node, rule_trace_node_atoms, rules_to_be_applied_node_trace, facts_to_be_applied_node_trace, store_interpretation_changes, mode=mode) + resolve_inconsistency_node(interpretations_node, comp, (l, bnd), ipl, t, fp_cnt, fi, atom_trace, rule_trace_node, rule_trace_node_atoms, rules_to_be_applied_node_trace, facts_to_be_applied_node_trace, store_interpretation_changes, mode=mode) else: - u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, i, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) + u, changes = _update_node(interpretations_node, predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, fi, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) update = u or update # Update convergence params @@ -338,15 +338,15 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi changes_cnt += changes if static: - facts_to_be_applied_node_new.append((numba.types.uint16(facts_to_be_applied_node[i][0]+1), comp, l, bnd, static, graph_attribute)) + facts_to_be_applied_node_new.append((numba.types.uint16(facts_to_be_applied_node[fi][0]+1), comp, l, bnd, static, graph_attribute)) if atom_trace: - facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[i]) + facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[fi]) # If time doesn't match, fact to be applied later else: - facts_to_be_applied_node_new.append(facts_to_be_applied_node[i]) + facts_to_be_applied_node_new.append(facts_to_be_applied_node[fi]) if atom_trace: - facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[i]) + facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[fi]) # Update list of facts with ones that have not been applied yet (delete applied facts) facts_to_be_applied_node[:] = facts_to_be_applied_node_new.copy() @@ -359,9 +359,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi facts_to_be_applied_edge_new.clear() facts_to_be_applied_edge_trace_new.clear() edges_set = set(edges) - for i in range(len(facts_to_be_applied_edge)): - if facts_to_be_applied_edge[i][0]==t: - comp, l, bnd, static, graph_attribute = facts_to_be_applied_edge[i][1], facts_to_be_applied_edge[i][2], facts_to_be_applied_edge[i][3], facts_to_be_applied_edge[i][4], facts_to_be_applied_edge[i][5] + for fi in range(len(facts_to_be_applied_edge)): + if facts_to_be_applied_edge[fi][0]==t: + comp, l, bnd, static, graph_attribute = facts_to_be_applied_edge[fi][1], facts_to_be_applied_edge[fi][2], facts_to_be_applied_edge[fi][3], facts_to_be_applied_edge[fi][4], facts_to_be_applied_edge[fi][5] # If the component is not in the graph, add it if comp not in edges_set: _add_edge(comp[0], comp[1], neighbors, reverse_neighbors, nodes, edges, label.Label(''), interpretations_node, interpretations_edge, predicate_map_edge, num_ga, t) @@ -371,25 +371,25 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi if l in interpretations_edge[comp].world and interpretations_edge[comp].world[l].is_static(): # Inverse of this is: if not save_graph_attributes_to_rule_trace and graph_attribute if (save_graph_attributes_to_rule_trace or not graph_attribute) and store_interpretation_changes: - meta_name = facts_to_be_applied_edge_trace[i] if atom_trace else '' + meta_name = facts_to_be_applied_edge_trace[fi] if atom_trace else '' rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, l, interpretations_edge[comp].world[l], True, 'Fact', meta_name, '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_edge_trace[fi]) for p1, p2 in ipl: if p1==l: rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p2, interpretations_edge[comp].world[p2], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p2], facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p2], facts_to_be_applied_edge_trace[fi]) elif p2==l: rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p1, interpretations_edge[comp].world[p1], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p1], facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[comp].world[p1], facts_to_be_applied_edge_trace[fi]) else: # Check for inconsistencies if check_consistent_edge(interpretations_edge, comp, (l, bnd)): mode = 'graph-attribute-fact' if graph_attribute else 'fact' override = True if update_mode == 'override' else False - u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, i, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) + u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, fi, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=override) update = u or update # Update convergence params @@ -401,9 +401,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi else: mode = 'graph-attribute-fact' if graph_attribute else 'fact' if inconsistency_check: - resolve_inconsistency_edge(interpretations_edge, comp, (l, bnd), ipl, t, fp_cnt, i, atom_trace, rule_trace_edge, rule_trace_edge_atoms, rules_to_be_applied_edge_trace, facts_to_be_applied_edge_trace, store_interpretation_changes, mode=mode) + resolve_inconsistency_edge(interpretations_edge, comp, (l, bnd), ipl, t, fp_cnt, fi, atom_trace, rule_trace_edge, rule_trace_edge_atoms, rules_to_be_applied_edge_trace, facts_to_be_applied_edge_trace, store_interpretation_changes, mode=mode) else: - u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, i, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) + u, changes = _update_edge(interpretations_edge, predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, fi, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, num_ga, mode=mode, override=True) update = u or update # Update convergence params @@ -413,15 +413,15 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi changes_cnt += changes if static: - facts_to_be_applied_edge_new.append((numba.types.uint16(facts_to_be_applied_edge[i][0]+1), comp, l, bnd, static, graph_attribute)) + facts_to_be_applied_edge_new.append((numba.types.uint16(facts_to_be_applied_edge[fi][0]+1), comp, l, bnd, static, graph_attribute)) if atom_trace: - facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[i]) + facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[fi]) # Time doesn't match, fact to be applied later else: - facts_to_be_applied_edge_new.append(facts_to_be_applied_edge[i]) + facts_to_be_applied_edge_new.append(facts_to_be_applied_edge[fi]) if atom_trace: - facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[i]) + facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[fi]) # Update list of facts with ones that have not been applied yet (delete applied facts) facts_to_be_applied_edge[:] = facts_to_be_applied_edge_new.copy() @@ -470,10 +470,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_remove_idx.add(idx) # Remove from rules to be applied and edges to be applied lists after coming out from loop - rules_to_be_applied_node[:] = numba.typed.List([rules_to_be_applied_node[i] for i in range(len(rules_to_be_applied_node)) if i not in rules_to_remove_idx]) - edges_to_be_added_node_rule[:] = numba.typed.List([edges_to_be_added_node_rule[i] for i in range(len(edges_to_be_added_node_rule)) if i not in rules_to_remove_idx]) + rules_to_be_applied_node[:] = numba.typed.List([rules_to_be_applied_node[j] for j in range(len(rules_to_be_applied_node)) if j not in rules_to_remove_idx]) + edges_to_be_added_node_rule[:] = numba.typed.List([edges_to_be_added_node_rule[j] for j in range(len(edges_to_be_added_node_rule)) if j not in rules_to_remove_idx]) if atom_trace: - rules_to_be_applied_node_trace[:] = numba.typed.List([rules_to_be_applied_node_trace[i] for i in range(len(rules_to_be_applied_node_trace)) if i not in rules_to_remove_idx]) + rules_to_be_applied_node_trace[:] = numba.typed.List([rules_to_be_applied_node_trace[j] for j in range(len(rules_to_be_applied_node_trace)) if j not in rules_to_remove_idx]) # Edges rules_to_remove_idx.clear() @@ -544,10 +544,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_remove_idx.add(idx) # Remove from rules to be applied and edges to be applied lists after coming out from loop - rules_to_be_applied_edge[:] = numba.typed.List([rules_to_be_applied_edge[i] for i in range(len(rules_to_be_applied_edge)) if i not in rules_to_remove_idx]) - edges_to_be_added_edge_rule[:] = numba.typed.List([edges_to_be_added_edge_rule[i] for i in range(len(edges_to_be_added_edge_rule)) if i not in rules_to_remove_idx]) + rules_to_be_applied_edge[:] = numba.typed.List([rules_to_be_applied_edge[j] for j in range(len(rules_to_be_applied_edge)) if j not in rules_to_remove_idx]) + edges_to_be_added_edge_rule[:] = numba.typed.List([edges_to_be_added_edge_rule[j] for j in range(len(edges_to_be_added_edge_rule)) if j not in rules_to_remove_idx]) if atom_trace: - rules_to_be_applied_edge_trace[:] = numba.typed.List([rules_to_be_applied_edge_trace[i] for i in range(len(rules_to_be_applied_edge_trace)) if i not in rules_to_remove_idx]) + rules_to_be_applied_edge_trace[:] = numba.typed.List([rules_to_be_applied_edge_trace[j] for j in range(len(rules_to_be_applied_edge_trace)) if j not in rules_to_remove_idx]) # Fixed point if update: @@ -568,13 +568,13 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi in_loop_threadsafe.append(False) update_threadsafe.append(True) - for i in prange(len(rules)): - rule = rules[i] + for ri in prange(len(rules)): + rule = rules[ri] # Only go through if the rule can be applied within the given timesteps, or we're running until convergence delta_t = rule.get_delta() if t + delta_t <= tmax or tmax == -1 or again: - applicable_node_rules, applicable_edge_rules = _ground_rule(rule, interpretations_node, interpretations_edge, predicate_map_node, predicate_map_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, extended_ann_fn_flags[i], allow_ground_rules, num_ga, t, head_functions, closed_world_predicates) + applicable_node_rules, applicable_edge_rules = _ground_rule(rule, interpretations_node, interpretations_edge, predicate_map_node, predicate_map_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, extended_ann_fn_flags[ri], allow_ground_rules, num_ga, t, head_functions, closed_world_predicates) # Loop through applicable rules and add them to the rules to be applied for later or next fp operation for applicable_rule in applicable_node_rules: @@ -592,14 +592,14 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bnd_u = min(max(bnd[1], 0), 1) bnd = interval.closed(bnd_l, bnd_u) max_rules_time = max(max_rules_time, t + delta_t) - rules_to_be_applied_node_threadsafe[i].append((numba.types.uint16(t + delta_t), n, rule.get_target(), bnd, rule.is_static_rule())) + rules_to_be_applied_node_threadsafe[ri].append((numba.types.uint16(t + delta_t), n, rule.get_target(), bnd, rule.is_static_rule())) if atom_trace: - rules_to_be_applied_node_trace_threadsafe[i].append((qualified_nodes, qualified_edges, rule.get_name())) + rules_to_be_applied_node_trace_threadsafe[ri].append((qualified_nodes, qualified_edges, rule.get_name())) # If delta_t is zero we apply the rules and check if more are applicable if delta_t == 0: - in_loop_threadsafe[i] = True - update_threadsafe[i] = False + in_loop_threadsafe[ri] = True + update_threadsafe[ri] = False for applicable_rule in applicable_edge_rules: e, annotations, qualified_nodes, qualified_edges, edges_to_add, clause_labels, clause_variables = applicable_rule @@ -617,38 +617,38 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bnd = interval.closed(bnd_l, bnd_u) max_rules_time = max(max_rules_time, t+delta_t) # edges_to_be_added_edge_rule.append(edges_to_add) - edges_to_be_added_edge_rule_threadsafe[i].append(edges_to_add) - rules_to_be_applied_edge_threadsafe[i].append((numba.types.uint16(t+delta_t), e, rule.get_target(), bnd, rule.is_static_rule())) + edges_to_be_added_edge_rule_threadsafe[ri].append(edges_to_add) + rules_to_be_applied_edge_threadsafe[ri].append((numba.types.uint16(t+delta_t), e, rule.get_target(), bnd, rule.is_static_rule())) if atom_trace: # rules_to_be_applied_edge_trace.append((qualified_nodes, qualified_edges, rule.get_name())) - rules_to_be_applied_edge_trace_threadsafe[i].append((qualified_nodes, qualified_edges, rule.get_name())) + rules_to_be_applied_edge_trace_threadsafe[ri].append((qualified_nodes, qualified_edges, rule.get_name())) # If delta_t is zero we apply the rules and check if more are applicable if delta_t == 0: - in_loop_threadsafe[i] = True - update_threadsafe[i] = False + in_loop_threadsafe[ri] = True + update_threadsafe[ri] = False # Update lists after parallel run - for i in range(len(rules)): - if len(rules_to_be_applied_node_threadsafe[i]) > 0: - rules_to_be_applied_node.extend(rules_to_be_applied_node_threadsafe[i]) - if len(rules_to_be_applied_edge_threadsafe[i]) > 0: - rules_to_be_applied_edge.extend(rules_to_be_applied_edge_threadsafe[i]) + for ri in range(len(rules)): + if len(rules_to_be_applied_node_threadsafe[ri]) > 0: + rules_to_be_applied_node.extend(rules_to_be_applied_node_threadsafe[ri]) + if len(rules_to_be_applied_edge_threadsafe[ri]) > 0: + rules_to_be_applied_edge.extend(rules_to_be_applied_edge_threadsafe[ri]) if atom_trace: - if len(rules_to_be_applied_node_trace_threadsafe[i]) > 0: - rules_to_be_applied_node_trace.extend(rules_to_be_applied_node_trace_threadsafe[i]) - if len(rules_to_be_applied_edge_trace_threadsafe[i]) > 0: - rules_to_be_applied_edge_trace.extend(rules_to_be_applied_edge_trace_threadsafe[i]) - if len(edges_to_be_added_edge_rule_threadsafe[i]) > 0: - edges_to_be_added_edge_rule.extend(edges_to_be_added_edge_rule_threadsafe[i]) + if len(rules_to_be_applied_node_trace_threadsafe[ri]) > 0: + rules_to_be_applied_node_trace.extend(rules_to_be_applied_node_trace_threadsafe[ri]) + if len(rules_to_be_applied_edge_trace_threadsafe[ri]) > 0: + rules_to_be_applied_edge_trace.extend(rules_to_be_applied_edge_trace_threadsafe[ri]) + if len(edges_to_be_added_edge_rule_threadsafe[ri]) > 0: + edges_to_be_added_edge_rule.extend(edges_to_be_added_edge_rule_threadsafe[ri]) # Merge threadsafe flags for in_loop and update in_loop = in_loop update = update - for i in range(len(rules)): - if in_loop_threadsafe[i]: + for ri in range(len(rules)): + if in_loop_threadsafe[ri]: in_loop = True - if not update_threadsafe[i]: + if not update_threadsafe[ri]: update = False # Check for convergence after each timestep (perfect convergence or convergence specified by user) From a8d83ea026a30844967fbf52fdd34ba09392342e Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 11:42:57 -0700 Subject: [PATCH 14/15] fix: resolve Numba type-unification and recursion failures on Python 3.11+ Three separate issues prevented JIT compilation of reason() on Python 3.11+: 1. Recursion limit: Python 3.11+ bytecode generates ~2300 CFG nodes for the large reason() function; Numba's recursive _dfs_rec DFS exceeded Python's default limit of 1000. Fixed by raising the limit to 5000 in __init__.py before any JIT compilation is triggered. 2. Variable type conflict: loop variable 'i' was reused across the 567-line reason() function as both int64 (range/prange loops) and a complex Tuple (enumerate loops). Python 3.11+ bytecode creates phi nodes where Numba 0.65.x can no longer unify the types. Fixed by renaming integer loop variables: facts loops use 'fi', rules-parallel loop uses 'ri', list-comprehension indices use 'j'. Same change applied to interpretation_parallel.py to keep files in sync. 3. Actions versions: bump actions/checkout v4 -> v6, actions/setup-python v4 -> v5, astral-sh/setup-uv v3 -> v6 across CI workflows to clear Node.js 20 deprecation warnings. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/python-package-version-test.yml | 2 +- .github/workflows/python-publish.yml | 2 +- pyreason/__init__.py | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package-version-test.yml b/.github/workflows/python-package-version-test.yml index bd1695ee..85a37f68 100644 --- a/.github/workflows/python-package-version-test.yml +++ b/.github/workflows/python-package-version-test.yml @@ -19,7 +19,7 @@ jobs: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index bcfb2de8..f8c9ba9c 100755 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Python diff --git a/pyreason/__init__.py b/pyreason/__init__.py index 5c95ebf9..08a67d83 100755 --- a/pyreason/__init__.py +++ b/pyreason/__init__.py @@ -1,6 +1,11 @@ # ruff: noqa: F403 F405 (Ignore Pyreason import * for public api) # Set numba environment variable import os +import sys + +# Python 3.11+ bytecode generates ~2300 CFG nodes for the large JIT-compiled +# reasoning functions; Numba's recursive DFS exceeds the default limit of 1000. +sys.setrecursionlimit(max(sys.getrecursionlimit(), 5000)) package_path = os.path.abspath(os.path.dirname(__file__)) cache_path = os.path.join(package_path, 'cache') cache_status_path = os.path.join(package_path, '.cache_status.yaml') From c0cc5667848625b52fad7c1aa00fff33f3ef5b9b Mon Sep 17 00:00:00 2001 From: Caitlyn O'Hanna Date: Thu, 14 May 2026 14:16:41 -0700 Subject: [PATCH 15/15] fix: Python 3.11+ Numba compatibility in interpretation_fp.py; add Numba JIT cache - Replace numba.typed.List comprehensions with explicit loops in interpretation_fp.py to fix TypingError on Python 3.12+ - Rename integer loop variable 'i' to avoid type-unification conflicts with enumerate tuple 'i' in the @numba.njit reason() function - Add actions/cache@v4 to persist Numba JIT artifacts across CI runs, keyed on Python version + script content hash (per-function hash invalidation means only changed functions recompile on cache restore) - Remove timeout-minutes from CI job so long 3.11+ JIT runs can complete Co-Authored-By: Claude Sonnet 4.6 --- .../workflows/python-package-version-test.yml | 7 + .../interpretation/interpretation_fp.py | 139 +++++++++--------- 2 files changed, 80 insertions(+), 66 deletions(-) diff --git a/.github/workflows/python-package-version-test.yml b/.github/workflows/python-package-version-test.yml index 85a37f68..e4640689 100644 --- a/.github/workflows/python-package-version-test.yml +++ b/.github/workflows/python-package-version-test.yml @@ -20,6 +20,13 @@ jobs: steps: - uses: actions/checkout@v6 + - name: Cache Numba JIT artifacts + uses: actions/cache@v4 + with: + path: pyreason/cache + key: numba-${{ matrix.python-version }}-${{ hashFiles('pyreason/scripts/**/*.py') }} + restore-keys: | + numba-${{ matrix.python-version }}- - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: diff --git a/pyreason/scripts/interpretation/interpretation_fp.py b/pyreason/scripts/interpretation/interpretation_fp.py index 2cf174d0..fe03f7bf 100755 --- a/pyreason/scripts/interpretation/interpretation_fp.py +++ b/pyreason/scripts/interpretation/interpretation_fp.py @@ -360,9 +360,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi facts_to_be_applied_node_new.clear() facts_to_be_applied_node_trace_new.clear() nodes_set = set(nodes) - for i in range(len(facts_to_be_applied_node)): - if facts_to_be_applied_node[i][0] == t: - comp, l, bnd, static, graph_attribute = facts_to_be_applied_node[i][1], facts_to_be_applied_node[i][2], facts_to_be_applied_node[i][3], facts_to_be_applied_node[i][4], facts_to_be_applied_node[i][5] + for fi in range(len(facts_to_be_applied_node)): + if facts_to_be_applied_node[fi][0] == t: + comp, l, bnd, static, graph_attribute = facts_to_be_applied_node[fi][1], facts_to_be_applied_node[fi][2], facts_to_be_applied_node[fi][3], facts_to_be_applied_node[fi][4], facts_to_be_applied_node[fi][5] # If the component is not in the graph, add it if comp not in nodes_set: nodes_set.add(comp) @@ -375,27 +375,27 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi # Check if we should even store any of the changes to the rule trace etc. # Inverse of this is: if not save_graph_attributes_to_rule_trace and graph_attribute if (save_graph_attributes_to_rule_trace or not graph_attribute) and store_interpretation_changes: - meta_name = facts_to_be_applied_node_trace[i] if atom_trace else '' + meta_name = facts_to_be_applied_node_trace[fi] if atom_trace else '' rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, l, bnd, True, 'Fact', meta_name, '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_node_trace[fi]) for p1, p2 in ipl: if p1==l: rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p2, interpretations_node[t][comp].world[p2], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[t][comp].world[p2], facts_to_be_applied_node_trace[i]) + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[t][comp].world[p2], facts_to_be_applied_node_trace[fi]) elif p2==l: rule_trace_node.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p1, interpretations_node[t][comp].world[p1], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[t][comp].world[p1], facts_to_be_applied_node_trace[i]) - + _update_rule_trace(rule_trace_node_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_node[t][comp].world[p1], facts_to_be_applied_node_trace[fi]) + else: # Check for inconsistencies (multiple facts) if check_consistent_node(interpretations_node[t], comp, (l, bnd)): mode = 'graph-attribute-fact' if graph_attribute else 'fact' override = True if update_mode == 'override' else False - u, changes = _update_node(interpretations_node[t], predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, i, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, mode=mode, override=override) - + u, changes = _update_node(interpretations_node[t], predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, fi, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, mode=mode, override=override) + update = u or update if update: max_t_changes = max(max_t_changes, t) @@ -408,10 +408,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi else: mode = 'graph-attribute-fact' if graph_attribute else 'fact' if inconsistency_check: - resolve_inconsistency_node(interpretations_node[t], comp, (l, bnd), ipl, t, fp_cnt, i, atom_trace, rule_trace_node, rule_trace_node_atoms, rules_to_be_applied_node_trace, facts_to_be_applied_node_trace, store_interpretation_changes, mode=mode) + resolve_inconsistency_node(interpretations_node[t], comp, (l, bnd), ipl, t, fp_cnt, fi, atom_trace, rule_trace_node, rule_trace_node_atoms, rules_to_be_applied_node_trace, facts_to_be_applied_node_trace, store_interpretation_changes, mode=mode) else: - u, changes = _update_node(interpretations_node[t], predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, i, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, mode=mode, override=True) - + u, changes = _update_node(interpretations_node[t], predicate_map_node, comp, (l, bnd), ipl, rule_trace_node, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_node_trace, fi, facts_to_be_applied_node_trace, rule_trace_node_atoms, store_interpretation_changes, mode=mode, override=True) + update = u or update if update: max_t_changes = max(max_t_changes, t) @@ -420,17 +420,17 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bound_delta = max(bound_delta, changes) else: changes_cnt += changes - + if static: - facts_to_be_applied_node_new.append((numba.types.uint16(facts_to_be_applied_node[i][0]+1), comp, l, bnd, static, graph_attribute)) + facts_to_be_applied_node_new.append((numba.types.uint16(facts_to_be_applied_node[fi][0]+1), comp, l, bnd, static, graph_attribute)) if atom_trace: - facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[i]) - + facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[fi]) + # If time doesn't match, fact to be applied later else: - facts_to_be_applied_node_new.append(facts_to_be_applied_node[i]) + facts_to_be_applied_node_new.append(facts_to_be_applied_node[fi]) if atom_trace: - facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[i]) + facts_to_be_applied_node_trace_new.append(facts_to_be_applied_node_trace[fi]) # Update list of facts with ones that have not been applied yet (delete applied facts) facts_to_be_applied_node[:] = facts_to_be_applied_node_new.copy() @@ -443,40 +443,40 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi facts_to_be_applied_edge_new.clear() facts_to_be_applied_edge_trace_new.clear() edges_set = set(edges) - for i in range(len(facts_to_be_applied_edge)): - if facts_to_be_applied_edge[i][0] == t: - comp, l, bnd, static, graph_attribute = facts_to_be_applied_edge[i][1], facts_to_be_applied_edge[i][2], facts_to_be_applied_edge[i][3], facts_to_be_applied_edge[i][4], facts_to_be_applied_edge[i][5] + for fi in range(len(facts_to_be_applied_edge)): + if facts_to_be_applied_edge[fi][0] == t: + comp, l, bnd, static, graph_attribute = facts_to_be_applied_edge[fi][1], facts_to_be_applied_edge[fi][2], facts_to_be_applied_edge[fi][3], facts_to_be_applied_edge[fi][4], facts_to_be_applied_edge[fi][5] # If the component is not in the graph, add it if comp not in edges_set: _add_edge(comp[0], comp[1], neighbors, reverse_neighbors, nodes, edges, label.Label(''), interpretations_node[t], interpretations_edge[t], predicate_map_edge, t) edges_set.add(comp) elif comp not in interpretations_edge[t]: _add_edge_to_interpretation(comp, interpretations_edge[t]) - + # Check if bnd is static. Then no need to update, just add to rule trace, check if graph attribute, and add ipl complement to rule trace as well if l in interpretations_edge[t][comp].world and interpretations_edge[t][comp].world[l].is_static(): # Inverse of this is: if not save_graph_attributes_to_rule_trace and graph_attribute if (save_graph_attributes_to_rule_trace or not graph_attribute) and store_interpretation_changes: - meta_name = facts_to_be_applied_edge_trace[i] if atom_trace else '' + meta_name = facts_to_be_applied_edge_trace[fi] if atom_trace else '' rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, l, interpretations_edge[t][comp].world[l], True, 'Fact', meta_name, '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), bnd, facts_to_be_applied_edge_trace[fi]) for p1, p2 in ipl: if p1 == l: rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p2, interpretations_edge[t][comp].world[p2], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[t][comp].world[p2], facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[t][comp].world[p2], facts_to_be_applied_edge_trace[fi]) elif p2 == l: rule_trace_edge.append((numba.types.uint16(t), numba.types.uint16(fp_cnt), comp, p1, interpretations_edge[t][comp].world[p1], True, 'IPL', f'IPL: {l.get_value()}', '')) if atom_trace: - _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[t][comp].world[p1], facts_to_be_applied_edge_trace[i]) + _update_rule_trace(rule_trace_edge_atoms, numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)), numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)), interpretations_edge[t][comp].world[p1], facts_to_be_applied_edge_trace[fi]) else: # Check for inconsistencies if check_consistent_edge(interpretations_edge[t], comp, (l, bnd)): mode = 'graph-attribute-fact' if graph_attribute else 'fact' override = True if update_mode == 'override' else False - u, changes = _update_edge(interpretations_edge[t], predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, i, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, mode=mode, override=override) - + u, changes = _update_edge(interpretations_edge[t], predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, fi, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, mode=mode, override=override) + update = u or update if update: max_t_changes = max(max_t_changes, t) @@ -489,10 +489,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi else: mode = 'graph-attribute-fact' if graph_attribute else 'fact' if inconsistency_check: - resolve_inconsistency_edge(interpretations_edge[t], comp, (l, bnd), ipl, t, fp_cnt, i, atom_trace, rule_trace_edge, rule_trace_edge_atoms, rules_to_be_applied_edge_trace, facts_to_be_applied_edge_trace, store_interpretation_changes, mode=mode) + resolve_inconsistency_edge(interpretations_edge[t], comp, (l, bnd), ipl, t, fp_cnt, fi, atom_trace, rule_trace_edge, rule_trace_edge_atoms, rules_to_be_applied_edge_trace, facts_to_be_applied_edge_trace, store_interpretation_changes, mode=mode) else: - u, changes = _update_edge(interpretations_edge[t], predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, i, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, mode=mode, override=True) - + u, changes = _update_edge(interpretations_edge[t], predicate_map_edge, comp, (l, bnd), ipl, rule_trace_edge, fp_cnt, t, static, convergence_mode, atom_trace, save_graph_attributes_to_rule_trace, rules_to_be_applied_edge_trace, fi, facts_to_be_applied_edge_trace, rule_trace_edge_atoms, store_interpretation_changes, mode=mode, override=True) + update = u or update if update: max_t_changes = max(max_t_changes, t) @@ -501,17 +501,17 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bound_delta = max(bound_delta, changes) else: changes_cnt += changes - + if static: - facts_to_be_applied_edge_new.append((numba.types.uint16(facts_to_be_applied_edge[i][0]+1), comp, l, bnd, static, graph_attribute)) + facts_to_be_applied_edge_new.append((numba.types.uint16(facts_to_be_applied_edge[fi][0]+1), comp, l, bnd, static, graph_attribute)) if atom_trace: - facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[i]) - + facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[fi]) + # Time doesn't match, fact to be applied later else: - facts_to_be_applied_edge_new.append(facts_to_be_applied_edge[i]) + facts_to_be_applied_edge_new.append(facts_to_be_applied_edge[fi]) if atom_trace: - facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[i]) + facts_to_be_applied_edge_trace_new.append(facts_to_be_applied_edge_trace[fi]) # Update list of facts with ones that have not been applied yet (delete applied facts) facts_to_be_applied_edge[:] = facts_to_be_applied_edge_new.copy() @@ -528,13 +528,13 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_be_applied_edge_trace_threadsafe = numba.typed.List([numba.typed.List.empty_list(rules_to_be_applied_trace_type) for _ in range(len(rules))]) edges_to_be_added_edge_rule_threadsafe = numba.typed.List([numba.typed.List.empty_list(edges_to_be_added_type) for _ in range(len(rules))]) - for i in prange(len(rules)): - rule = rules[i] + for ri in prange(len(rules)): + rule = rules[ri] # Only go through if the rule can be applied within the given timesteps, or we're running until convergence delta_t = rule.get_delta() if t + delta_t <= tmax or tmax == -1 or again: - applicable_node_rules, applicable_edge_rules = _ground_rule(rule, interpretations_node[t], interpretations_edge[t], predicate_map_node, predicate_map_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, extended_ann_fn_flags[i], allow_ground_rules, t, head_functions, closed_world_predicates) + applicable_node_rules, applicable_edge_rules = _ground_rule(rule, interpretations_node[t], interpretations_edge[t], predicate_map_node, predicate_map_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, extended_ann_fn_flags[ri], allow_ground_rules, t, head_functions, closed_world_predicates) # Loop through applicable rules and add them to the rules to be applied for later or next fp operation for applicable_rule in applicable_node_rules: @@ -567,9 +567,9 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bnd_u = min(max(bnd[1], 0), 1) bnd = interval.closed(bnd_l, bnd_u) max_rules_time = max(max_rules_time, t + delta_t) - rules_to_be_applied_node_threadsafe[i].append((numba.types.uint16(t + delta_t), n, rule.get_target(), bnd, rule.is_static_rule())) + rules_to_be_applied_node_threadsafe[ri].append((numba.types.uint16(t + delta_t), n, rule.get_target(), bnd, rule.is_static_rule())) if atom_trace: - rules_to_be_applied_node_trace_threadsafe[i].append((qualified_nodes, qualified_edges, rule.get_name())) + rules_to_be_applied_node_trace_threadsafe[ri].append((qualified_nodes, qualified_edges, rule.get_name())) # If delta_t is zero we apply the rules and check if more are applicable if delta_t == 0: @@ -610,29 +610,29 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi bnd = interval.closed(bnd_l, bnd_u) max_rules_time = max(max_rules_time, t+delta_t) # edges_to_be_added_edge_rule.append(edges_to_add) - edges_to_be_added_edge_rule_threadsafe[i].append(edges_to_add) - rules_to_be_applied_edge_threadsafe[i].append((numba.types.uint16(t+delta_t), e, rule.get_target(), bnd, rule.is_static_rule())) + edges_to_be_added_edge_rule_threadsafe[ri].append(edges_to_add) + rules_to_be_applied_edge_threadsafe[ri].append((numba.types.uint16(t+delta_t), e, rule.get_target(), bnd, rule.is_static_rule())) if atom_trace: # rules_to_be_applied_edge_trace.append((qualified_nodes, qualified_edges, rule.get_name())) - rules_to_be_applied_edge_trace_threadsafe[i].append((qualified_nodes, qualified_edges, rule.get_name())) + rules_to_be_applied_edge_trace_threadsafe[ri].append((qualified_nodes, qualified_edges, rule.get_name())) # If delta_t is zero we apply the rules and check if more are applicable if delta_t == 0: update = False # Update lists after parallel run - for i in range(len(rules)): - if len(rules_to_be_applied_node_threadsafe[i]) > 0: - rules_to_be_applied_node.extend(rules_to_be_applied_node_threadsafe[i]) - if len(rules_to_be_applied_edge_threadsafe[i]) > 0: - rules_to_be_applied_edge.extend(rules_to_be_applied_edge_threadsafe[i]) + for ri in range(len(rules)): + if len(rules_to_be_applied_node_threadsafe[ri]) > 0: + rules_to_be_applied_node.extend(rules_to_be_applied_node_threadsafe[ri]) + if len(rules_to_be_applied_edge_threadsafe[ri]) > 0: + rules_to_be_applied_edge.extend(rules_to_be_applied_edge_threadsafe[ri]) if atom_trace: - if len(rules_to_be_applied_node_trace_threadsafe[i]) > 0: - rules_to_be_applied_node_trace.extend(rules_to_be_applied_node_trace_threadsafe[i]) - if len(rules_to_be_applied_edge_trace_threadsafe[i]) > 0: - rules_to_be_applied_edge_trace.extend(rules_to_be_applied_edge_trace_threadsafe[i]) - if len(edges_to_be_added_edge_rule_threadsafe[i]) > 0: - edges_to_be_added_edge_rule.extend(edges_to_be_added_edge_rule_threadsafe[i]) + if len(rules_to_be_applied_node_trace_threadsafe[ri]) > 0: + rules_to_be_applied_node_trace.extend(rules_to_be_applied_node_trace_threadsafe[ri]) + if len(rules_to_be_applied_edge_trace_threadsafe[ri]) > 0: + rules_to_be_applied_edge_trace.extend(rules_to_be_applied_edge_trace_threadsafe[ri]) + if len(edges_to_be_added_edge_rule_threadsafe[ri]) > 0: + edges_to_be_added_edge_rule.extend(edges_to_be_added_edge_rule_threadsafe[ri]) # Increment t, update number of ground atoms t += 1 @@ -682,10 +682,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_remove_idx.add(idx) # Remove from rules to be applied and edges to be applied lists after coming out from loop - rules_to_be_applied_node[:] = numba.typed.List([rules_to_be_applied_node[i] for i in range(len(rules_to_be_applied_node)) if i not in rules_to_remove_idx]) - edges_to_be_added_node_rule[:] = numba.typed.List([edges_to_be_added_node_rule[i] for i in range(len(edges_to_be_added_node_rule)) if i not in rules_to_remove_idx]) + rules_to_be_applied_node[:] = numba.typed.List([rules_to_be_applied_node[j] for j in range(len(rules_to_be_applied_node)) if j not in rules_to_remove_idx]) + edges_to_be_added_node_rule[:] = numba.typed.List([edges_to_be_added_node_rule[j] for j in range(len(edges_to_be_added_node_rule)) if j not in rules_to_remove_idx]) if atom_trace: - rules_to_be_applied_node_trace[:] = numba.typed.List([rules_to_be_applied_node_trace[i] for i in range(len(rules_to_be_applied_node_trace)) if i not in rules_to_remove_idx]) + rules_to_be_applied_node_trace[:] = numba.typed.List([rules_to_be_applied_node_trace[j] for j in range(len(rules_to_be_applied_node_trace)) if j not in rules_to_remove_idx]) # Edges rules_to_remove_idx.clear() @@ -766,10 +766,10 @@ def reason(interpretations_node, interpretations_edge, predicate_map_node, predi rules_to_remove_idx.add(idx) # Remove from rules to be applied and edges to be applied lists after coming out from loop - rules_to_be_applied_edge[:] = numba.typed.List([rules_to_be_applied_edge[i] for i in range(len(rules_to_be_applied_edge)) if i not in rules_to_remove_idx]) - edges_to_be_added_edge_rule[:] = numba.typed.List([edges_to_be_added_edge_rule[i] for i in range(len(edges_to_be_added_edge_rule)) if i not in rules_to_remove_idx]) + rules_to_be_applied_edge[:] = numba.typed.List([rules_to_be_applied_edge[j] for j in range(len(rules_to_be_applied_edge)) if j not in rules_to_remove_idx]) + edges_to_be_added_edge_rule[:] = numba.typed.List([edges_to_be_added_edge_rule[j] for j in range(len(edges_to_be_added_edge_rule)) if j not in rules_to_remove_idx]) if atom_trace: - rules_to_be_applied_edge_trace[:] = numba.typed.List([rules_to_be_applied_edge_trace[i] for i in range(len(rules_to_be_applied_edge_trace)) if i not in rules_to_remove_idx]) + rules_to_be_applied_edge_trace[:] = numba.typed.List([rules_to_be_applied_edge_trace[j] for j in range(len(rules_to_be_applied_edge_trace)) if j not in rules_to_remove_idx]) # Check for convergence after each timestep (perfect convergence or convergence specified by user) # Check number of changed interpretations or max bound change @@ -1543,14 +1543,18 @@ def get_rule_edge_clause_grounding(clause_var_1, clause_var_2, groundings, groun # We replace Y by the sources of Z elif clause_var_1 not in groundings and clause_var_2 in groundings: for n in groundings[clause_var_2]: - es = numba.typed.List([(nn, n) for nn in reverse_neighbors[n]]) + es = numba.typed.List.empty_list(edge_type) + for nn in reverse_neighbors[n]: + es.append((nn, n)) edge_groundings.extend(es) # Case 3: # We replace Z by the neighbors of Y elif clause_var_1 in groundings and clause_var_2 not in groundings: for n in groundings[clause_var_1]: - es = numba.typed.List([(n, nn) for nn in neighbors[n]]) + es = numba.typed.List.empty_list(edge_type) + for nn in neighbors[n]: + es.append((n, nn)) edge_groundings.extend(es) # Case 4: @@ -1563,7 +1567,10 @@ def get_rule_edge_clause_grounding(clause_var_1, clause_var_2, groundings, groun else: groundings_clause_var_2_set = set(groundings[clause_var_2]) for n in groundings[clause_var_1]: - es = numba.typed.List([(n, nn) for nn in neighbors[n] if nn in groundings_clause_var_2_set]) + es = numba.typed.List.empty_list(edge_type) + for nn in neighbors[n]: + if nn in groundings_clause_var_2_set: + es.append((n, nn)) edge_groundings.extend(es) return edge_groundings