From d706321d1e8af496632c8194000353fde96d26ed Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Thu, 8 Jun 2023 22:11:05 -0700 Subject: [PATCH 01/34] Add docstrings --- python/hyperon/atoms.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 68cf13caa..87965f41c 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -1,3 +1,7 @@ +""" +The Python wrapper for Hyperon Atom Rust types +""" + import hyperonpy as hp from hyperonpy import AtomKind from typing import Union @@ -20,6 +24,7 @@ def __repr__(self): return hp.atom_to_str(self.catom) def get_type(self): + """Gets the type of this Atom""" return hp.atom_get_type(self.catom) def iterate(self): @@ -34,6 +39,7 @@ def match_atom(self, b): @staticmethod def _from_catom(catom): + """Constructs an Atom from C Atom of the same type""" type = hp.atom_get_type(catom) if type == AtomKind.SYMBOL: return SymbolAtom(catom) @@ -47,6 +53,8 @@ def _from_catom(catom): raise Exception("Unexpected type of the atom: " + str(type)) class SymbolAtom(Atom): + """Symbol Atom represents a single concepts which is identified by name. Two symbols + which have the same name reference the same concept.""" def __init__(self, catom): super().__init__(catom) @@ -58,6 +66,9 @@ def S(name): return SymbolAtom(hp.atom_sym(name)) class VariableAtom(Atom): + """ + Variable Atom represents a variable like a variable in the pattern. + """ def __init__(self, catom): super().__init__(catom) @@ -69,18 +80,21 @@ def V(name): return VariableAtom(hp.atom_var(name)) class ExpressionAtom(Atom): + """Expression Atom combines other kinds of atoms including expressions themselves.""" def __init__(self, catom): super().__init__(catom) def get_children(self): - return [Atom._from_catom(catom) for catom in - hp.atom_get_children(self.catom)] + """Gets all of the children Atoms""" + return [Atom._from_catom(catom) for catom in hp.atom_get_children(self.catom)] + def E(*args): return ExpressionAtom(hp.atom_expr([atom.catom for atom in args])) class AtomType: + """Defines all the types of Atom""" UNDEFINED = Atom._from_catom(hp.CAtomType.UNDEFINED) TYPE = Atom._from_catom(hp.CAtomType.TYPE) @@ -92,6 +106,15 @@ class AtomType: GROUNDED_SPACE = Atom._from_catom(hp.CAtomType.GROUNDED_SPACE) class GroundedAtom(Atom): + """Grounded Atom represents sub-symbolic knowledge. On the API level it allows + keeping data and behaviour inside an atom. There are three aspects of the grounded + atom which can be customized: + + - the type of grounded atom is provided by the atom itself; + - matching algorithm of the atom can be modified by the user; + - atom can be made executable; such atom can be used to apply some sub-symbolic + operations to other atoms as arguments. + """ def __init__(self, catom): super().__init__(catom) From 95851dd4e8eb7c0b60ead46999a61ebae5308c28 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Sun, 11 Jun 2023 22:34:19 -0700 Subject: [PATCH 02/34] Add documentation files --- .github/workflows/docs.yaml | 23 ++++++++++++++++ docs/index.md | 2 ++ mkdocs.yml | 53 +++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 .github/workflows/docs.yaml create mode 100644 docs/index.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 000000000..2000fc393 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,23 @@ +name: docs +on: + push: + branches: + - master + - main +permissions: + contents: write +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-versoin: 3.x + - uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..0d10b6f46 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,2 @@ +# Homepage + diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..2706fa95e --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,53 @@ +site_name: Hyperon +theme: + name: material + features: + - navigation.tabs + - navigation.sections + - toc.integrate + - navigation.top + - search.suggest + - search.highlight + - content.tabs.link + - content.code.annotation + - content.code.copy + language: en + palette: + - scheme: default + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + primary: teal + accent: purple + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + primary: teal + accent: lime + +extra: + social: + - icon: fontawesome/brands/github-alt + link: https://github.com/trueagi-io/hyperon-experimental + - icon: fontawesome/brands/twitter + link: https://twitter.com/OpenCog + +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - admonition + - pymdownx.arithmatex: + generic: true + - footnotes + - pymdownx.superfences + - pymdownx.mark + - attr_list + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + +copyright: | + © 2023 OpenCog Hyperon From 4b34598bd9fe75ef6d3b8cb2748d3fffbc8125ec Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Mon, 12 Jun 2023 22:05:13 -0700 Subject: [PATCH 03/34] Update docs --- docs/assets/P8T2_ASO_400x400.jpg | Bin 0 -> 10511 bytes mkdocs.yml | 94 +++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 docs/assets/P8T2_ASO_400x400.jpg diff --git a/docs/assets/P8T2_ASO_400x400.jpg b/docs/assets/P8T2_ASO_400x400.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad286b93ec3730b9c5bb65c55a8aab275be89361 GIT binary patch literal 10511 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 CMKnzS literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index 2706fa95e..f26d5e54f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,16 +1,36 @@ -site_name: Hyperon +site_name: OpenCog Hyperon +repo_name: trueagi-io/hyperon-experimental +repo_url: https://github.com/trueagi-io/hyperon-experimental + +copyright: | + © 2023 OpenCog Hyperon + theme: name: material features: - - navigation.tabs + - announce.dismiss + - content.action.edit + - content.action.view + - content.code.annotate + - content.code.copy + # - content.tabs.link + - content.tooltips + # - header.autohide + # - navigation.expand + - navigation.footer + - navigation.indexes + # - navigation.instant + # - navigation.prune - navigation.sections - - toc.integrate + - navigation.tabs + # - navigation.tabs.sticky - navigation.top - - search.suggest + - navigation.tracking - search.highlight - - content.tabs.link - - content.code.annotation - - content.code.copy + - search.share + - search.suggest + - toc.follow + # - toc.integrate language: en palette: - scheme: default @@ -25,6 +45,19 @@ theme: name: Switch to light mode primary: teal accent: lime + font: + text: Roboto + code: Roboto Mono + favicon: assets/P8T2_ASO_400x400.jpg + icon: + logo: logo + +plugins: + - search: + separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' + - minify: + minify_html: true + - mkdocstrings extra: social: @@ -34,20 +67,47 @@ extra: link: https://twitter.com/OpenCog markdown_extensions: - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.inlinehilite - pymdownx.snippets + - abbr - admonition + - attr_list + - def_list + - footnotes + - md_in_html + - toc: + permalink: true - pymdownx.arithmatex: generic: true - - footnotes - - pymdownx.superfences - - pymdownx.mark - - attr_list + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:materialx.emoji.twemoji + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + repo_url_shorthand: true + user: squidfunk + repo: mkdocs-material + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde -copyright: | - © 2023 OpenCog Hyperon +# Page tree +nav: + - Home: index.md From f45acfdff5160630b94c74363cc2fec376c31266 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Mon, 12 Jun 2023 23:23:11 -0700 Subject: [PATCH 04/34] Generate docstring docs --- docs/reference/atoms.md | 2 ++ docs/reference/base.md | 2 ++ docs/reference/ext.md | 2 ++ docs/reference/runner.md | 2 ++ docs/reference/stdlib.md | 2 ++ docs/requirements.txt | 2 ++ mkdocs.yml | 14 +++++++++++++- 7 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 docs/reference/atoms.md create mode 100644 docs/reference/base.md create mode 100644 docs/reference/ext.md create mode 100644 docs/reference/runner.md create mode 100644 docs/reference/stdlib.md create mode 100644 docs/requirements.txt diff --git a/docs/reference/atoms.md b/docs/reference/atoms.md new file mode 100644 index 000000000..f8b3dce74 --- /dev/null +++ b/docs/reference/atoms.md @@ -0,0 +1,2 @@ +# Atoms module +::: hyperon.atoms diff --git a/docs/reference/base.md b/docs/reference/base.md new file mode 100644 index 000000000..19ecb5f8c --- /dev/null +++ b/docs/reference/base.md @@ -0,0 +1,2 @@ +# Base module +::: hyperon.base diff --git a/docs/reference/ext.md b/docs/reference/ext.md new file mode 100644 index 000000000..203304d5e --- /dev/null +++ b/docs/reference/ext.md @@ -0,0 +1,2 @@ +# Ext module +::: hyperon.ext diff --git a/docs/reference/runner.md b/docs/reference/runner.md new file mode 100644 index 000000000..0ae702750 --- /dev/null +++ b/docs/reference/runner.md @@ -0,0 +1,2 @@ +# Runner module +::: hyperon.runner diff --git a/docs/reference/stdlib.md b/docs/reference/stdlib.md new file mode 100644 index 000000000..e50da0fae --- /dev/null +++ b/docs/reference/stdlib.md @@ -0,0 +1,2 @@ +# Stdlib module +::: hyperon.stdlib diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..760318bfc --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +mkdocs-minify-plugin +mkdocstrings[python] diff --git a/mkdocs.yml b/mkdocs.yml index f26d5e54f..5ad00c19e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -57,7 +57,13 @@ plugins: separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' - minify: minify_html: true - - mkdocstrings + - mkdocstrings: + handlers: + python: + paths: + - python + - autorefs + extra: social: @@ -111,3 +117,9 @@ markdown_extensions: # Page tree nav: - Home: index.md + - Reference: + - Atoms: reference/atoms.md + - Base: reference/base.md + - Ext: reference/ext.md + - Runner: reference/runner.md + - Stdlib: reference/stdlib.md From 28729a8f1b896b79b2f0f7ed4fbde27632c96f29 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Mon, 12 Jun 2023 23:26:26 -0700 Subject: [PATCH 05/34] Update docs workflow --- .github/workflows/docs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 2000fc393..fcafe2a04 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -4,6 +4,7 @@ on: branches: - master - main + - docs-py permissions: contents: write jobs: From 3f438c32b4508bf591bb83c5805a1c888732b850 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Mon, 12 Jun 2023 23:27:47 -0700 Subject: [PATCH 06/34] Install deps --- .github/workflows/docs.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index fcafe2a04..81f005f50 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -19,6 +19,5 @@ jobs: with: key: ${{ github.ref }} path: .cache - - run: pip install mkdocs-material + - run: pip install mkdocs-material mkdocs-minify-plugin mkdocstrings[python] - run: mkdocs gh-deploy --force - From 20b11e9fb4fd0e420b6e285000aae994274e143c Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Fri, 14 Jul 2023 09:20:10 -0700 Subject: [PATCH 07/34] Update docs workflow --- .github/workflows/docs.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 81f005f50..5086e8b9d 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -1,9 +1,7 @@ -name: docs +name: build docs on: push: branches: - - master - - main - docs-py permissions: contents: write From a8e5d9b40a4c94e1ce052fb40831e25a07e3797a Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Sun, 23 Jul 2023 21:38:53 -0700 Subject: [PATCH 08/34] Create home page --- docs/CONTRIBUTING.md | 70 ++++++++++ docs/assets/structure.plantuml | 20 +++ docs/assets/structure.svg | 2 + docs/index.md | 233 ++++++++++++++++++++++++++++++++- 4 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/assets/structure.plantuml create mode 100644 docs/assets/structure.svg diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 000000000..20040d0e3 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,70 @@ +# How to contribute + +## Making changes + +Before making changes create a personal fork of the repository. Sync fork and +create new branch from the latest version of the `main` branch. Create separate +branch for each change. Thus it is simpler to support consistent state of the +`main` in your fork. + +Prefer incremental commits to one big commit which contains the whole change. +Prefer commits which passes all of the tests. If it is not possible to satisfy +both requirements at once you can make single commit which passes tests or mark +tests as ignored until change is done. + +Each commit should have a message in the following format: +``` +Change summary in 50 characters (less than 73 characters) + +Optional detailed description of the change when required. Each line +is less than 73 character long. +``` +Such commits looks better in GitHub history. + +Please avoid generic commit messages like `Update README.md`. +Good commit message should describe the change, not a fact of the change. +For example `Add troubleshooting section about No module named 'hyperonpy'`. +By looking to the commit message history the reviewer should understand +the order and brief description of changes. + +Please don't include number and description of the issue into a commit summary +line. Use `Fixes #` in the pull request description instead +to link the PR and the issue. + +PR should satisfy the following requirement before being merged: +- contain latest changes from the repo; +- pass tests; +- be reviewed. + +Feel free to raise draft PR if you need an advice or help with your changes. + +## Code style + +We have small set of code style rules for now. The rule of thumb is to take a look +at the existing code and stick to its style. + +### General + +If you want to leave some reminder in code, for example to fix something later, +you can do it by two ways. Add a comment starting with `FIXME` to mark something +which should be done before the PR is merged. Add a comment starting with `TODO` +to mark the improvement which can be postponed and done later by a separate PR. +The main purpose of a `TODO` comment is to trigger a developer who looks at the +code after you and make him fix the issue if it is appropriate. If the change or +question is big enough or it affects the API of the module it is better to raise +an issue instead. + +### Libraries + +When adding new library into the project please ensure you specify the exact +version instead of using ranges. The minor update of the library can break the +build unexpectedly. The broken build is a real burden because most of the users +build the project from the source. + +### Rust + +When working on Rust C API prefer making `unsafe` blocks as small as possible. +This makes it easier to find blocks that might be source of issues. Usually it is +not required to mark C API functions `unsafe` because they are not intended to +be used from the Rust safe code. + diff --git a/docs/assets/structure.plantuml b/docs/assets/structure.plantuml new file mode 100644 index 000000000..6425e8546 --- /dev/null +++ b/docs/assets/structure.plantuml @@ -0,0 +1,20 @@ +@startuml + +package "Rust ./lib/*" { + [libhyperon.rlib] as libhyperon +} + +package "Rust, C ./c/*" { + [libhyperonc.so\nlibhyperon.a\nhyperon/hyperon.h] as libhyperonc +} + +package "Python ./python/*" { + [libhyperonpy.so\n./python/libhyperon.cpp] as libhyperonpy + [hyperon\n./python/hyperon/*] as hyperonpy +} + +libhyperonc -> libhyperon +libhyperonpy -u-> libhyperonc +hyperonpy -u-> libhyperonpy + +@enduml diff --git a/docs/assets/structure.svg b/docs/assets/structure.svg new file mode 100644 index 000000000..84e686494 --- /dev/null +++ b/docs/assets/structure.svg @@ -0,0 +1,2 @@ + +Rust ./lib/*Rust, C ./c/*Python ./python/*libhyperon.rliblibhyperonc.solibhyperon.ahyperon/hyperon.hlibhyperonpy.so./python/libhyperon.cpphyperon./python/hyperon/* \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 0d10b6f46..d5582ce41 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,2 +1,233 @@ -# Homepage +![CI](https://github.com/trueagi-io/hyperon-experimental/actions/workflows/ci.yml/badge.svg) + +# Overview + +OpenCog Hyperon is a substantially revised, novel version of OpenCog - which is currently at an active +pre-alpha stage of development and experimentation. One of the focuses in the Hyperon design is a successor +to the OpenCog Classic Atomese language with clear semantics supporting meta-language features, +different types of inference, etc. What we have landed on is an "Atomese 2" language called MeTTa (Meta Type Talk). + +In order to get familiar with MeTTa one can read [MeTTa specification](https://wiki.opencog.org/w/File:MeTTa_Specification.pdf) +and watch video with different [MeTTa example explained](https://singularitynet.zoom.us/rec/share/VqHmU37XtbS7VnKY474tkTvvTglsgOIfsI-21MXWxVm_in7U3tGPcfjjiE0P_15R.yUwPdCzEONSUx1EL?startTime=1650636238000). +The examples of MeTTa programs can be found in [./python/tests/scripts](./python/tests/scripts) directory. +Please look at the [Python unit tests](./python/tests) to understand how one can use MeTTa from Python. +More complex usage scenarios are located at [MeTTa examples repo](https://github.com/trueagi-io/metta-examples). +A lot of different materials can be found on [OpenCog wiki server](https://wiki.opencog.org/w/Hyperon). + +If you want to contribute the project please see the [contribution guide](CONTRIBUTING.md) first. +If you find troubles with the installation, see the [Troubleshooting](#troubleshooting) section below. + +# Prepare environment + +## Docker + +A docker image can be used as a ready to run environment. + +Build docker image running: +``` +docker build -t trueagi/hyperon https://raw.githubusercontent.com/trueagi-io/hyperon-experimental/main/Dockerfile +``` + +Run the image: +``` +docker run --rm -ti trueagi/hyperon +``` + +Resulting container contains the latest code from the repo compiled and ready +to run. If the docker image doesn't work, please raise an +[issue](https://github.com/trueagi-io/hyperon-experimental/issues). + +## Manual installation + +### Prerequisites + +* Install latest stable Rust (1.63 or later), see [Rust installation +page](https://www.rust-lang.org/tools/install). Make sure your +`PATH` variable includes `$HOME/.cargo/bin` directory after installing +Rust (see the Notes at the installation page). + + Requirements for building C and Python API + * Python3 and Python3-dev (3.7 or later) + * GCC (7.5 or later) + * CMake (3.10 or later) + +* Install cbindgen: +``` +cargo install --force cbindgen +``` + +* Install Conan and make default Conan profile: +``` +python3 -m pip install conan==1.57 +conan profile new --detect default +``` + +* Install Python library and dependencies in development mode (execute following command in the top directory of repository): +``` +python3 -m pip install -e ./python[dev] +``` + +# Build and run + +## Hyperon library + +Build and test the library: +``` +cd ./lib +cargo build +cargo test +``` + +Run examples: +``` +cargo run --example sorted_list +``` + +To enable logging during running tests or examples export `RUST_LOG` +environment variable: +``` +RUST_LOG=hyperon=debug cargo test +``` + +Running benchmarks requires nightly toolchain so they can be run using: +``` +cargo +nightly bench +``` + +Generate docs: +``` +cd ./lib +cargo doc --no-deps +``` +Docs can be found at `./lib/target/doc/hyperon/index.html`. + +## C and Python API + +Setup build: +``` +mkdir -p build +cd build +cmake .. +``` +To run release build use `-DCMAKE_BUILD_TYPE=Release` cmake flag. + +Build and run tests: +``` +make +make check +``` + +## Running Python and MeTTa examples from command line + +In order to run examples you need to add Python libraries into the `PYTHONPATH` +after compilation: +``` +cd build +export PYTHONPATH=$PYTHONPATH:`pwd`/python +``` + +Run MeTTa script from command line: +``` +cd python/tests +python3 metta.py ./scripts/.metta +``` + +## Troubleshooting + +### Conan claims it cannot find out the version of the C compiler + +If you see the following `cmake` output: +``` +ERROR: Not able to automatically detect '/usr/bin/cc' version +ERROR: Unable to find a working compiler +WARN: Remotes registry file missing, creating default one in /root/.conan/remotes.json +ERROR: libcheck/0.15.2: 'settings.compiler' value not defined +``` +Try to create the default Conan profile manually: +``` +conan profile new --detect default +``` +If it doesn't help, then try to manually add `compiler`, `compiler.version` and +`compiler.libcxx` values in the default Conan profile +(`~/.conan/profiles/default`). +For example: +``` +conan profile update settings.compiler=gcc default +conan profile update settings.compiler.version=7 default +conan profile update settings.compiler.libcxx=libstdc++ default +``` + +### Rust compiler shows errors + +Please ensure you are using the latest stable version: +``` +rustup update stable +``` + +### Importing hyperon Python module fails + +If importing the hyperon module in Python + +```python +import hyperon +``` + +returns the error + +``` +ModuleNotFoundError: No module named 'hyperonpy' +``` + +it likely means that `PYTHONPATH` is set incorrectly. Make sure it +points to `build/python` not just `python`. The path should typically +look like + +``` +/home///build/python +``` + +# Development + +## Structure of the codebase + +Main library `libhyperon.rlib` is written in Rust language, it contains core +API which can be used from other Rust projects. Source code of the library is +located under [./lib](./lib) directory. It is a plain Rust project which can be +built and tested using Cargo tool. + +In order to provide API for platforms and languages other than Rust there is a +C API export library `libhyperonc`. Source code of the library is located under +[./c](./c) directory. The library contains Rust C API bindings and depends on +`libhyperon.rlib` library. Native library is compiled using Cargo, C headers +are generated using cbindgen tool. + +Source code of the Python integration library is located under +[./python](./python) directory. It contains two main parts. First part is a +native Python library `libhyperonpy` which is written using +[pybind11](https://github.com/pybind/pybind11), it converts Python API calls +into C API calls and vice versa. Second part is a Python library `hyperon` +which uses `libhyperonpy` as a proxy for a C API calls. + +All components which depend on `libhyperonc` are built using +[CMake](https://cmake.org/) build tool in order to manage dependencies +automatically. + +Diagram below demonstrates main components and dependencies between them: +![Diagram of the structure](assets/structure.svg) +[Source code of the diagram](assets/structure.plantuml) + +## Language support for IDEs + +Different IDEs may require different tweaks to support the languages +used in the codebase. The language servers which we use +for development are: +- [Rust Language Server](https://github.com/rust-lang/rls#setup); +- [clangd](https://clangd.llvm.org/installation), generate compile + commands for the `clangd` using `cmake` variable: + ``` + cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=Y .. + ``` +- [Python LSP server](https://github.com/python-lsp/python-lsp-server#installation). + + From 8eff29f435f9167c91c1dadc8a19ba23e3631506 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Wed, 9 Aug 2023 18:58:05 -0700 Subject: [PATCH 09/34] Build docs on main --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 5086e8b9d..151d55cd4 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -2,7 +2,7 @@ name: build docs on: push: branches: - - docs-py + - main permissions: contents: write jobs: From 2e66af5d8b45852f449a1b7012347218cef1d2b0 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Sat, 12 Aug 2023 22:04:03 -0700 Subject: [PATCH 10/34] Merge docs and doc folders --- CONTRIBUTING.md | 70 ---------- README.md | 4 +- doc/structure.plantuml | 20 --- doc/structure.svg | 2 - docs/.overrides/main.html | 12 ++ docs/index.md | 233 --------------------------------- {doc => docs}/minimal-metta.md | 0 mkdocs.yml | 28 +++- 8 files changed, 37 insertions(+), 332 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 doc/structure.plantuml delete mode 100644 doc/structure.svg create mode 100644 docs/.overrides/main.html delete mode 100644 docs/index.md rename {doc => docs}/minimal-metta.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 20040d0e3..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,70 +0,0 @@ -# How to contribute - -## Making changes - -Before making changes create a personal fork of the repository. Sync fork and -create new branch from the latest version of the `main` branch. Create separate -branch for each change. Thus it is simpler to support consistent state of the -`main` in your fork. - -Prefer incremental commits to one big commit which contains the whole change. -Prefer commits which passes all of the tests. If it is not possible to satisfy -both requirements at once you can make single commit which passes tests or mark -tests as ignored until change is done. - -Each commit should have a message in the following format: -``` -Change summary in 50 characters (less than 73 characters) - -Optional detailed description of the change when required. Each line -is less than 73 character long. -``` -Such commits looks better in GitHub history. - -Please avoid generic commit messages like `Update README.md`. -Good commit message should describe the change, not a fact of the change. -For example `Add troubleshooting section about No module named 'hyperonpy'`. -By looking to the commit message history the reviewer should understand -the order and brief description of changes. - -Please don't include number and description of the issue into a commit summary -line. Use `Fixes #` in the pull request description instead -to link the PR and the issue. - -PR should satisfy the following requirement before being merged: -- contain latest changes from the repo; -- pass tests; -- be reviewed. - -Feel free to raise draft PR if you need an advice or help with your changes. - -## Code style - -We have small set of code style rules for now. The rule of thumb is to take a look -at the existing code and stick to its style. - -### General - -If you want to leave some reminder in code, for example to fix something later, -you can do it by two ways. Add a comment starting with `FIXME` to mark something -which should be done before the PR is merged. Add a comment starting with `TODO` -to mark the improvement which can be postponed and done later by a separate PR. -The main purpose of a `TODO` comment is to trigger a developer who looks at the -code after you and make him fix the issue if it is appropriate. If the change or -question is big enough or it affects the API of the module it is better to raise -an issue instead. - -### Libraries - -When adding new library into the project please ensure you specify the exact -version instead of using ranges. The minor update of the library can break the -build unexpectedly. The broken build is a real burden because most of the users -build the project from the source. - -### Rust - -When working on Rust C API prefer making `unsafe` blocks as small as possible. -This makes it easier to find blocks that might be source of issues. Usually it is -not required to mark C API functions `unsafe` because they are not intended to -be used from the Rust safe code. - diff --git a/README.md b/README.md index 63e0a0a01..06afbb695 100644 --- a/README.md +++ b/README.md @@ -242,8 +242,8 @@ All components which depend on `libhyperonc` are built using automatically. Diagram below demonstrates main components and dependencies between them: -![Diagram of the structure](./doc/structure.svg) -[Source code of the diagram](./doc/structure.plantuml) +![Diagram of the structure](./docs/assets/structure.svg) +[Source code of the diagram](./docs/assets/structure.plantuml) ## Language support for IDEs diff --git a/doc/structure.plantuml b/doc/structure.plantuml deleted file mode 100644 index 6425e8546..000000000 --- a/doc/structure.plantuml +++ /dev/null @@ -1,20 +0,0 @@ -@startuml - -package "Rust ./lib/*" { - [libhyperon.rlib] as libhyperon -} - -package "Rust, C ./c/*" { - [libhyperonc.so\nlibhyperon.a\nhyperon/hyperon.h] as libhyperonc -} - -package "Python ./python/*" { - [libhyperonpy.so\n./python/libhyperon.cpp] as libhyperonpy - [hyperon\n./python/hyperon/*] as hyperonpy -} - -libhyperonc -> libhyperon -libhyperonpy -u-> libhyperonc -hyperonpy -u-> libhyperonpy - -@enduml diff --git a/doc/structure.svg b/doc/structure.svg deleted file mode 100644 index 84e686494..000000000 --- a/doc/structure.svg +++ /dev/null @@ -1,2 +0,0 @@ - -Rust ./lib/*Rust, C ./c/*Python ./python/*libhyperon.rliblibhyperonc.solibhyperon.ahyperon/hyperon.hlibhyperonpy.so./python/libhyperon.cpphyperon./python/hyperon/* \ No newline at end of file diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html new file mode 100644 index 000000000..d5539f750 --- /dev/null +++ b/docs/.overrides/main.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block announce %} + + For updates follow + + + {% include ".icons/fontawesome/brands/twitter.svg" %} + + OpenCog Hyperon + +{% endblock %} diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index d5582ce41..000000000 --- a/docs/index.md +++ /dev/null @@ -1,233 +0,0 @@ -![CI](https://github.com/trueagi-io/hyperon-experimental/actions/workflows/ci.yml/badge.svg) - -# Overview - -OpenCog Hyperon is a substantially revised, novel version of OpenCog - which is currently at an active -pre-alpha stage of development and experimentation. One of the focuses in the Hyperon design is a successor -to the OpenCog Classic Atomese language with clear semantics supporting meta-language features, -different types of inference, etc. What we have landed on is an "Atomese 2" language called MeTTa (Meta Type Talk). - -In order to get familiar with MeTTa one can read [MeTTa specification](https://wiki.opencog.org/w/File:MeTTa_Specification.pdf) -and watch video with different [MeTTa example explained](https://singularitynet.zoom.us/rec/share/VqHmU37XtbS7VnKY474tkTvvTglsgOIfsI-21MXWxVm_in7U3tGPcfjjiE0P_15R.yUwPdCzEONSUx1EL?startTime=1650636238000). -The examples of MeTTa programs can be found in [./python/tests/scripts](./python/tests/scripts) directory. -Please look at the [Python unit tests](./python/tests) to understand how one can use MeTTa from Python. -More complex usage scenarios are located at [MeTTa examples repo](https://github.com/trueagi-io/metta-examples). -A lot of different materials can be found on [OpenCog wiki server](https://wiki.opencog.org/w/Hyperon). - -If you want to contribute the project please see the [contribution guide](CONTRIBUTING.md) first. -If you find troubles with the installation, see the [Troubleshooting](#troubleshooting) section below. - -# Prepare environment - -## Docker - -A docker image can be used as a ready to run environment. - -Build docker image running: -``` -docker build -t trueagi/hyperon https://raw.githubusercontent.com/trueagi-io/hyperon-experimental/main/Dockerfile -``` - -Run the image: -``` -docker run --rm -ti trueagi/hyperon -``` - -Resulting container contains the latest code from the repo compiled and ready -to run. If the docker image doesn't work, please raise an -[issue](https://github.com/trueagi-io/hyperon-experimental/issues). - -## Manual installation - -### Prerequisites - -* Install latest stable Rust (1.63 or later), see [Rust installation -page](https://www.rust-lang.org/tools/install). Make sure your -`PATH` variable includes `$HOME/.cargo/bin` directory after installing -Rust (see the Notes at the installation page). - - Requirements for building C and Python API - * Python3 and Python3-dev (3.7 or later) - * GCC (7.5 or later) - * CMake (3.10 or later) - -* Install cbindgen: -``` -cargo install --force cbindgen -``` - -* Install Conan and make default Conan profile: -``` -python3 -m pip install conan==1.57 -conan profile new --detect default -``` - -* Install Python library and dependencies in development mode (execute following command in the top directory of repository): -``` -python3 -m pip install -e ./python[dev] -``` - -# Build and run - -## Hyperon library - -Build and test the library: -``` -cd ./lib -cargo build -cargo test -``` - -Run examples: -``` -cargo run --example sorted_list -``` - -To enable logging during running tests or examples export `RUST_LOG` -environment variable: -``` -RUST_LOG=hyperon=debug cargo test -``` - -Running benchmarks requires nightly toolchain so they can be run using: -``` -cargo +nightly bench -``` - -Generate docs: -``` -cd ./lib -cargo doc --no-deps -``` -Docs can be found at `./lib/target/doc/hyperon/index.html`. - -## C and Python API - -Setup build: -``` -mkdir -p build -cd build -cmake .. -``` -To run release build use `-DCMAKE_BUILD_TYPE=Release` cmake flag. - -Build and run tests: -``` -make -make check -``` - -## Running Python and MeTTa examples from command line - -In order to run examples you need to add Python libraries into the `PYTHONPATH` -after compilation: -``` -cd build -export PYTHONPATH=$PYTHONPATH:`pwd`/python -``` - -Run MeTTa script from command line: -``` -cd python/tests -python3 metta.py ./scripts/.metta -``` - -## Troubleshooting - -### Conan claims it cannot find out the version of the C compiler - -If you see the following `cmake` output: -``` -ERROR: Not able to automatically detect '/usr/bin/cc' version -ERROR: Unable to find a working compiler -WARN: Remotes registry file missing, creating default one in /root/.conan/remotes.json -ERROR: libcheck/0.15.2: 'settings.compiler' value not defined -``` -Try to create the default Conan profile manually: -``` -conan profile new --detect default -``` -If it doesn't help, then try to manually add `compiler`, `compiler.version` and -`compiler.libcxx` values in the default Conan profile -(`~/.conan/profiles/default`). -For example: -``` -conan profile update settings.compiler=gcc default -conan profile update settings.compiler.version=7 default -conan profile update settings.compiler.libcxx=libstdc++ default -``` - -### Rust compiler shows errors - -Please ensure you are using the latest stable version: -``` -rustup update stable -``` - -### Importing hyperon Python module fails - -If importing the hyperon module in Python - -```python -import hyperon -``` - -returns the error - -``` -ModuleNotFoundError: No module named 'hyperonpy' -``` - -it likely means that `PYTHONPATH` is set incorrectly. Make sure it -points to `build/python` not just `python`. The path should typically -look like - -``` -/home///build/python -``` - -# Development - -## Structure of the codebase - -Main library `libhyperon.rlib` is written in Rust language, it contains core -API which can be used from other Rust projects. Source code of the library is -located under [./lib](./lib) directory. It is a plain Rust project which can be -built and tested using Cargo tool. - -In order to provide API for platforms and languages other than Rust there is a -C API export library `libhyperonc`. Source code of the library is located under -[./c](./c) directory. The library contains Rust C API bindings and depends on -`libhyperon.rlib` library. Native library is compiled using Cargo, C headers -are generated using cbindgen tool. - -Source code of the Python integration library is located under -[./python](./python) directory. It contains two main parts. First part is a -native Python library `libhyperonpy` which is written using -[pybind11](https://github.com/pybind/pybind11), it converts Python API calls -into C API calls and vice versa. Second part is a Python library `hyperon` -which uses `libhyperonpy` as a proxy for a C API calls. - -All components which depend on `libhyperonc` are built using -[CMake](https://cmake.org/) build tool in order to manage dependencies -automatically. - -Diagram below demonstrates main components and dependencies between them: -![Diagram of the structure](assets/structure.svg) -[Source code of the diagram](assets/structure.plantuml) - -## Language support for IDEs - -Different IDEs may require different tweaks to support the languages -used in the codebase. The language servers which we use -for development are: -- [Rust Language Server](https://github.com/rust-lang/rls#setup); -- [clangd](https://clangd.llvm.org/installation), generate compile - commands for the `clangd` using `cmake` variable: - ``` - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=Y .. - ``` -- [Python LSP server](https://github.com/python-lsp/python-lsp-server#installation). - - - diff --git a/doc/minimal-metta.md b/docs/minimal-metta.md similarity index 100% rename from doc/minimal-metta.md rename to docs/minimal-metta.md diff --git a/mkdocs.yml b/mkdocs.yml index 5ad00c19e..227b7958c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,6 +7,7 @@ copyright: | theme: name: material + custom_dir: docs/.overrides features: - announce.dismiss - content.action.edit @@ -58,13 +59,28 @@ plugins: - minify: minify_html: true - mkdocstrings: + default_hander: python handlers: python: - paths: - - python + paths: [python] + import: + - https://docs.python.org/3/objects.inv + options: + docstring_options: + ignore_init_summary: true + docstring_section_style: list + heading_level: 1 + inherited_members: true + merge_init_into_class: true + separate_signature: true + show_root_heading: true + show_root_full_path: false + show_signature_annotations: true + show_symbol_type_heading: true + show_symbol_type_toc: true + signature_crossrefs: true - autorefs - extra: social: - icon: fontawesome/brands/github-alt @@ -116,8 +132,10 @@ markdown_extensions: # Page tree nav: - - Home: index.md - - Reference: + - Home: + - Minimal Metta: minimal-metta.md + - Contribution: CONTRIBUTING.md + - Python Reference: - Atoms: reference/atoms.md - Base: reference/base.md - Ext: reference/ext.md From 09b987df283d3e17ddd3753283684f8e0d267c89 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Tue, 22 Aug 2023 23:17:54 -0700 Subject: [PATCH 11/34] Add docstrings for atom module --- python/hyperon/atoms.py | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 87965f41c..ab8b92f60 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -7,20 +7,25 @@ from typing import Union class Atom: + """Represents an Atom of any type""" def __init__(self, catom): + """Initialize an atom""" self.catom = catom def __del__(self): + """Frees an atom and all associated resources.""" #import sys; sys.stderr.write("Atom._del_(" + str(self) + ")\n"); sys.stderr.flush() hp.atom_free(self.catom) def __eq__(self, other): + """Checks if two atom objects represent the same conceptual atom. """ return (isinstance(other, Atom) and hp.atom_eq(self.catom, other.catom)) def __repr__(self): + """Renders a human-readable text description of an atom. """ return hp.atom_to_str(self.catom) def get_type(self): @@ -28,6 +33,7 @@ def get_type(self): return hp.atom_get_type(self.catom) def iterate(self): + """Performs a depth-first exhaustive iteration of an atom and all its children recursively.""" res = hp.atom_iterate(self.catom) result = [] for r in res: @@ -35,6 +41,7 @@ def iterate(self): return result def match_atom(self, b): + """Matches one atom with another, establishing bindings between them.""" return BindingsSet(hp.atom_match_atom(self.catom, b.catom)) @staticmethod @@ -57,9 +64,11 @@ class SymbolAtom(Atom): which have the same name reference the same concept.""" def __init__(self, catom): + """Initialize a symbol atom""" super().__init__(catom) def get_name(self): + """Renders the name of an atom into a text buffer.""" return hp.atom_get_name(self.catom) def S(name): @@ -71,9 +80,11 @@ class VariableAtom(Atom): """ def __init__(self, catom): + """Initialize a variable atom""" super().__init__(catom) def get_name(self): + """Renders the name of an atom into a text buffer.""" return hp.atom_get_name(self.catom) def V(name): @@ -83,6 +94,7 @@ class ExpressionAtom(Atom): """Expression Atom combines other kinds of atoms including expressions themselves.""" def __init__(self, catom): + """Initialize an expression atom""" super().__init__(catom) def get_children(self): @@ -117,9 +129,11 @@ class GroundedAtom(Atom): """ def __init__(self, catom): + """Initialize a grounded atom""" super().__init__(catom) def get_object(self): + """Gets the Grounded Atom object or the space wrapped inside a Grounded Atom""" from .base import SpaceRef if self.get_grounded_type() == AtomType.GROUNDED_SPACE: return SpaceRef._from_cspace(hp.atom_get_space(self.catom)) @@ -127,26 +141,27 @@ def get_object(self): return hp.atom_get_object(self.catom) def get_grounded_type(self): + """Retrieve the grounded type of a Grounded Atom.""" return Atom._from_catom(hp.atom_get_grounded_type(self.catom)) def G(object, type=AtomType.UNDEFINED): assert hasattr(object, "copy"), "Method copy should be implemented by grounded object" return GroundedAtom(hp.atom_gnd(object, type.catom)) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_execute_on_grounded_atom(gnd, typ, args): + """ + Private glue for Hyperonpy implementation + """ # ... if hp.atom_to_str(typ) == AtomType.UNDEFINED res_typ = AtomType.UNDEFINED if hp.atom_get_type(typ) != AtomKind.EXPR \ else Atom._from_catom(hp.atom_get_children(typ)[-1]) args = [Atom._from_catom(catom) for catom in args] return gnd.execute(*args, res_typ=res_typ) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_match_on_grounded_atom(gnd, catom): + """ + Private glue for Hyperonpy implementation + """ return gnd.match_(Atom._from_catom(catom)) def atoms_are_equivalent(first, second): @@ -243,25 +258,31 @@ def MatchableAtom(value, type_name=None, atom_id=None): class Bindings: + """Interface for working with atom matching and variable-to-atom binding.""" def __init__(self, bindings: Union[hp.CBindings, None] = None): + """Initialize bindings""" if bindings is None: self.cbindings = hp.bindings_new() else: self.cbindings = bindings def __del__(self): + """Frees a binding""" if self.cbindings is not None: hp.bindings_free(self.cbindings) def __eq__(self, other): + """Checks if two bindings objects contain identical associations.""" return (isinstance(other, Bindings) and hp.bindings_eq(self.cbindings, other.cbindings)) def __repr__(self): + """Renders a text description of the bindings set""" return hp.bindings_to_str(self.cbindings) def __deepcopy__(self, memodict={}): + """Makes a "deep copy" of the bindings set""" return self.clone() def __enter__(self): @@ -273,21 +294,27 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.cbindings = None def clone(self): + """Makes a "deep copy" of the bindings set""" return Bindings(hp.bindings_clone(self.cbindings)) def merge(self, other: 'Bindings') -> 'BindingsSet': + """Merges two Bindings frames together into a Bindings Set.""" return BindingsSet(hp.bindings_merge(self.cbindings, other.cbindings)) def add_var_binding(self, var: Union[str, Atom], atom: Atom) -> bool: + """Adds a new variable <-> atom association within a bindings""" if isinstance(var, Atom): return hp.bindings_add_var_binding(self.cbindings, var.get_name(), atom.catom) else: return hp.bindings_add_var_binding(self.cbindings, var, atom.catom) def is_empty(self) -> bool: + """Checks if a bindings contains no associations. """ return hp.bindings_is_empty(self.cbindings) def narrow_vars(self, vars ): + """Removes all variable associations from a bindings except those in the + supplied list.""" cvars = hp.CVecAtom = hp.atom_vec_new() for var in vars: hp.atom_vec_push(cvars, var.catom) @@ -295,6 +322,7 @@ def narrow_vars(self, vars ): hp.atom_vec_free(cvars) def resolve(self, var_name: str) -> Union[Atom, None]: + """Returns the atom bound to the supplied variable name in the bindings""" raw_atom = hp.bindings_resolve(self.cbindings, var_name) return None if raw_atom is None else Atom._from_catom(raw_atom) From 8498029ef5af56055df448a5660bfaa839657637 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Wed, 23 Aug 2023 21:42:59 -0700 Subject: [PATCH 12/34] Update docs --- docs/reference/atoms.md | 1 - docs/reference/base.md | 1 - docs/reference/ext.md | 1 - docs/reference/runner.md | 1 - docs/reference/stdlib.md | 1 - mkdocs.yml | 13 +++-- python/hyperon/atoms.py | 38 ++++++++++-- python/hyperon/base.py | 121 +++++++++++++++++++-------------------- python/hyperon/ext.py | 4 +- 9 files changed, 103 insertions(+), 78 deletions(-) diff --git a/docs/reference/atoms.md b/docs/reference/atoms.md index f8b3dce74..9655c9f7e 100644 --- a/docs/reference/atoms.md +++ b/docs/reference/atoms.md @@ -1,2 +1 @@ -# Atoms module ::: hyperon.atoms diff --git a/docs/reference/base.md b/docs/reference/base.md index 19ecb5f8c..c0ec722fc 100644 --- a/docs/reference/base.md +++ b/docs/reference/base.md @@ -1,2 +1 @@ -# Base module ::: hyperon.base diff --git a/docs/reference/ext.md b/docs/reference/ext.md index 203304d5e..64e464aec 100644 --- a/docs/reference/ext.md +++ b/docs/reference/ext.md @@ -1,2 +1 @@ -# Ext module ::: hyperon.ext diff --git a/docs/reference/runner.md b/docs/reference/runner.md index 0ae702750..03a637f28 100644 --- a/docs/reference/runner.md +++ b/docs/reference/runner.md @@ -1,2 +1 @@ -# Runner module ::: hyperon.runner diff --git a/docs/reference/stdlib.md b/docs/reference/stdlib.md index e50da0fae..ac52b18da 100644 --- a/docs/reference/stdlib.md +++ b/docs/reference/stdlib.md @@ -1,2 +1 @@ -# Stdlib module ::: hyperon.stdlib diff --git a/mkdocs.yml b/mkdocs.yml index 227b7958c..01726572f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -17,13 +17,13 @@ theme: # - content.tabs.link - content.tooltips # - header.autohide - # - navigation.expand + - navigation.expand - navigation.footer - navigation.indexes - # - navigation.instant + - navigation.instant # - navigation.prune - navigation.sections - - navigation.tabs + # - navigation.tabs # - navigation.tabs.sticky - navigation.top - navigation.tracking @@ -132,12 +132,13 @@ markdown_extensions: # Page tree nav: - - Home: - - Minimal Metta: minimal-metta.md - - Contribution: CONTRIBUTING.md + - Minimal Metta: minimal-metta.md + - Contribution: CONTRIBUTING.md - Python Reference: - Atoms: reference/atoms.md - Base: reference/base.md - Ext: reference/ext.md - Runner: reference/runner.md - Stdlib: reference/stdlib.md + - C Reference: mainpage.md + - Doxygen: html diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index ab8b92f60..ed90b4c42 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -268,7 +268,7 @@ def __init__(self, bindings: Union[hp.CBindings, None] = None): self.cbindings = bindings def __del__(self): - """Frees a binding""" + """Frees a bindings""" if self.cbindings is not None: hp.bindings_free(self.cbindings) @@ -278,11 +278,11 @@ def __eq__(self, other): hp.bindings_eq(self.cbindings, other.cbindings)) def __repr__(self): - """Renders a text description of the bindings set""" + """Renders a text description of the bindings""" return hp.bindings_to_str(self.cbindings) def __deepcopy__(self, memodict={}): - """Makes a "deep copy" of the bindings set""" + """Makes a "deep copy" of the bindings""" return self.clone() def __enter__(self): @@ -294,7 +294,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.cbindings = None def clone(self): - """Makes a "deep copy" of the bindings set""" + """Makes a "deep copy" of the bindings""" return Bindings(hp.bindings_clone(self.cbindings)) def merge(self, other: 'Bindings') -> 'BindingsSet': @@ -339,8 +339,11 @@ def iterator(self): return iter(result) class BindingsSet: + """Represents a set of Bindings frames. Potentially expressing all possible + matches produced by a match operarion. """ def __init__(self, input: Union[hp.CBindingsSet, Bindings, None] = None): + """Initialize Bindings set""" self.shadow_list = None # A lazily initialized list that shadows the BindingsSet values for indexed access if input is None: self.c_set = hp.bindings_set_single() @@ -350,18 +353,22 @@ def __init__(self, input: Union[hp.CBindingsSet, Bindings, None] = None): self.c_set = input def __del__(self): + """Frees a Frees a Bindings set""" if self.c_set is not None: hp.bindings_set_free(self.c_set) self.c_set = None def __eq__(self, other): + """Checks if two Bindings set objects contain identical associations.""" return (isinstance(other, BindingsSet) and hp.bindings_set_eq(self.c_set, other.c_set)) def __repr__(self): + """Renders a text description of a Bindings set""" return hp.bindings_set_to_str(self.c_set) def __deepcopy__(self, memodict={}): + """Makes a "deep copy" of a Bindings set""" return self.clone() def __enter__(self): @@ -379,22 +386,42 @@ def __getitem__(self, key): return self.shadow_list[key] def empty(): + """Creates a new Bindings set without any Bindings frames. + Conceptually this means no valid matches exist. + """ return BindingsSet(hp.bindings_set_empty()) def clone(self): + """Makes a "deep copy" of a Bindings set""" return BindingsSet(hp.bindings_set_clone(self.c_set)) def is_empty(self) -> bool: + """Checks if a Bindings set contains no Bindings frames, and thus indicates + no match.""" return hp.bindings_set_is_empty(self.c_set) def is_single(self) -> bool: + """Checks if a Bindings set contains a frame with no associations, and is + thus allows variables to take on any value. + """ return hp.bindings_set_is_single(self.c_set) def push(self, bindings: Bindings): + """Adds a Bindings frame to an existing Bindings set + + Parameters + ---------- + bindings: + The Bindings set to incorporate into set. Ownership of this argument is + taken by this function + """ self.shadow_list = None hp.bindings_set_push(self.c_set, bindings.cbindings) def add_var_binding(self, var: Union[str, Atom], value: Atom) -> bool: + """Adds a new variable <-> atom association to every Bindings frame in a + Bindings set. + """ self.shadow_list = None if isinstance(var, Atom): return hp.bindings_set_add_var_binding(self.c_set, var.catom, value.catom) @@ -402,10 +429,12 @@ def add_var_binding(self, var: Union[str, Atom], value: Atom) -> bool: return hp.bindings_set_add_var_binding(self.c_set, V(var), value.catom) def add_var_equality(self, a: Atom, b: Atom) -> bool: + """Asserts equality between two Variable atoms in a Bindings set.""" self.shadow_list = None return hp.bindings_set_add_var_equality(self.c_set, a.catom, b.catom) def merge_into(self, input: Union['BindingsSet', Bindings]): + """Merges the contents of one Bindings set into another Bindings set. """ self.shadow_list = None if isinstance(input, BindingsSet): hp.bindings_set_merge_into(self.c_set, input.c_set); @@ -414,6 +443,7 @@ def merge_into(self, input: Union['BindingsSet', Bindings]): hp.bindings_set_merge_into(self.c_set, new_set.c_set); def iterator(self): + """Gets Bindings set iterator""" res = hp.bindings_set_list(self.c_set) result = [] for r in res: diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 5529cb3b5..b213f9756 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -2,11 +2,10 @@ from .atoms import Atom, BindingsSet -""" -A virtual base class upon which Spaces can be implemented in Python -""" class AbstractSpace: - + """ + A virtual base class upon which Spaces can be implemented in Python + """ def __init__(self): return @@ -33,11 +32,11 @@ def atom_count(self): def atoms_iter(self): None -""" -A wrapper over the native GroundingSpace implementation, that can be subclassed and extended within Python -""" class GroundingSpace(AbstractSpace): - + """ + A wrapper over the native GroundingSpace implementation, that can be subclassed + and extended within Python + """ def __init__(self, unwrap=True): super().__init__() # self.cspace = hp.space_new_grounding() @@ -64,39 +63,39 @@ def atom_count(self): def atoms_iter(self): return iter(self.gspace.get_atoms()) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_query_on_python_space(space, query_catom): + """ + Private glue for Hyperonpy implementation + """ query_atom = Atom._from_catom(query_catom) return space.query(query_atom) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_add_on_python_space(space, catom): + """ + Private glue for Hyperonpy implementation + """ atom = Atom._from_catom(catom) space.add(atom) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_remove_on_python_space(space, catom): + """ + Private glue for Hyperonpy implementation + """ atom = Atom._from_catom(catom) return space.remove(atom) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_replace_on_python_space(space, cfrom, cto): + """ + Private glue for Hyperonpy implementation + """ from_atom = Atom._from_catom(cfrom) to_atom = Atom._from_catom(cto) return space.replace(from_atom, to_atom) -""" -Private glue for Hyperonpy implementation -""" def _priv_call_atom_count_on_python_space(space): + """ + Private glue for Hyperonpy implementation + """ if hasattr(space, "atom_count"): count = space.atom_count() if count is not None: @@ -106,20 +105,20 @@ def _priv_call_atom_count_on_python_space(space): else: return -1 -""" -Private glue for Hyperonpy implementation -""" def _priv_call_new_iter_state_on_python_space(space): + """ + Private glue for Hyperonpy implementation + """ if hasattr(space, "atoms_iter"): return space.atoms_iter() else: return None -""" -A reference to a Space, which may be accessed directly, wrapped in a grounded atom, -or passed to a MeTTa interpreter -""" class SpaceRef: + """ + A reference to a Space, which may be accessed directly, wrapped in a grounded atom, + or passed to a MeTTa interpreter + """ def __init__(self, space_obj): if type(space_obj) is hp.CSpace: @@ -137,40 +136,40 @@ def __eq__(self, other): def _from_cspace(cspace): return SpaceRef(cspace) - """ - Returns a new copy of the SpaceRef, referencing the same underlying Space - """ def copy(self): + """ + Returns a new copy of the SpaceRef, referencing the same underlying Space + """ return self - """ - Add an Atom to the Space - """ def add_atom(self, atom): + """ + Add an Atom to the Space + """ hp.space_add(self.cspace, atom.catom) - """ - Delete the specified atom from the Space - """ def remove_atom(self, atom): + """ + Delete the specified atom from the Space + """ return hp.space_remove(self.cspace, atom.catom) - """ - Replace the specified Atom, if it exists in the Space, with the supplied replacement Atom - """ def replace_atom(self, atom, replacement): + """ + Replace the specified Atom, if it exists in the Space, with the supplied replacement Atom + """ return hp.space_replace(self.cspace, atom.catom, replacement.catom) - """ - Returns the number of Atoms in the Space, or -1 if it cannot readily computed - """ def atom_count(self): + """ + Returns the number of Atoms in the Space, or -1 if it cannot readily computed + """ return hp.space_atom_count(self.cspace) - """ - Returns a list of all Atoms in the Space, or None if that is impossible - """ def get_atoms(self): + """ + Returns a list of all Atoms in the Space, or None if that is impossible + """ res = hp.space_list(self.cspace) if res == None: return None @@ -179,32 +178,32 @@ def get_atoms(self): result.append(Atom._from_catom(r)) return result - """ - Returns the Space object referenced by the SpaceRef, or None if the object does not have a - direct Python interface - """ def get_payload(self): + """ + Returns the Space object referenced by the SpaceRef, or None if the object does not have a + direct Python interface + """ return hp.space_get_payload(self.cspace) - """ - Performs the specified query on the Space, and returns the result BindingsSet - """ def query(self, pattern): + """ + Performs the specified query on the Space, and returns the result BindingsSet + """ result = hp.space_query(self.cspace, pattern.catom) return BindingsSet(result) - """ - Performs a substitution within the Space - """ def subst(self, pattern, templ): + """ + Performs a substitution within the Space + """ return [Atom._from_catom(catom) for catom in hp.space_subst(self.cspace, pattern.catom, templ.catom)] -""" -A reference to a native GroundingSpace, implemented by the MeTTa core library -""" class GroundingSpaceRef(SpaceRef): + """ + A reference to a native GroundingSpace, implemented by the MeTTa core library + """ def __init__(self, cspace = None): if cspace is None: diff --git a/python/hyperon/ext.py b/python/hyperon/ext.py index 90cdca070..5c1d35cce 100644 --- a/python/hyperon/ext.py +++ b/python/hyperon/ext.py @@ -17,13 +17,13 @@ def metta_register(metta): return metta_register return inner -def register_atoms(*args, **kwargs): +def register_atoms(*args, pass_metta=False, **kwargs): """Function decorator which registers returned pairs of regular expressions and atoms in MeTTa tokenizer using MeTTa.register_atom() method. Parameters ---------- - pass_metta : bool, optional + pass_metta: Pass instance of MeTTa class to the decorated function as an argument. Default is False. """ From 7297551e8328161bb82f1c06114ddf3f1fcfaf89 Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Sat, 26 Aug 2023 23:37:22 -0700 Subject: [PATCH 13/34] Update docs --- python/hyperon/atoms.py | 4 ++++ python/hyperon/base.py | 13 +++++++++++++ python/hyperon/metta.py | 15 +++++++++++++++ python/hyperon/runner.py | 10 ++++++++++ 4 files changed, 42 insertions(+) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index ed90b4c42..fb9de5e85 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -72,6 +72,7 @@ def get_name(self): return hp.atom_get_name(self.catom) def S(name): + """A convenient method to construct a SymbolAtom""" return SymbolAtom(hp.atom_sym(name)) class VariableAtom(Atom): @@ -88,6 +89,7 @@ def get_name(self): return hp.atom_get_name(self.catom) def V(name): + """A convenient method to construct a VariableAtom""" return VariableAtom(hp.atom_var(name)) class ExpressionAtom(Atom): @@ -103,6 +105,7 @@ def get_children(self): def E(*args): + """A convenient method to construct a ExpressionAtom""" return ExpressionAtom(hp.atom_expr([atom.catom for atom in args])) class AtomType: @@ -145,6 +148,7 @@ def get_grounded_type(self): return Atom._from_catom(hp.atom_get_grounded_type(self.catom)) def G(object, type=AtomType.UNDEFINED): + """A convenient method to construct a GroundedAtom""" assert hasattr(object, "copy"), "Method copy should be implemented by grounded object" return GroundedAtom(hp.atom_gnd(object, type.catom)) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index b213f9756..a87b5ae20 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -18,18 +18,23 @@ def query(self, query_atom): # None def add(self, atom): + """Adds an atom to the atom space""" raise RuntimeError("Space::add() is not implemented") def remove(self, atom): + """Removes an atom from the atom space""" raise RuntimeError("Space::remove() is not implemented") def replace(self, atom, replacement): + """Replaces an atom in the atom space""" raise RuntimeError("Space::replace() is not implemented") def atom_count(self): + """Counts the number of atoms in the atom space""" None def atoms_iter(self): + """Gets the atom iterator""" None class GroundingSpace(AbstractSpace): @@ -43,24 +48,32 @@ def __init__(self, unwrap=True): self.gspace = GroundingSpaceRef() def query(self, query_atom): + """ + Performs the specified query on the Space, and returns the result BindingsSet + """ return self.gspace.query(query_atom) # TODO (INTERNAL): Currently unimplemented. # def subst(self, pattern, templ): def add(self, atom): + """Adds an atom to the atom space""" self.gspace.add_atom(atom) def remove(self, atom): + """Removes an atom from the atom space""" return self.gspace.remove_atom(atom) def replace(self, from_atom, to_atom): + """Replaces an atom in the atom space""" return self.gspace.replace_atom(from_atom, to_atom) def atom_count(self): + """Counts the number of atoms in the atom space""" return self.gspace.atom_count() def atoms_iter(self): + """Gets the atom iterator""" return iter(self.gspace.get_atoms()) def _priv_call_query_on_python_space(space, query_catom): diff --git a/python/hyperon/metta.py b/python/hyperon/metta.py index bf73dfb72..7058ed1b8 100644 --- a/python/hyperon/metta.py +++ b/python/hyperon/metta.py @@ -1,9 +1,24 @@ +""" +This is the MeTTa entrypoint +""" import sys import argparse from hyperon import MeTTa def main(): + """ + usage: metta.py [-h] metta file + + Metta script interpreter + + positional arguments: + metta file metta script + + optional arguments: + -h, --help show this help message and exit + + """ parser = argparse.ArgumentParser(description='Metta script interpreter') parser.add_argument( 'file', metavar="metta file", help='metta script') diff --git a/python/hyperon/runner.py b/python/hyperon/runner.py index 78f4aa33e..b941082de 100644 --- a/python/hyperon/runner.py +++ b/python/hyperon/runner.py @@ -5,6 +5,7 @@ from .base import GroundingSpaceRef, Tokenizer, SExprParser class MeTTa: + """This class contains the MeTTa program execution utilities""" def __init__(self, space = None, cwd = ".", cmetta = None): if cmetta is not None: @@ -25,15 +26,19 @@ def __del__(self): hp.metta_free(self.cmetta) def space(self): + """Gets the metta space""" return GroundingSpaceRef._from_cspace(hp.metta_space(self.cmetta)) def tokenizer(self): + """Gets the tokenizer""" return Tokenizer._from_ctokenizer(hp.metta_tokenizer(self.cmetta)) def register_token(self, regexp, constr): + """Registers a token""" self.tokenizer().register_token(regexp, constr) def register_atom(self, name, symbol): + """Registers an atom""" self.register_token(name, lambda _: symbol) def _parse_all(self, program): @@ -45,12 +50,15 @@ def _parse_all(self, program): yield atom def parse_all(self, program): + """Parse the entire program""" return list(self._parse_all(program)) def parse_single(self, program): + """Parse the next single line in the program""" return next(self._parse_all(program)) def load_py_module(self, name): + """Loads the give python module""" if not isinstance(name, str): name = repr(name) mod = import_module(name) @@ -60,6 +68,7 @@ def load_py_module(self, name): obj(self) def import_file(self, fname): + """Loads the program file and run it""" path = fname.split(os.sep) if len(path) == 1: path = ['.'] + path @@ -75,6 +84,7 @@ def import_file(self, fname): return result def run(self, program, flat=False): + """Runs the program""" parser = SExprParser(program) results = hp.metta_run(self.cmetta, parser.cparser) if flat: From 2f13b17ff83a34d243ba44d4a103c30db68008ea Mon Sep 17 00:00:00 2001 From: Wenwei Huang Date: Sun, 27 Aug 2023 17:35:15 -0700 Subject: [PATCH 14/34] Update docs --- python/hyperon/base.py | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index a87b5ae20..13940856f 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -10,6 +10,9 @@ def __init__(self): return def query(self, query_atom): + """ + Performs the specified query on the Space, and returns the result BindingsSet + """ raise RuntimeError("Space::query() is not implemented") # TODO (INTERNAL): Currently unimplemented. We may do this differently depending on lazy / comprehensions @@ -244,6 +247,17 @@ def __del__(self): hp.tokenizer_free(self.ctokenizer) def register_token(self, regex, constr): + """Registers a new custom Token in a Tokenizer. + + Hyperon uses the Rust RegEx engine and syntax + https://docs.rs/regex/latest/regex/ + + Parameters + ---------- + regex: + A regular expression to match the incoming text, triggering this token to + generate a new atom + """ hp.tokenizer_register_token(self.ctokenizer, regex, constr) class SExprParser: @@ -261,34 +275,71 @@ def __init__(self, gnd_space, expr): self.step_result = hp.interpret_init(gnd_space.cspace, expr.catom) def has_next(self): + """Examines a Step Result to determine if more work is needed.""" return hp.step_has_next(self.step_result) def next(self): + """Moves to the next Step Result""" if not self.has_next(): raise StopIteration() self.step_result = hp.interpret_step(self.step_result) def get_result(self): + """Consumes a Step Result and provides the ultimate outcome of a MeTTa + interpreter session. + """ if self.has_next(): raise RuntimeError("Plan execution is not finished") return hp.step_get_result(self.step_result) def get_step_result(self): + """Gets the current Step Result""" return self.step_result def interpret(gnd_space, expr): + """Parses the expression in the grounded space""" interpreter = Interpreter(gnd_space, expr) while interpreter.has_next(): interpreter.next() return [Atom._from_catom(catom) for catom in interpreter.get_result()] def check_type(gnd_space, atom, type): + """Checks whether the atom has the type `type` in context of space + + Parameters + ---------- + space: + A pointer to the space_t representing the space context in which to perform + the check + atom: + A pointer to the atom_t or atom_ref_t representing the atom whose Type the + function will check + type: + A pointer to the atom_t or atom_ref_t representing the type to check against + """ + return hp.check_type(gnd_space.cspace, atom.catom, type.catom) def validate_atom(gnd_space, atom): + """Checks whether atom is correctly typed. + + Parameters + ---------- + gnd_space: + A pointer to the space_t representing the space context in which to perform + the check + atom: + A pointer to the atom_t or atom_ref_t representing the atom whose Type the + function will check + + Returns + ------- + if the Atom is correctly typed, otherwise false + """ return hp.validate_atom(gnd_space.cspace, atom.catom) def get_atom_types(gnd_space, atom): + """Provides all types for atom in the context of space""" result = hp.get_atom_types(gnd_space.cspace, atom.catom) return [Atom._from_catom(catom) for catom in result] From df8a7c9cb9b85abede9773ec1229131453da71e4 Mon Sep 17 00:00:00 2001 From: glicerico Date: Thu, 7 Sep 2023 20:16:47 -0600 Subject: [PATCH 15/34] First batch of docstring revision --- README.md | 2 +- python/hyperon/atoms.py | 101 +++++++++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 06afbb695..de5f8da00 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ All components which depend on `libhyperonc` are built using [CMake](https://cmake.org/) build tool in order to manage dependencies automatically. -Diagram below demonstrates main components and dependencies between them: +The diagram below demonstrates main components and dependencies between them: ![Diagram of the structure](./docs/assets/structure.svg) [Source code of the diagram](./docs/assets/structure.plantuml) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index fb9de5e85..fd2f82281 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -10,30 +10,29 @@ class Atom: """Represents an Atom of any type""" def __init__(self, catom): - """Initialize an atom""" + """Initialize an Atom""" self.catom = catom def __del__(self): - """Frees an atom and all associated resources.""" + """Frees an Atom and all associated resources.""" #import sys; sys.stderr.write("Atom._del_(" + str(self) + ")\n"); sys.stderr.flush() hp.atom_free(self.catom) - def __eq__(self, other): - """Checks if two atom objects represent the same conceptual atom. """ + """Checks if two atom objects represent the same conceptual Atom.""" return (isinstance(other, Atom) and hp.atom_eq(self.catom, other.catom)) def __repr__(self): - """Renders a human-readable text description of an atom. """ + """Renders a human-readable text description of the Atom.""" return hp.atom_to_str(self.catom) def get_type(self): - """Gets the type of this Atom""" + """Gets the type of the current Atom instance""" return hp.atom_get_type(self.catom) def iterate(self): - """Performs a depth-first exhaustive iteration of an atom and all its children recursively.""" + """Performs a depth-first exhaustive iteration of an Atom and all its children recursively.""" res = hp.atom_iterate(self.catom) result = [] for r in res: @@ -41,7 +40,7 @@ def iterate(self): return result def match_atom(self, b): - """Matches one atom with another, establishing bindings between them.""" + """Matches one Atom with another, establishing bindings between them.""" return BindingsSet(hp.atom_match_atom(self.catom, b.catom)) @staticmethod @@ -57,18 +56,18 @@ def _from_catom(catom): elif type == AtomKind.GROUNDED: return GroundedAtom(catom) else: - raise Exception("Unexpected type of the atom: " + str(type)) + raise Exception("Unexpected type of the Atom: " + str(type)) class SymbolAtom(Atom): - """Symbol Atom represents a single concepts which is identified by name. Two symbols - which have the same name reference the same concept.""" + """A SymbolAtom represents a single concept, identified by name. If two symbols + have the same name, they reference the same concept.""" def __init__(self, catom): - """Initialize a symbol atom""" + """Initialize a SymbolAtom""" super().__init__(catom) def get_name(self): - """Renders the name of an atom into a text buffer.""" + """Renders the name of the Atom into a text buffer.""" return hp.atom_get_name(self.catom) def S(name): @@ -76,16 +75,15 @@ def S(name): return SymbolAtom(hp.atom_sym(name)) class VariableAtom(Atom): - """ - Variable Atom represents a variable like a variable in the pattern. - """ + """A VariableAtom represents a variable in an expression. It serves as a + placeholder that can be matched with, or bound to other Atoms.""" def __init__(self, catom): - """Initialize a variable atom""" + """Initialize a VariableAtom""" super().__init__(catom) def get_name(self): - """Renders the name of an atom into a text buffer.""" + """Renders the name of the Atom into a text buffer.""" return hp.atom_get_name(self.catom) def V(name): @@ -93,23 +91,23 @@ def V(name): return VariableAtom(hp.atom_var(name)) class ExpressionAtom(Atom): - """Expression Atom combines other kinds of atoms including expressions themselves.""" + """An ExpressionAtom combines different kinds of Atoms, including expressions.""" def __init__(self, catom): """Initialize an expression atom""" super().__init__(catom) def get_children(self): - """Gets all of the children Atoms""" + """Gets all children Atoms""" return [Atom._from_catom(catom) for catom in hp.atom_get_children(self.catom)] def E(*args): - """A convenient method to construct a ExpressionAtom""" + """A convenient method to construct an ExpressionAtom""" return ExpressionAtom(hp.atom_expr([atom.catom for atom in args])) class AtomType: - """Defines all the types of Atom""" + """Defines all Atom types""" UNDEFINED = Atom._from_catom(hp.CAtomType.UNDEFINED) TYPE = Atom._from_catom(hp.CAtomType.TYPE) @@ -121,22 +119,23 @@ class AtomType: GROUNDED_SPACE = Atom._from_catom(hp.CAtomType.GROUNDED_SPACE) class GroundedAtom(Atom): - """Grounded Atom represents sub-symbolic knowledge. On the API level it allows - keeping data and behaviour inside an atom. There are three aspects of the grounded - atom which can be customized: - - - the type of grounded atom is provided by the atom itself; - - matching algorithm of the atom can be modified by the user; - - atom can be made executable; such atom can be used to apply some sub-symbolic - operations to other atoms as arguments. + """ + A GroundedAtom represents sub-symbolic knowledge. At the API level, it allows + keeping data and behaviour inside an Atom. There are three aspects of a GroundedAtom + which can be customized: + + - the type of GroundedAtom is provided by the Atom itself; + - the matching algorithm used by the Atom; + - an Atom can be made executable, and used to apply sub-symbolic + operations to other Atoms as arguments. """ def __init__(self, catom): - """Initialize a grounded atom""" + """Initialize a GroundedAtom""" super().__init__(catom) def get_object(self): - """Gets the Grounded Atom object or the space wrapped inside a Grounded Atom""" + """Gets the GroundedAtom object, or the space wrapped inside a GroundedAtom""" from .base import SpaceRef if self.get_grounded_type() == AtomType.GROUNDED_SPACE: return SpaceRef._from_cspace(hp.atom_get_space(self.catom)) @@ -144,7 +143,7 @@ def get_object(self): return hp.atom_get_object(self.catom) def get_grounded_type(self): - """Retrieve the grounded type of a Grounded Atom.""" + """Retrieve the grounded type of the GroundedAtom.""" return Atom._from_catom(hp.atom_get_grounded_type(self.catom)) def G(object, type=AtomType.UNDEFINED): @@ -154,7 +153,8 @@ def G(object, type=AtomType.UNDEFINED): def _priv_call_execute_on_grounded_atom(gnd, typ, args): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Executes grounded Atoms. """ # ... if hp.atom_to_str(typ) == AtomType.UNDEFINED res_typ = AtomType.UNDEFINED if hp.atom_get_type(typ) != AtomKind.EXPR \ @@ -164,20 +164,26 @@ def _priv_call_execute_on_grounded_atom(gnd, typ, args): def _priv_call_match_on_grounded_atom(gnd, catom): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Matches grounded atoms """ return gnd.match_(Atom._from_catom(catom)) def atoms_are_equivalent(first, second): + """Check if two atoms are equivalent""" return hp.atoms_are_equivalent(first.catom, second.catom) class GroundedObject: + """A GroundedObject holds some content and, optionally, an identifier.""" def __init__(self, content, id=None): + """Initializes a new GroundedObject with the given content and identifier.""" self.content = content self.id = id def __repr__(self): + """Returns the object's ID if present, or a string representation of + its content if not.""" # Overwrite Python default representation of a string to use # double quotes instead of single quotes. if isinstance(self.content, str): @@ -187,19 +193,40 @@ def __repr__(self): return repr(self.content) if self.id is None else self.id def copy(self): + """ + Returns a copy of this GroundedObject instance. + + Note: Currently, this method returns the original + instance, effectively making the GroundedObject immutable. + """ return self class ValueObject(GroundedObject): + """ + A ValueObject is a specialized form of GroundedObject, which treats its content + as a value. It allows for equality comparison between the content of two ValueObjects. + + Example: + obj1 = ValueObject(5) + obj2 = ValueObject(5) + obj3 = ValueObject(6) + + print(obj1 == obj2) # True + print(obj1 == obj3) # False + """ @property def value(self): + """Gets the value of the object, which is its content.""" return self.content def __eq__(self, other): - # TODO: ?typecheck + """Compares the equality of this ValueObject with another based on their content.""" + # TODO: ?typecheck for the contents return isinstance(other, ValueObject) and self.content == other.content class NoReduceError(Exception): + """Custom exception raised when a reduction operation cannot be performed.""" pass class OperationObject(GroundedObject): @@ -223,7 +250,7 @@ def execute(self, *args, res_typ=AtomType.UNDEFINED): if not isinstance(arg, GroundedAtom): # REM: # Currently, applying grounded operations to pure atoms is not reduced. - # If we want, we can raise an exception, or to form a error expression instead, + # If we want, we can raise an exception, or form an error expression instead, # so a MeTTa program can catch and analyze it. # raise RuntimeError("Grounded operation " + self.name + " with unwrap=True expects only grounded arguments") raise NoReduceError() From f82b8ade1e20e95ab1e82c39e5f79ce1860cd039 Mon Sep 17 00:00:00 2001 From: glicerico Date: Thu, 7 Sep 2023 20:35:45 -0600 Subject: [PATCH 16/34] Docstring for OperationObject --- python/hyperon/atoms.py | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index fd2f82281..b9eef1567 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -230,20 +230,77 @@ class NoReduceError(Exception): pass class OperationObject(GroundedObject): + """ + An OperationObject represents an operation as a grounded object, allowing for more + advanced logic like lazy evaluation, type-checking, and more. + + Inherits: + GroundedObject: The parent class that provides the basic wrapper around content. + + Attributes: + unwrap (bool): Determines whether to unwrap the content of GroundedAtoms + when passed as arguments to the operation. + + Properties: + op: Returns the operation function. + name: Returns the identifier name for this operation object. + + Methods: + __init__(name, op, unwrap): Initializes an OperationObject instance. + execute(*args, res_typ): Executes the operation with the provided arguments. + __eq__(other): Compares the equality of this OperationObject instance with another. + + Example: + def add(a, b): + return a + b + + op_obj = OperationObject("addition", add) + result = op_obj.execute(3, 4) + """ def __init__(self, name, op, unwrap=True): + """ + Initializes a new OperationObject with a name identifier, operation function, + and an optional unwrap flag. + Parameters: + name (str): The identifier for this operation. + op (function): The function representing the operation. + unwrap (bool, optional): Whether to unwrap GroundedAtom content when applying + the operation. Defaults to True. + + """ super().__init__(op, name) self.unwrap = unwrap @property def op(self): + """Returns the operation function.""" return self.content @property def name(self): + """Returns the identifier name for this operation object.""" return self.id def execute(self, *args, res_typ=AtomType.UNDEFINED): + """ + Executes the operation with the provided arguments. + + Parameters: + *args: Arguments to pass to the operation function. + res_typ (AtomType, optional): The expected result type. Defaults to AtomType.UNDEFINED. + + Returns: + The result of the operation. + + Raises: + NoReduceError: Raised when `unwrap=True` and a non-GroundedAtom argument is provided. + RuntimeError: Raised when the result of the operation is not a list. + + Note: + Depending on the `unwrap` attribute, this method will either unwrap GroundedAtoms + before passing them to the operation or pass them as is. + """ # type-check? if self.unwrap: for arg in args: @@ -263,6 +320,15 @@ def execute(self, *args, res_typ=AtomType.UNDEFINED): return result def __eq__(self, other): + """ + Compares the equality of this OperationObject with another based on their names. + + Parameters: + other (OperationObject): Another OperationObject instance to compare. + + Returns: + True if both OperationObjects have the same name; False otherwise. + """ return isinstance(other, OperationObject) and self.name == other.name class MatchableObject(ValueObject): From 0530c903f9637698b4b95ae8d71c7589b2a74fe3 Mon Sep 17 00:00:00 2001 From: glicerico Date: Thu, 7 Sep 2023 20:44:46 -0600 Subject: [PATCH 17/34] More docstrings --- python/hyperon/atoms.py | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index b9eef1567..021d12b39 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -332,10 +332,72 @@ def __eq__(self, other): return isinstance(other, OperationObject) and self.name == other.name class MatchableObject(ValueObject): + """ + Represents an object that can be involved in a matching operation with an Atom. + + This class is meant to be subclassed by objects that define specific matching behavior + with an Atom. It provides a stub method for the matching operation that raises + a RuntimeError when called, which must be overridden by subclasses. + + Inherits: + ValueObject: The parent class that provides basic value-based equality and representation. + + Methods: + match_(atom): A stub method for matching the object with an Atom. + + Example: + class MyMatchableObject(MatchableObject): + def match_(self, atom): + # Implement the matching logic here + pass + + my_obj = MyMatchableObject("some_value") + my_obj.match_(some_atom) # Should not raise RuntimeError + + Raises: + RuntimeError: Raised when the match_ method is called without being overridden by a subclass. + """ + def match_(self, atom): + """ + A stub method for matching the object with an Atom. + + This method is intended to be overridden by subclasses to provide specific + matching behavior with an Atom. + + Parameters: + atom (Atom): An Atom object to match against. + + Raises: + RuntimeError: Raised when this method is called without being overridden in a subclass. + """ raise RuntimeError("MatchableObject::match_() is not implemented") def _type_sugar(type_names): + """ + Transforms a variety of type representations into a unified Atom-based format. + + This utility function is intended for internal use to handle different ways in which + type information can be provided. It converts `type_names` into a form that can be + readily used for type checking or other internal operations. + + Parameters: + type_names (Union[None, list, str, AtomType]): The type information to be converted. + - If None, will return AtomType.UNDEFINED. + - If list, will recursively transform each element. + - If str, will return a Variable Atom (`V`) if the string starts with '$'; otherwise, returns a Symbol Atom (`S`). + - If already an AtomType, returns it as is. + + Returns: + AtomType: The transformed type information in AtomType format. + + Examples: + _type_sugar(None) => AtomType.UNDEFINED + _type_sugar(["int", "str"]) => E(S("->"), S("int"), S("str")) + _type_sugar("$var") => V("var") + _type_sugar("int") => S("int") + _type_sugar(AtomType.SOME_TYPE) => AtomType.SOME_TYPE + """ if type_names is None: return AtomType.UNDEFINED if isinstance(type_names, list): From 688609721a740f2d00385b055e9ca7ac9d4b6158 Mon Sep 17 00:00:00 2001 From: glicerico Date: Fri, 8 Sep 2023 13:17:26 -0600 Subject: [PATCH 18/34] More docstrings --- python/hyperon/atoms.py | 73 +++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 021d12b39..431a38733 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -407,12 +407,21 @@ def _type_sugar(type_names): return type_names def OperationAtom(name, op, type_names=None, unwrap=True): + """ + An OperationAtom wraps an operation with optional type information into a GroundedAtom + and associates a name with it. Useful for registering custom operations + that can be executed in an Atom-based computational environment. + """ return G(OperationObject(name, op, unwrap), _type_sugar(type_names)) def ValueAtom(value, type_name=None, atom_id=None): + """Creates a GroundedAtom that wraps a given value, optionally specifying its type and identifier.""" return G(ValueObject(value, atom_id), _type_sugar(type_name)) def MatchableAtom(value, type_name=None, atom_id=None): + """ + Creates a Grounded Atom that wraps a matchable value, optionally specifying its type and identifier. + """ return G(MatchableObject(value, atom_id), _type_sugar(type_name)) @@ -420,14 +429,14 @@ class Bindings: """Interface for working with atom matching and variable-to-atom binding.""" def __init__(self, bindings: Union[hp.CBindings, None] = None): - """Initialize bindings""" + """Initializes with or without pre-existing bindings.""" if bindings is None: self.cbindings = hp.bindings_new() else: self.cbindings = bindings def __del__(self): - """Frees a bindings""" + """Frees the binding resources.""" if self.cbindings is not None: hp.bindings_free(self.cbindings) @@ -441,13 +450,15 @@ def __repr__(self): return hp.bindings_to_str(self.cbindings) def __deepcopy__(self, memodict={}): - """Makes a "deep copy" of the bindings""" + """Makes a "deep copy" of the bindings.""" return self.clone() def __enter__(self): + """For context management.""" return self def __exit__(self, exc_type, exc_val, exc_tb): + """Frees resources on exit.""" if self.cbindings is not None: hp.bindings_free(self.cbindings) self.cbindings = None @@ -457,23 +468,22 @@ def clone(self): return Bindings(hp.bindings_clone(self.cbindings)) def merge(self, other: 'Bindings') -> 'BindingsSet': - """Merges two Bindings frames together into a Bindings Set.""" + """Merges with another Bindings instance, into a Bindings Set.""" return BindingsSet(hp.bindings_merge(self.cbindings, other.cbindings)) def add_var_binding(self, var: Union[str, Atom], atom: Atom) -> bool: - """Adds a new variable <-> atom association within a bindings""" + """Adds a binding between a variable and an Atom.""" if isinstance(var, Atom): return hp.bindings_add_var_binding(self.cbindings, var.get_name(), atom.catom) else: return hp.bindings_add_var_binding(self.cbindings, var, atom.catom) def is_empty(self) -> bool: - """Checks if a bindings contains no associations. """ + """Checks if a bindings contains no associations.""" return hp.bindings_is_empty(self.cbindings) def narrow_vars(self, vars ): - """Removes all variable associations from a bindings except those in the - supplied list.""" + """Keeps only specific variable associations.""" cvars = hp.CVecAtom = hp.atom_vec_new() for var in vars: hp.atom_vec_push(cvars, var.catom) @@ -481,15 +491,17 @@ def narrow_vars(self, vars ): hp.atom_vec_free(cvars) def resolve(self, var_name: str) -> Union[Atom, None]: - """Returns the atom bound to the supplied variable name in the bindings""" + """Finds the atom for a given variable name""" raw_atom = hp.bindings_resolve(self.cbindings, var_name) return None if raw_atom is None else Atom._from_catom(raw_atom) def resolve_and_remove(self, var_name: str) -> Union[Atom, None]: + """Finds anr removes the atom for a given variable name""" raw_atom = hp.bindings_resolve_and_remove(self.cbindings, var_name) return None if raw_atom is None else Atom._from_catom(raw_atom) def iterator(self): + """Iterates through all variable-atom pairs in the bindings""" res = hp.bindings_list(self.cbindings) result = [] for r in res: @@ -498,11 +510,11 @@ def iterator(self): return iter(result) class BindingsSet: - """Represents a set of Bindings frames. Potentially expressing all possible - matches produced by a match operarion. """ + """Represents a set of Bindings frames, potentially expressing all possible + matches produced by a match operation.""" def __init__(self, input: Union[hp.CBindingsSet, Bindings, None] = None): - """Initialize Bindings set""" + """Initializes with optional input.""" self.shadow_list = None # A lazily initialized list that shadows the BindingsSet values for indexed access if input is None: self.c_set = hp.bindings_set_single() @@ -512,74 +524,77 @@ def __init__(self, input: Union[hp.CBindingsSet, Bindings, None] = None): self.c_set = input def __del__(self): - """Frees a Frees a Bindings set""" + """Frees the BindingsSet""" if self.c_set is not None: hp.bindings_set_free(self.c_set) self.c_set = None def __eq__(self, other): - """Checks if two Bindings set objects contain identical associations.""" + """Checks if other BindingsSet contains identical associations.""" return (isinstance(other, BindingsSet) and hp.bindings_set_eq(self.c_set, other.c_set)) def __repr__(self): - """Renders a text description of a Bindings set""" + """Renders a text description of a BindingsSet""" return hp.bindings_set_to_str(self.c_set) def __deepcopy__(self, memodict={}): - """Makes a "deep copy" of a Bindings set""" + """Makes a "deep copy" of a BindingsSet""" return self.clone() def __enter__(self): + """For context management.""" return self def __exit__(self, exc_type, exc_val, exc_tb): + """Frees resources on exit.""" if self.c_set is not None: hp.bindings_set_free(self.c_set) self.c_set = None def __getitem__(self, key): + """Gets a Bindings frame by index""" if self.shadow_list is None: result = hp.bindings_set_unpack(self.c_set) self.shadow_list = [{k: Atom._from_catom(v) for k, v in bindings.items()} for bindings in result] return self.shadow_list[key] def empty(): - """Creates a new Bindings set without any Bindings frames. - Conceptually this means no valid matches exist. + """Creates a new BindingsSet without any Bindings frames. + Conceptually, this means no valid matches exist. """ return BindingsSet(hp.bindings_set_empty()) def clone(self): - """Makes a "deep copy" of a Bindings set""" + """Makes a "deep copy" of a BindingsSet""" return BindingsSet(hp.bindings_set_clone(self.c_set)) def is_empty(self) -> bool: - """Checks if a Bindings set contains no Bindings frames, and thus indicates + """Checks if a BindingsSet contains no Bindings frames, and thus indicates no match.""" return hp.bindings_set_is_empty(self.c_set) def is_single(self) -> bool: - """Checks if a Bindings set contains a frame with no associations, and is - thus allows variables to take on any value. + """Checks if a Bindings set contains a frame with no associations, and + thus allows variables to take any value. """ return hp.bindings_set_is_single(self.c_set) def push(self, bindings: Bindings): - """Adds a Bindings frame to an existing Bindings set + """Adds a Bindings frame to an existing BindingsSet Parameters ---------- bindings: The Bindings set to incorporate into set. Ownership of this argument is - taken by this function + taken by this function. """ self.shadow_list = None hp.bindings_set_push(self.c_set, bindings.cbindings) def add_var_binding(self, var: Union[str, Atom], value: Atom) -> bool: - """Adds a new variable <-> atom association to every Bindings frame in a - Bindings set. + """Adds a new variable to atom association to every Bindings frame in a + BindingsSet. """ self.shadow_list = None if isinstance(var, Atom): @@ -588,12 +603,12 @@ def add_var_binding(self, var: Union[str, Atom], value: Atom) -> bool: return hp.bindings_set_add_var_binding(self.c_set, V(var), value.catom) def add_var_equality(self, a: Atom, b: Atom) -> bool: - """Asserts equality between two Variable atoms in a Bindings set.""" + """Asserts equality between two Variable atoms in a BindingsSet.""" self.shadow_list = None return hp.bindings_set_add_var_equality(self.c_set, a.catom, b.catom) def merge_into(self, input: Union['BindingsSet', Bindings]): - """Merges the contents of one Bindings set into another Bindings set. """ + """Merges the contents of another BindingsSet or Bindings frame.""" self.shadow_list = None if isinstance(input, BindingsSet): hp.bindings_set_merge_into(self.c_set, input.c_set); @@ -602,7 +617,7 @@ def merge_into(self, input: Union['BindingsSet', Bindings]): hp.bindings_set_merge_into(self.c_set, new_set.c_set); def iterator(self): - """Gets Bindings set iterator""" + """Iterates through all Bindings frames.""" res = hp.bindings_set_list(self.c_set) result = [] for r in res: From 2af780ac6f83d29022f19f6a348df8ddd2582098 Mon Sep 17 00:00:00 2001 From: glicerico Date: Fri, 8 Sep 2023 15:28:32 -0600 Subject: [PATCH 19/34] Document register_results --- python/hyperon/ext.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/python/hyperon/ext.py b/python/hyperon/ext.py index 5c1d35cce..ac2629b58 100644 --- a/python/hyperon/ext.py +++ b/python/hyperon/ext.py @@ -1,18 +1,30 @@ from .runner import MeTTa def register_results(method, args, kwargs): + """Returns a decorator for registering the results of a method. + The behavior of the decorator depends on whether it is used with or without arguments.""" + + # Case 1: Decorator used without arguments (i.e., @decorator instead of @decorator(args)) if len(args) == 1 and len(kwargs) == 0 and callable(args[0]): - # no arguments - func = args[0] + func = args[0] # func is the decorated function + + # Define the decorator def metta_register(metta): + # Register the results of calling the decorated function using the provided method method(metta, func()) return metta_register + + # Case 2: Decorator used with arguments (i.e., @decorator(args)) else: - # with arguments + # Check if the decorator is used with arguments pass_metta = kwargs.get('pass_metta', False) + + # Define the decorator def inner(func): def metta_register(metta): + # Get the results of calling the decorated function regs = func(metta) if pass_metta else func() + # Register the results using the provided method method(metta, regs) return metta_register return inner From e81c5850b801e5947d0b12f6008090916979ed1b Mon Sep 17 00:00:00 2001 From: glicerico Date: Fri, 8 Sep 2023 15:31:58 -0600 Subject: [PATCH 20/34] Fix typos --- python/hyperon/runner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/hyperon/runner.py b/python/hyperon/runner.py index b941082de..da428df98 100644 --- a/python/hyperon/runner.py +++ b/python/hyperon/runner.py @@ -38,7 +38,7 @@ def register_token(self, regexp, constr): self.tokenizer().register_token(regexp, constr) def register_atom(self, name, symbol): - """Registers an atom""" + """Registers an Atom""" self.register_token(name, lambda _: symbol) def _parse_all(self, program): @@ -58,7 +58,7 @@ def parse_single(self, program): return next(self._parse_all(program)) def load_py_module(self, name): - """Loads the give python module""" + """Loads the given python module""" if not isinstance(name, str): name = repr(name) mod = import_module(name) @@ -68,7 +68,7 @@ def load_py_module(self, name): obj(self) def import_file(self, fname): - """Loads the program file and run it""" + """Loads the program file and runs it""" path = fname.split(os.sep) if len(path) == 1: path = ['.'] + path From 81682fc3d9c3fed82f7c089de9bebc858a9fdcf9 Mon Sep 17 00:00:00 2001 From: glicerico Date: Fri, 8 Sep 2023 16:34:06 -0600 Subject: [PATCH 21/34] Comment base.py --- python/hyperon/base.py | 189 ++++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 50 deletions(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 13940856f..d5350cebc 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -7,11 +7,13 @@ class AbstractSpace: A virtual base class upon which Spaces can be implemented in Python """ def __init__(self): + """Initialiize the AbstractSpace. Does nothing in the base class""" return def query(self, query_atom): """ - Performs the specified query on the Space, and returns the result BindingsSet + Performs the specified query on the Space. + Should be overridden to return a BindingsSet as the result of the query. """ raise RuntimeError("Space::query() is not implemented") @@ -21,38 +23,50 @@ def query(self, query_atom): # None def add(self, atom): - """Adds an atom to the atom space""" + """ + Adds an Atom to the atom space. Must be implemented in derived classes. + """ raise RuntimeError("Space::add() is not implemented") def remove(self, atom): - """Removes an atom from the atom space""" + """ + Removes an Atom from the atom space. Must be implemented in derived classes. + """ raise RuntimeError("Space::remove() is not implemented") def replace(self, atom, replacement): - """Replaces an atom in the atom space""" + """ + Replaces an Atom from the atom space. Must be implemented in derived classes. + """ raise RuntimeError("Space::replace() is not implemented") def atom_count(self): - """Counts the number of atoms in the atom space""" + """ + Counts the number of atoms in the atom space. Optional for derived classes. + """ None def atoms_iter(self): - """Gets the atom iterator""" + """ + Returns an iterator over atoms in the Space. Optional for derived classes. + """ None class GroundingSpace(AbstractSpace): """ - A wrapper over the native GroundingSpace implementation, that can be subclassed + A wrapper over the native GroundingSpace implementation, which can be subclassed and extended within Python """ def __init__(self, unwrap=True): + """Initialize GroundingSpace and its underlying native implementation.""" super().__init__() # self.cspace = hp.space_new_grounding() self.gspace = GroundingSpaceRef() def query(self, query_atom): """ - Performs the specified query on the Space, and returns the result BindingsSet + Delegates the query to the underlying native GroundingSpace + and returns the result BindingsSet """ return self.gspace.query(query_atom) @@ -60,49 +74,67 @@ def query(self, query_atom): # def subst(self, pattern, templ): def add(self, atom): - """Adds an atom to the atom space""" + """ + Adds an Atom to the atom space. + """ self.gspace.add_atom(atom) def remove(self, atom): - """Removes an atom from the atom space""" + """ + Removes an Atom from the atom space. + """ return self.gspace.remove_atom(atom) def replace(self, from_atom, to_atom): - """Replaces an atom in the atom space""" + """ + Replaces an Atom in the atom space. + """ return self.gspace.replace_atom(from_atom, to_atom) def atom_count(self): - """Counts the number of atoms in the atom space""" + """ + Counts the number of Atoms in the atom space. + """ return self.gspace.atom_count() def atoms_iter(self): - """Gets the atom iterator""" + """ + Returns an iterator over atoms in the atom space. + """ return iter(self.gspace.get_atoms()) def _priv_call_query_on_python_space(space, query_catom): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Translates a native 'catom' into an Atom object, and then delegates the query + to the provided 'space' object. """ query_atom = Atom._from_catom(query_catom) return space.query(query_atom) def _priv_call_add_on_python_space(space, catom): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Translates a native 'catom' into an Atom object, and then adds it + to the provided 'space' object. """ atom = Atom._from_catom(catom) space.add(atom) def _priv_call_remove_on_python_space(space, catom): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Translates a native 'catom' into an Atom object, and then removes it + from the provided 'space' object. """ atom = Atom._from_catom(catom) return space.remove(atom) def _priv_call_replace_on_python_space(space, cfrom, cto): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Translates native 'catom' objects into Atom objects, and then replaces + the first with the second in the provided 'space' object. """ from_atom = Atom._from_catom(cfrom) to_atom = Atom._from_catom(cto) @@ -110,7 +142,8 @@ def _priv_call_replace_on_python_space(space, cfrom, cto): def _priv_call_atom_count_on_python_space(space): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Returns the number of Atoms in the provided 'space' object. """ if hasattr(space, "atom_count"): count = space.atom_count() @@ -123,7 +156,8 @@ def _priv_call_atom_count_on_python_space(space): def _priv_call_new_iter_state_on_python_space(space): """ - Private glue for Hyperonpy implementation + Private glue for Hyperonpy implementation. + Returns an iterator over Atoms in the provided 'space' object. """ if hasattr(space, "atoms_iter"): return space.atoms_iter() @@ -133,58 +167,67 @@ def _priv_call_new_iter_state_on_python_space(space): class SpaceRef: """ A reference to a Space, which may be accessed directly, wrapped in a grounded atom, - or passed to a MeTTa interpreter + or passed to a MeTTa interpreter. """ def __init__(self, space_obj): + """ + Initialize a new SpaceRef based on the given space object, either a CSpace + or a custom Python object. + """ if type(space_obj) is hp.CSpace: self.cspace = space_obj else: self.cspace = hp.space_new_custom(space_obj) def __del__(self): + """Free the underlying CSpace object """ hp.space_free(self.cspace) def __eq__(self, other): + """Compare two SpaceRef objects for equality, based on their underlying spaces.""" return hp.space_eq(self.cspace, other.cspace) @staticmethod def _from_cspace(cspace): + """ + Create a new SpaceRef based on the given CSpace object. + """ return SpaceRef(cspace) def copy(self): """ - Returns a new copy of the SpaceRef, referencing the same underlying Space + Returns a new copy of the SpaceRef, referencing the same underlying Space. """ return self def add_atom(self, atom): """ - Add an Atom to the Space + Add an Atom to the Space. """ hp.space_add(self.cspace, atom.catom) def remove_atom(self, atom): """ - Delete the specified atom from the Space + Delete the specified Atom from the Space. """ return hp.space_remove(self.cspace, atom.catom) def replace_atom(self, atom, replacement): """ - Replace the specified Atom, if it exists in the Space, with the supplied replacement Atom + Replaces an existing Atom in the Space with a new one. """ return hp.space_replace(self.cspace, atom.catom, replacement.catom) def atom_count(self): """ - Returns the number of Atoms in the Space, or -1 if it cannot readily computed + Returns the number of Atoms in the Space, or -1 if it cannot be readily computed. """ return hp.space_atom_count(self.cspace) def get_atoms(self): """ - Returns a list of all Atoms in the Space, or None if that is impossible + Returns a list of all Atoms in the Space, or None if that is impossible. """ res = hp.space_list(self.cspace) if res == None: @@ -197,20 +240,20 @@ def get_atoms(self): def get_payload(self): """ Returns the Space object referenced by the SpaceRef, or None if the object does not have a - direct Python interface + direct Python interface. """ return hp.space_get_payload(self.cspace) def query(self, pattern): """ - Performs the specified query on the Space, and returns the result BindingsSet + Performs the specified query on the Space, and returns the result as a BindingsSet. """ result = hp.space_query(self.cspace, pattern.catom) return BindingsSet(result) def subst(self, pattern, templ): """ - Performs a substitution within the Space + Performs a substitution within the Space, based on a pattern and a template. """ return [Atom._from_catom(catom) for catom in hp.space_subst(self.cspace, pattern.catom, @@ -218,10 +261,16 @@ def subst(self, pattern, templ): class GroundingSpaceRef(SpaceRef): """ - A reference to a native GroundingSpace, implemented by the MeTTa core library + A reference to a native GroundingSpace, implemented by the MeTTa core library. + This class extends SpaceRef to provide the same set of functionalities, + specifically for GroundingSpaces. """ def __init__(self, cspace = None): + """ + Initialize a new GroundingSpaceRef. + If a CSpace object is provided, use it; otherwise create a new GroundingSpace. + """ if cspace is None: self.cspace = hp.space_new_grounding() else: @@ -229,11 +278,21 @@ def __init__(self, cspace = None): @staticmethod def _from_cspace(cspace): + """ + Creates a GroundingSpaceRef from a CSpace object. + """ return GroundingSpaceRef(cspace) class Tokenizer: + """ + A class responsible for text tokenization in the context of Hyperon. + This class wraps around a CTokenizer object from the core library. + """ def __init__(self, ctokenizer = None): + """ + Initialize a new Tokenizer. + """ if ctokenizer is None: self.ctokenizer = hp.tokenizer_new() else: @@ -241,75 +300,104 @@ def __init__(self, ctokenizer = None): @staticmethod def _from_ctokenizer(ctokenizer): + """ + Creates a Tokenizer from a CTokenizer object. + """ return Tokenizer(ctokenizer) def __del__(self): + """ + Destructor that frees the CTokenizer object when the Tokenizer instance is destroyed. + """ hp.tokenizer_free(self.ctokenizer) def register_token(self, regex, constr): - """Registers a new custom Token in a Tokenizer. - - Hyperon uses the Rust RegEx engine and syntax - https://docs.rs/regex/latest/regex/ + """ + Registers a new custom Token in the Tokenizer based on a regular expression. - Parameters + Parameters: ---------- - regex: - A regular expression to match the incoming text, triggering this token to - generate a new atom - """ + regex: + A string representing the regular expression to match incoming text. + Hyperon uses the Rust RegEx engine and syntax. + constr: + A constructor function for generating a new atom when the regex is triggered. + """ hp.tokenizer_register_token(self.ctokenizer, regex, constr) class SExprParser: + """ + A class responsible for parsing S-expressions (Symbolic Expressions). + This class wraps around a CSExprParser object from the core library. + """ def __init__(self, text): + """Initialize a new SExprParser object.""" self.cparser = hp.CSExprParser(text) def parse(self, tokenizer): + """ + Parses the S-expression using the provided Tokenizer. + """ catom = self.cparser.parse(tokenizer.ctokenizer) return Atom._from_catom(catom) if catom is not None else None class Interpreter: + """ + A wrapper class for the MeTTa interpreter that handles the interpretation of expressions in a given grounding space. + """ def __init__(self, gnd_space, expr): + """ + Initializes the interpreter with the given grounded space and expression. + """ self.step_result = hp.interpret_init(gnd_space.cspace, expr.catom) def has_next(self): - """Examines a Step Result to determine if more work is needed.""" + """ + Checks if there are more steps to execute in the interpretation plan. + """ return hp.step_has_next(self.step_result) def next(self): - """Moves to the next Step Result""" + """ + Executes the next step in the interpretation plan. + """ if not self.has_next(): raise StopIteration() self.step_result = hp.interpret_step(self.step_result) def get_result(self): - """Consumes a Step Result and provides the ultimate outcome of a MeTTa - interpreter session. + """ + Retrieves the final outcome of the interpretation plan. """ if self.has_next(): raise RuntimeError("Plan execution is not finished") return hp.step_get_result(self.step_result) def get_step_result(self): - """Gets the current Step Result""" + """ + Gets the current result of the interpretation plan. + """ return self.step_result def interpret(gnd_space, expr): - """Parses the expression in the grounded space""" + """ + Parses the given expression in the specified grounded space. + """ interpreter = Interpreter(gnd_space, expr) while interpreter.has_next(): interpreter.next() return [Atom._from_catom(catom) for catom in interpreter.get_result()] def check_type(gnd_space, atom, type): - """Checks whether the atom has the type `type` in context of space + """ + Checks whether the given Atom has the specified type in the given space context. Parameters ---------- - space: + gnd_space: A pointer to the space_t representing the space context in which to perform the check atom: @@ -322,7 +410,8 @@ def check_type(gnd_space, atom, type): return hp.check_type(gnd_space.cspace, atom.catom, type.catom) def validate_atom(gnd_space, atom): - """Checks whether atom is correctly typed. + """ + Checks whether the given Atom is correctly typed. Parameters ---------- @@ -335,11 +424,11 @@ def validate_atom(gnd_space, atom): Returns ------- - if the Atom is correctly typed, otherwise false + True if the Atom is correctly typed, otherwise false """ return hp.validate_atom(gnd_space.cspace, atom.catom) def get_atom_types(gnd_space, atom): - """Provides all types for atom in the context of space""" + """Provides all types for the given Atom in the context of the given Space.""" result = hp.get_atom_types(gnd_space.cspace, atom.catom) return [Atom._from_catom(catom) for catom in result] From 08c302e514a73d97e1ec1dc9f4cc697b7f3aad5f Mon Sep 17 00:00:00 2001 From: glicerico Date: Wed, 13 Sep 2023 18:08:55 -0600 Subject: [PATCH 22/34] Address comments --- python/hyperon/atoms.py | 11 +++++------ python/hyperon/base.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 431a38733..2f0ba9871 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -196,8 +196,7 @@ def copy(self): """ Returns a copy of this GroundedObject instance. - Note: Currently, this method returns the original - instance, effectively making the GroundedObject immutable. + Note: Currently, this method returns the original instance. """ return self @@ -226,7 +225,7 @@ def __eq__(self, other): return isinstance(other, ValueObject) and self.content == other.content class NoReduceError(Exception): - """Custom exception raised when a reduction operation cannot be performed.""" + """Custom exception; raised when a reduction operation cannot be performed.""" pass class OperationObject(GroundedObject): @@ -496,12 +495,12 @@ def resolve(self, var_name: str) -> Union[Atom, None]: return None if raw_atom is None else Atom._from_catom(raw_atom) def resolve_and_remove(self, var_name: str) -> Union[Atom, None]: - """Finds anr removes the atom for a given variable name""" + """Finds and removes the atom for a given variable name""" raw_atom = hp.bindings_resolve_and_remove(self.cbindings, var_name) return None if raw_atom is None else Atom._from_catom(raw_atom) def iterator(self): - """Iterates through all variable-atom pairs in the bindings""" + """Returns an iterator over the variable-atom pairs in the bindings""" res = hp.bindings_list(self.cbindings) result = [] for r in res: @@ -617,7 +616,7 @@ def merge_into(self, input: Union['BindingsSet', Bindings]): hp.bindings_set_merge_into(self.c_set, new_set.c_set); def iterator(self): - """Iterates through all Bindings frames.""" + """Returns an iterator over all Bindings frames""" res = hp.bindings_set_list(self.c_set) result = [] for r in res: diff --git a/python/hyperon/base.py b/python/hyperon/base.py index d5350cebc..43fe46a12 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -215,7 +215,7 @@ def remove_atom(self, atom): def replace_atom(self, atom, replacement): """ - Replaces an existing Atom in the Space with a new one. + Replaces the specified Atom, if it exists in the Space, with the supplied replacement. """ return hp.space_replace(self.cspace, atom.catom, replacement.catom) From 797715b4b1332a1c5773cd6c174cd42dda8bb89b Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:42:26 +0300 Subject: [PATCH 23/34] Update python/hyperon/atoms.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/atoms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 395951d37..b33a7113a 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -45,7 +45,7 @@ def match_atom(self, b): @staticmethod def _from_catom(catom): - """Constructs an Atom from C Atom of the same type""" + """Constructs an Atom by wrapping a C Atom""" type = hp.atom_get_type(catom) if type == AtomKind.SYMBOL: return SymbolAtom(catom) From 8a60a07a9827e4792e60449b0bbf1adf8247a06f Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:42:40 +0300 Subject: [PATCH 24/34] Update python/hyperon/atoms.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/atoms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index b33a7113a..55e6d867b 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -67,7 +67,7 @@ def __init__(self, catom): super().__init__(catom) def get_name(self): - """Renders the name of the Atom into a text buffer.""" + """Returns the name of the Atom.""" return hp.atom_get_name(self.catom) def S(name): From bad2fd720286aaabc5128230362cc11a91694070 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:42:49 +0300 Subject: [PATCH 25/34] Update python/hyperon/atoms.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/atoms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 55e6d867b..57e137faf 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -83,7 +83,7 @@ def __init__(self, catom): super().__init__(catom) def get_name(self): - """Renders the name of the Atom into a text buffer.""" + """Returns the name of the Atom.""" return hp.atom_get_name(self.catom) def V(name): From 1a854ebaad9d9e4d34ceaadb922c0d1fdcf1774d Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:43:03 +0300 Subject: [PATCH 26/34] Update python/hyperon/atoms.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/atoms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 57e137faf..1b6ad5d1a 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -98,7 +98,7 @@ def __init__(self, catom): super().__init__(catom) def get_children(self): - """Gets all children Atoms""" + """Returns all children Atoms of an expression""" return [Atom._from_catom(catom) for catom in hp.atom_get_children(self.catom)] From ecabfa797f4c5293f3de8c86a09850d05297e169 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:43:19 +0300 Subject: [PATCH 27/34] Update python/hyperon/atoms.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/atoms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 1b6ad5d1a..6dcd4c0e1 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -139,7 +139,7 @@ def __init__(self, catom): super().__init__(catom) def get_object(self): - """Gets the GroundedAtom object, or the space wrapped inside a GroundedAtom""" + """Returns the GroundedAtom object, or the Space wrapped inside a GroundedAtom""" from .base import SpaceRef if self.get_grounded_type() == AtomType.GROUNDED_SPACE: return SpaceRef._from_cspace(hp.atom_get_space(self.catom)) From 06e238e5816fecc21fb70a17fab31a10385f06e3 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:43:38 +0300 Subject: [PATCH 28/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 43fe46a12..5ea61a1ff 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -286,7 +286,7 @@ def _from_cspace(cspace): class Tokenizer: """ A class responsible for text tokenization in the context of Hyperon. - This class wraps around a CTokenizer object from the core library. + This class wraps around a Tokenizer object from the core library. """ def __init__(self, ctokenizer = None): From 329a1a91708c879aec4dbea7af159f5008e60785 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:44:00 +0300 Subject: [PATCH 29/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 5ea61a1ff..624d79f81 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -307,7 +307,7 @@ def _from_ctokenizer(ctokenizer): def __del__(self): """ - Destructor that frees the CTokenizer object when the Tokenizer instance is destroyed. + Destructor that frees the underlying resources when the Tokenizer instance is destroyed. """ hp.tokenizer_free(self.ctokenizer) From 955fd839018fe10650519c8ff95607f78d22baeb Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:44:53 +0300 Subject: [PATCH 30/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 624d79f81..f5f2693f9 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -328,7 +328,7 @@ def register_token(self, regex, constr): class SExprParser: """ A class responsible for parsing S-expressions (Symbolic Expressions). - This class wraps around a CSExprParser object from the core library. + This class wraps around a SExprParser object from the core library. """ def __init__(self, text): From fd7088c7ca5c6944c88ce6e8299c00f8ff0b583a Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:45:07 +0300 Subject: [PATCH 31/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index f5f2693f9..634b8d122 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -384,7 +384,7 @@ def get_step_result(self): def interpret(gnd_space, expr): """ - Parses the given expression in the specified grounded space. + Parses the given expression in the specified grounding space. """ interpreter = Interpreter(gnd_space, expr) while interpreter.has_next(): From 8d5ff9bd401fbe5d0aa2aa0d7509a2d9f3dd34c2 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:45:32 +0300 Subject: [PATCH 32/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 634b8d122..c9d996239 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -349,7 +349,7 @@ class Interpreter: def __init__(self, gnd_space, expr): """ - Initializes the interpreter with the given grounded space and expression. + Initializes the interpreter with the given grounding space and expression. """ self.step_result = hp.interpret_init(gnd_space.cspace, expr.catom) From b2aaacfa735f6baae4df3cd78bbe74935d292c5a Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:47:55 +0300 Subject: [PATCH 33/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index c9d996239..15d0ec2e5 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -263,7 +263,6 @@ class GroundingSpaceRef(SpaceRef): """ A reference to a native GroundingSpace, implemented by the MeTTa core library. This class extends SpaceRef to provide the same set of functionalities, - specifically for GroundingSpaces. """ def __init__(self, cspace = None): From 5694a801e7172f1e05189ed14058a63f6caa8ffa Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Thu, 14 Sep 2023 10:48:43 +0300 Subject: [PATCH 34/34] Update python/hyperon/base.py Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperon/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/hyperon/base.py b/python/hyperon/base.py index 15d0ec2e5..7b49edde9 100644 --- a/python/hyperon/base.py +++ b/python/hyperon/base.py @@ -262,7 +262,6 @@ def subst(self, pattern, templ): class GroundingSpaceRef(SpaceRef): """ A reference to a native GroundingSpace, implemented by the MeTTa core library. - This class extends SpaceRef to provide the same set of functionalities, """ def __init__(self, cspace = None):