diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 6f55660..38b07a2 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -8,6 +8,7 @@ on: branches: - main - develop + - develop_carsten pull_request: branches: - main @@ -38,15 +39,6 @@ jobs: run: | python -m pip install --upgrade pip - - name: get ocse as test data - run: | - mkdir -p erk-data-for-unittests - cd erk-data-for-unittests - echo "PYERK_TEST_DATA_PARENT_PATH=$(pwd)" >> "$GITHUB_ENV" - git clone https://github.com/ackrep-org/ocse.git - cd ocse - git checkout ut__pyerk__main - - name: debug env run: | echo "$PYERK_TEST_DATA_PARENT_PATH" diff --git a/README.md b/README.md index f6a1539..ed2c740 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![PyPI version](https://badge.fury.io/py/pyerk.svg)](https://pypi.org/project/pyerk/) -[![Documentation Status](https://readthedocs.org/projects/pyerk-core/badge/?version=latest)](https://pyerk-core.readthedocs.io/en/latest) -![ci](https://github.com/ackrep-org/pyerk-core/actions/workflows/python-app.yml/badge.svg) +[![PyPI version](https://badge.fury.io/py/pyirk.svg)](https://pypi.org/project/pyirk/) +[![Documentation Status](https://readthedocs.org/projects/pyirk-core/badge/?version=latest)](https://pyirk-core.readthedocs.io/en/latest) +![ci](https://github.com/ackrep-org/pyirk-core/actions/workflows/python-app.yml/badge.svg) -# Overview: pyerk +# Overview: pyirk -Pyerk is the python implementation of the ***e**mergent **r**epresentation of **k**nowledge* framework. +Pyirk is a Python framework for ***i**mperative **r**epresentation of **k**nowledge*. - Designed to formally represent knowledge (including meta levels) - Implementation-status: "early alpha" @@ -14,48 +14,37 @@ Pyerk is the python implementation of the ***e**mergent **r**epresentation of ** - ... at the cost of guarantied computability - Inspired by Wikidata, but much simpler - Inspired by SUO-KIF, but with less brackets -- Represented directly in python: → imperative instaead of declarative knowledge representation +- Represented directly in python: → imperative instead of declarative knowledge representation -Pyerk originated and is currently (2022) mainly developed with focus on representing knowledge from the domain of *control theory* as part of the *Automatic Control Knowledge Repository ([ACKREP](https://ackrep.org))*. However, in principle, it aims to be applicable wo a wide range of domains. +While pyirk aims to be applicable to a wide range of knowledge domains, its origin an its current (2023) main focus is the representation of knowledge from the domain of *control theory* as part of the *Automatic Control Knowledge Repository ([ACKREP](https://ackrep.org))*. +Thus, a subset of the [Ontology of Control Systems Engineering](https://github.com/ackrep-org/ocse) is used as test data for pyirk. In fact, both projects are practically co-developed. -# Assumed Directory Structure +Not that, originally pyirk was called pyerk (imperative representation of knowledge), in case you come across some old version. + +# Recommended Directory Structure ``` -/ -├── pyerk/ ← repo with the code of the core package +/ +├── pyirk-core/ ← repo with the code of the core package │ ├── .git/ │ ├── README.md ← the currently displayed file (README.md) │ ├── setup.py ← deployment script -│ ├── src/pyerk/auxiliary.py ← module containing function get_erk_root_dir() +│ ├── src/pyirk/auxiliary.py ← module containing function get_irk_root_dir() │ └── ... │ -├── django-erk-gui/ ← repo with the code for the django gui (project and! app) +├── django-irk-gui/ ← repo with the code for the django gui (project and! app) │ │ (this package is optional) │ ├── .git/ │ ├── manage.py │ └── ... -│ -├── erk-data/ ← directory that contains erk-knowledge packages (for actual usage) -│ ├── ocse/ ← a knowledge package (ontology of control systems engineering) -│ │ ├── .git/ -│ │ ├── README.md -│ │ ├── ocse.py ← a knowledge module -│ │ │ (this one is currently used for unit testing) -│ │ └── ... -│ ├── xyz123/ ← another knowledge package -│ │ └── ... -│ └── ... -├── erk-data-for-unittests/ ← directory that contains erk-knowledge packages -│ └── ... (unittest version, probably older and different from -│ production data.) └──... ``` # Documentation -Rudimentary documentation is available at (generated from the [`/docs`](/docs) directory). To get an overview of the most importent features you might also want to have a look at the source code, especially at the files [builtin_entities.py](/src/pyerk/builtin_entities.py) and the [test_core.py](tests/test_core.py). +Rudimentary documentation is available at (generated from the [`/docs`](/docs) directory). To get an overview of the most important features you might also want to have a look at the source code, especially at the files [builtin_entities.py](/src/pyirk/builtin_entities.py) and the test cases, e.g., [test_core.py](tests/test_core.py). # Coding style diff --git a/docs/README.md b/docs/README.md index 4f02d5b..5ef3d1f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,4 +6,9 @@ Run `pip install -r requirements.txt` in this directory to install the dependenc Run `make html` in this directory to build the docs on your system. See `build/html` for the result. -Automatically generated documentation is available at: . (Be sure to select the branch of interest from the menu in the lower right). +Automatically generated documentation is available at: . (Be sure to select the branch of interest from the menu in the lower right). + + +### Debug Readthedocs Build + +In `/docs/source` run `python -m sphinx -T -E -W --keep-going -b html -d _build/doctrees -D language=en . build/html`. diff --git a/docs/source/conf.py b/docs/source/conf.py index 9009123..0d6f980 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,16 +17,16 @@ sys.path.insert(0, pathlib.Path(__file__).parents[2].resolve().as_posix()) # print(sys.path[0]) -import pyerk +import pyirk # -- Project information ----------------------------------------------------- -project = "pyerk" +project = "pyirk" copyright = "2022, Ackrep Team" author = "Carsten Knoll et. al." # The full version, including alpha/beta/rc tags -release = pyerk.__version__ +release = pyirk.__version__ # -- General configuration --------------------------------------------------- diff --git a/docs/source/devdoc/overview.md b/docs/source/devdoc/overview.md index 6c7694a..39fcf4e 100644 --- a/docs/source/devdoc/overview.md +++ b/docs/source/devdoc/overview.md @@ -1,3 +1,50 @@ (sec_devdoc_overview)= -# pyerk Developer Documentation Overview +# pyirk Developer Documentation Overview + + +(sec_test_data)= +## Test Data + +The directory `tests/test_data/ocse_subset` contains an autogenerated subset of the OCSE (ontology of control systems engineering) which is used as real world testing data. + +Originally the "real" OCSE data was used for testing. This had the advantage of avoiding double maintaince effort (OCSE and testing data) but led to increased runtimes of the test suite with the growing number of entities and statements in the OCSE. This is because the test data is loaded and unloaded multiple times to keep the tests (mostly) independent. + +To keep the test data small but avoid additional maintaince effort we now automatically extract the relevant entities and statements from the real ocse. + +```bash +pyirk -utd +``` + +Which entities (and objects) are relevant is specified in the `ocse_subset/templates` subdirectory of `testdata`. The syntax of the template files is as follows: + +```python + +# ... +insert_entities = [ + +# copy all physical lines which are part of the entity definition +I9841["vector field"], +I4236["mathematical expression"], +# ... + +# copy all physical lines which are part of the respective context manager +with__I9907.scope("setting"), +with__I9907.scope("premise"), +with__I9907.scope("assertion"), +# ... + +# copy all physical lines which are part of the class or function definition +class__IntegerRangeElement, +def__create_person, +# ... + +# insert the line as is beginning after 'raw__' +raw__I4237["monovariate rational function"].add_method(p.create_evaluated_mapping, "_custom_call"), +# ... + + +] + + +``` diff --git a/docs/source/index.md b/docs/source/index.md index b252385..5a2ddd5 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -1,6 +1,6 @@ -# Python Based Emergent Representation of Knowledge (Pyerk) +# Python Based Imperative Representation of Knowledge (Pyirk) -Pyerk is an experimental framework for *imperative* knowledge representation. I is basically a collection of classes and functions which facilitates the construction of and the interaction with knowledge graphs. +Pyirk is an experimental framework for *imperative* knowledge representation. I is basically a collection of classes and functions which facilitates the construction of and the interaction with knowledge graphs. The framewok is structurally inspired by @@ -12,7 +12,7 @@ The framewok is structurally inspired by - [Suggested Upper Merged Ontology (SUMO)](https://www.ontologyportal.org/) - modelling of higher order statements -However, pyerk is a pure python framework and aims to be intuitively usable without prior familiarity with knowledge engineering techniques such as OWL, but instead requiring only some understanding of programming. +However, pyirk is a pure python framework and aims to be intuitively usable without prior familiarity with knowledge engineering techniques such as OWL, but instead requiring only some understanding of programming. ## Motivation @@ -22,17 +22,17 @@ While library catalogues, full text search engines and similar facilities help a One reason is that the available technologies like OWL (based on so called [description logic](https://en.wikipedia.org/wiki/description_logic)) are widely considered to be hard to grasp by non-specialists. The current mainstream approach of formal knowledge representation thus consists the cooperation between domain-experts and knowledge engineers to create useful knowledge bases for specific domains. -In contrast, Pyerk aims to enable domain-experts themselves to create such knowledge bases, by providing an interface in the widespread Python programming language, which poses a significantly lower barrier compared to the special purpose OWL. +In contrast, Pyirk aims to enable domain-experts themselves to create such knowledge bases, by providing an interface in the widespread Python programming language, which poses a significantly lower barrier compared to the special purpose OWL. -Pyerk also aims to allow for much greater [expressive power](https://en.wikipedia.org/wiki/Expressive_power_(computer_science)) (e.g. allowing higher order logic statements) than most other approaches, despite the computational consequences. +Pyirk also aims to allow for much greater [expressive power](https://en.wikipedia.org/wiki/Expressive_power_(computer_science)) (e.g. allowing higher order logic statements) than most other approaches, despite the computational consequences. ## Status -The whole Pyerk project and even much more this documentation is currently still under development and should be considered as incomplete and only partially functional. Nevertheless some usefulness cannot be excluded. +The whole Pyirk project and even much more this documentation is currently still under development and should be considered as incomplete and only partially functional. Nevertheless some usefulness cannot be excluded. ## User Documentation -Information regarding the local usage of pyerk. +Information regarding the local usage of pyirk. ```{toctree} :maxdepth: 1 :caption: User Documentation @@ -43,7 +43,7 @@ userdoc/overview ## Developer Documentation -Information regarding actively contributing to pyerk. +Information regarding actively contributing to pyirk. ```{toctree} :maxdepth: 1 :caption: Developer Documentation diff --git a/docs/source/userdoc/cli.rst b/docs/source/userdoc/cli.rst index e514634..28e8596 100644 --- a/docs/source/userdoc/cli.rst +++ b/docs/source/userdoc/cli.rst @@ -1,5 +1,5 @@ .. argparse:: - :module: pyerk.script + :module: pyirk.script :func: create_parser - :prog: pyerk + :prog: pyirk diff --git a/docs/source/userdoc/overview.md b/docs/source/userdoc/overview.md index 04ae1bd..a99eede 100644 --- a/docs/source/userdoc/overview.md +++ b/docs/source/userdoc/overview.md @@ -1,21 +1,21 @@ (sec_userdoc_overview)= -# pyerk User Documentation Overview +# Pyirk User Documentation Overview (sec_keys)= -## Keys in Pyerk +## Keys in Pyirk -In Pyerk there are the following kinds of keys: +In Pyirk there are the following kinds of keys: - a) short_key like `"R1234"` - b) name-labeled key like `"R1234__my_relation"` (consisting of a short_key, a delimiter (`__`) and a label) -- c) prefixed short_key like `"bi__R1234"` (here the prefix `bi` referse to the module `builtin_entities`) +- c) prefixed short_key like `"bi__R1234"` (here the prefix `bi` refers to the module `builtin_entities`) - d) prefixed name-labeled key like `"bi__R1234__my_relation"` -- e) index-labeld key like `"R1234['my relation']"` +- e) index-labeled key like `"R1234['my relation']"` -Note: prefixed and name-labled keys can optionally have a language indicator. Examples: ``"bi__R1__de"`` or `"R1__has_label__fr"`. +Note: prefixed and name-labeled keys can optionally have a language indicator. Examples: ``"bi__R1__de"`` or `"R1__has_label__fr"`. Also, the leading character indicates the entity type (called `EType` in the code): `I` → item, `R` → relation. -The usage of these syntax variants depens on the context. +The usage of these syntax variants depends on the context. For more information see See also {ref}`sec_modules`. @@ -27,7 +27,7 @@ For more information see See also {ref}`sec_modules`. Currently there is some basic visualization support via the command line. To visualize your a module (including its relations to the builtin_entities) you can use a command like ``` -pyerk --load-mod demo-module.py demo -vis __all__ +pyirk --load-mod demo-module.py demo -vis __all__ ``` (sec_cli_overview)= @@ -36,7 +36,7 @@ pyerk --load-mod demo-module.py demo -vis __all__ For an overview of available command line options, see the [CLI page](cli) or the command: ``` -pyerk -h +pyirk -h ``` ### Interactive Usage @@ -44,19 +44,29 @@ pyerk -h To open an IPython shell with a loaded module run e.g. ``` -pyerk -i -l control_theory1.py ct +pyirk -i -l control_theory1.py ct ``` -Then, you have `ct` as variable in your namespace and can e.g. run `print(ct.I5167.R1`). +Then, you have `ct` as variable in your namespace and can e.g. run `print(ct.I5167.R1)`. (The above command assumes that the file `control_theory1.py` is in your current working directory.) + +### Update Test Data + +``` +pyirk --update-test-data +``` + +For details see [devdoc#test_data](sec_test_data) + + ## Multilinguality -Pyerk aims to support an arbitrary number of languages by so called *language specified strings*. Currently support for English and German is preconfigured in `pyerk.settings`. These language specified strings are instances of the class `rdflib.Literal` where the `.language`-attribute is set to one of the values from `pyerk.setting.SUPPORTED_LANGUAGES` and can be created like: +Pyirk aims to support an arbitrary number of languages by so called *language specified strings*. Currently support for English and German is preconfigured in `pyirk.settings`. These language specified strings are instances of the class `rdflib.Literal` where the `.language`-attribute is set to one of the values from `pyirk.setting.SUPPORTED_LANGUAGES` and can be created like: ```python -from pyerk import de, en +from pyirk import de, en # ... @@ -64,18 +74,96 @@ lss1 = "some english words"@en lss2 = "ein paar deutsche Wörter"@de ``` -where `en, de` are instances of `pyerk.core.LangaguageCode`. +where `en, de` are instances of `pyirk.core.LanguageCode`. + +The usage inside Pyirk is best demonstrated by the unittest `test_c02__multilingual_relations`, see [test_core.py](https://github.com/ackrep-org/pyirk-core/blob/main/tests/test_core.py) (maybe change branch). + + + +(sec_patterns)= +## Patterns for Knowledge Representation in Pyirk + + +In Pyirk knowledge is represented via *entities* and *statements* (inspired by Wikidata). There are two types of entities: *items* (mostly associated with nouns) and *relations* (mostly associated with verbs). Statements consist of *subject-predicate-object*-triples. + +- subject: can be any entity (item or a relation), +- predicate: is always a relation, +- object: can be any entity or *literal*. + +Literals are "atomic" values like strings, numbers or boolean values. -The usage inside pyerk is best demonstrated by the unittest `test_c02__multilingual_relations`, see [test_core.py](https://github.com/ackrep-org/pyerk-core/blob/main/tests/test_core.py) (maybe change branch). +Every entity has short_key (`entity.short_key`, see also {ref}`sec_keys`.) and an uri (`entity.uri`). It is recommended but not required that every entity has a label (by means of relation `R1["has label"]`) and a description (by means of `R2["has description"]`). +(sec_items)= +### Items (Python Subclass of `core.Entity`) + +The `short_key` of any items starts with "`I`" and ends with a sequence of number characters (maximum sequence length not yet specified). Optionally the second character is "`a`" which indicates that this item was generated automatically (see [below](sec_auto_gen_items)). + +(Almost) All items are part of a taxonomy, i.e. a hierarchy of *"is-a"*-relations). This is expressed by the relations `R3["is_subclass_of"]` and `R4["is instance of"]`. + + +```{note} +Unlike in OWL (but like in Wikidata) an item can be an instance and a class at the same time. This allows to treat classes as "ordinary" items if necessary, e.g. use them directly in statements. +``` + + +(sec_auto_gen_items)= +#### Automatically Generated Items + +One consequence of expressing knowledge as a collection of triples is the necessity of auxiliary items. E.g. consider the equation {math}`y = \sin(x)` where `x, y, sin` can be assumed to be well defined items. Because the predicate must be a relation, it is not possible to relate these three items in one triple. The usual approach to deal with such situations is to introduce auxiliary items and more triples (see also [wikipedia on "reification"](https://en.wikipedia.org/wiki/Reification_(knowledge_representation))). One possible (fictional) triple representation of the above equation is + +``` +auxiliary_expr is_function_call_of_type sin +auxiliary_expr has_arg x +y is_equal_to expr +``` + +One of the main goals of Pyirk is to simplify the creation of triples which involves creating auxiliary items (such as evaluated expressions). This can be achieved by calling functions such as `pyirk.instance_of(...)`. A more sophisticated way is to overload the `__call__` method of entities. + + +(sec___call__mechanism)= +#### The `__call__`-Method + +The class `pyirk.Entity` implements the `__call__` method which formally makes all items and relations callable Python objects. However, by default no method `_custom_call` is implemented which results in an exception. Associating a `_custom_call` method and thus truly make an item callable can be achieved by + +- explicitly adding the method, like e.g. in `I4895["mathematical operator"].add_method(p.create_evaluated_mapping, "_custom_call")` +- creating an item which is a subclass (`R3`) or instance (`R4`) of a method which already has a `_custom_call` method, see `core.Entity._perform_inheritance` and `core.Entity._perform_instantiation` for details. + + +(sec___adding_convenience_methods)= +#### Adding Convenience Methods + +The method `core.Entity.add_method(...)` can be used to add arbitrary methods to items (which then can be inherited by other items). Example: see how the function `builtin_entities.get_arguments` is attached to every result of `builtin_entities.create_evaluated_mapping` (which itself is used as `_custom_call` method). + + +(sec_relations)= +### Relations (`core.Relation`, subclass of `core.Entity`) + +The `.short_key` of any relation starts with `R`. The *predicate* part of a semantic triple must always be a (python) instance of `Core.Relation` . In general they can occur as *subject* or *object* as well. + +From a graph perspective the relation defines the type of the edge between two nodes. The nodes are typically `Item`-instances. + +(sec_Literals)= +### Literals (`core.Literal` imported from `rdflib`) + +Instances of the class model string values (including a `.language` attribute). + +(sec_Statements)= +### Statements (`core.Statement`) + +Instances of this class model semantic triples (subject, predicate, object) and corresponding [qualifiers](sec_qualifiers). Every edge in the knowledge graph corresponds to a statement instance. + +Note: For technical reasons for every `Statement` instance there exits a dual `Statement` instance. For most situations this does not matter, though. + +The whole knowledge graph is a collection of Entities (Items, Relation, Literals) and Statements. Roughly speaking, the collection of Entities defines what exists (in the respective universe of discourse) while the collection of Statements defines how these things are related. Because flat subject-predicate-object triples have very limited expressivity it is possible to "make statements about statements", i.e. use a `Statement` instance as subject another triple. This [Wikidata](https://www.wikidata.org/wiki/Wikidata:SPARQL_tutorial#Qualifiers)-inspired mechanism is called [qualifiers](sec_qualifiers) (see below). (sec_qualifiers)= -## Qualifiers +### Qualifiers -Basic statements in pyerk are modeled as `subject`-`predicate`-`object`-triples. +Basic statements in Pyirk are modeled as `subject`-`predicate`-`object`-triples. E.g. to express that R. Kalman works at Stanford University one could use: ```python # example from ocse0.2 (adapted) @@ -83,7 +171,7 @@ I2746["Rudolf Kalman"].set_relation(R1833["has employer"], I9942["Stanford Unive #. ``` -This results in the triple: `(I2746, R1833, I9942)`. In pyerk such triples are modeled as instances of class `Statement`; each such instance represents an edge in the knowledge graph, where the subject and object are the corresponding nodes and each such edge has a lable (the relation type) and optionally other information attachend to it. +This results in the triple: `(I2746, R1833, I9942)`. In Pyirk such triples are modeled as instances of class `Statement`; each such instance represents an edge in the knowledge graph, where the subject and object are the corresponding nodes and each such edge has a lable (the relation type) and optionally other information attached to it. However, in many cases more complexity is needed. To express that Kalman worked at Stanford between 1964 and 1971, we can exploit that `Statement`-instances can themselves be use as subject of other triples, by means of so called qualifiers: @@ -114,86 +202,224 @@ The concept of qualifiers is borrowed from Wikidata, see e.g the [WD-SPARQL-tuto ``` -**Summary:** Qualifiers are a flexible possibility to model "information about information" in pyerk. They are used, e.g. to model the universal quantification. +**Summary:** Qualifiers are a flexible possibility to model "information about information" in Pyirk. They are used, e.g. to model the universal quantification. -## Universal and Existential Quantification -Background, see . +(sec_scopes)= +### Scopes -> commonly used quantifiers are ∀ (`$\forall$`) and ∃ (`$\exists$`). +#### Basics +Many knowledge artifacts (such as theorems or definitions) consists of multiple simpler statements which are in a specific semantic relation to each other. Consider the example theorem: -They are also called *universal quantifier* and *existential quantifier*. In pyerk they can be expressed via [qualifiers](sec_qualifiers) +> Let {math}`(a, b, c)` be the sides of a triangle, ordered from shortest to longest, and {math}`(l_a, l_b, l_c)` the respective lengths. If the angle between a and b is a rect angle then the equation {math}`l_c^2 = l_a^2 + l_b^2` holds. -```{warning} -Despite having similar phonetics (and spelling) quantifiers (logic operators) and qualifiers (knowledge modeling technique, in triple-based knowledge graphs) are totally different concepts. However, qualifiers can (among many other use cases) be used to model universal or existential quantification of an statement. + +Such a theorem consists of several "semantic parts", which in the context of Pyirk are called *scopes*. In particular we the three following scopes: + +- *setting*: "Let (a, b, c) be the sides of a triangle, ordered from shortest to longest, and (la, lb, lc) the respective lengths." +- *premise*: "If the angle between a and b is a rect angle" +- *assertion*: "then the equation {math}`l_c^2 = l_a^2 + l_b^2` holds." + +While the concepts "premise" and "assertion" are usually used to refer to parts of theorems (etc). The concept of "setting" is used to refer to those statements which do "set the stage" to properly formulate the premise and the assertion (e.g. by introducing and specifying the relevant objects). + +#### Scopes in Pyirk + +Scopes are represented by Items (instances (`R4`) of `I16["scope"]`). A scope item is specified by `R64__has_scope_type`. It is associated with a parent item (e.g. a theorem) via `R21__is_scope_of`. A statement which semantically belongs to a specific scope is associated to the respective scope item via the [qualifier](sec_qualifiers) `R20__has_defining_scope`. + +```{note} +`R21__is_scope_of` and `R20__has_defining_scope` are not inverse (`R68__is_inverse_of`) to each other. ``` +#### Notation of Scopes via Context Managers (`with ... as cm`) -(sec_patterns)= -## Patterns for Knowledge Representation in pyerk +To simplify the creation of the auxiliary scope-items [python context managers](https://docs.python.org/3/reference/datamodel.html#context-managers) (i.e. `with`-statements) are used. This is illustrated by the following example: -In pyerk knowledge is represented via *entities* and *statements* (inspired by Wikidata). There are two types of entities: *items* (mostly associated with nouns) and *relations* (mostly associated with verbs). Statements consist of *subject-predicate-object*-triples. +```python +I5000 = p.create_item( + R1__has_label="simplified Pythagorean theorem", + R4__is_instance_of=p.I15["implication proposition"], +) -- subject: can be any entity (item or a relation), -- predicate: is always a relation, -- object: can be any entity or *literal*. +# because I5000 is an instance of I15 it has a `.scope` method: +with I5000["simplified Pythagorean theorem"].scope("setting") as cm: + # the theorem should hold for every planar triangle, + # thus a universally quantified instance is created + cm.new_var(ta=p.uq_instance_of(I1000["planar triangle"])) + cm.new_var(sides=I1001["get polygon sides ordered by length"](cm.ta)) -Literals are "atomic" values like strings, numbers or boolean values. + a, b, c = p.unpack_tuple_item(cm.sides) + la, lb, lc = a.R2000__has_length, b.R2000, c.R2000 -Every entity has short_key (`entity.short_key`, see also {ref}`sec_keys`.) and an uri (`entity.uri`). It is recommended but not requiered that every entity has a label (by means of relation `R1["has label"]`) and a description (by means of `R2["has description"]`). +with I5000["simplified Pythagorean theorem"].scope("premise") as cm: + cm.new_equation(lhs=I1002["angle"](a, b), rhs=I1003["right angle"]) -(sec_items)= -### Items +with I5000["simplified Pythagorean theorem"].scope("assertion") as cm: -The `short_key` of any items starts with "`I`" and ends with a sequence of number characters (maximum sequence length not yet specified). Optionally the second character is "`a`" which indicates that this item was generated automatically (see [below](sec_auto_gen_items)). + # convert a pyirk items into sympy.Symbol instances to conveniently + # denote formulas (see documentation below) + La, Lb, Lc = p.items_to_symbols(la, lb, lc) + cm.new_equation( La**2 + Lb**2, "==", Lc**2 ) +``` -(Almost) All items are part of a taxonomy, i.e. a hierarchy of *"is-a"*-relations). This is expressed by the relations `R3["is_subclass_of"]` and `R4["is instance of"]`. +(sec_operators)= +### Operators +Example from `math.py` (OCSE): -```{note} -Unlike in OWL (but like in Wikidata) an item can be an instance and a class at the same time. This allows to treat classes as "ordinary" items if necessary, e.g. use them directly in statements. + +```python +I4895 = p.create_item( + R1__has_label="mathematical operator", + R2__has_description="general (unspecified) mathematical operator", + R3__is_subclass_of=p.I12["mathematical object"], +) + +I4895["mathematical operator"].add_method(p.create_evaluated_mapping, "_custom_call") + + +I5177 = p.create_item( + R1__has_label="matmul", + R2__has_description=("matrix multiplication operator"), + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I9904["matrix"], + R9__has_domain_of_argument_2=I9904["matrix"], + R11__has_range_of_result=I9904["matrix"], +) + +# representing the product of two matrices: + +A = p.instance_of(I9904["matrix"]) +B = p.instance_of(I9904["matrix"]]) + +# this call creates and returns a new item +# (instance of `I32["evaluated mapping"]`) +C = I5177["matmul"](A, B) + +# equivalent but more readable: +mul = I5177["matmul"] +C = mul(A, B) ``` +(sec_formulas)= +### Representing Formulas -(sec_auto_gen_items)= -#### Automatically Generated Items +In the module `math1.py` of OCSE there is an implementation for a convenient formula notation (write `x + y + z` instead of `add_item(x, add_item(y, z))`). See this example from the OCSE unittests: -One consequence of expressing knowledge as a collection of triples is the necessity of auxiliary items. E.g. consider the equation {math}`y = \sin(x)` where `x, y, sin` can be assumed to be well defined items. Because the predicate must be a relation, it is not possible to relate these three items in one triple. The usual approach to deal with such situations is to introduce auxiliary items and more triples (see also [wikipedia on "reification"](https://en.wikipedia.org/wiki/Reification_(knowledge_representation))). One possible (fictional) triple representation of the above equation is +```python +ma = p.irkloader.load_mod_from_path(pjoin(OCSE_PATH, "math1.py"), prefix="ma") +t = p.instance_of(ma.I2917["planar triangle"]) +sides = ma.I9148["get polygon sides ordered by length"](t) +a, b, c = sides.R39__has_element +la, lb, lc = ma.items_to_symbols(a, b, c, relation=ma.R2495["has length"]) +symbolic_sum = la + lb + lc + +sum_item = ma.symbolic_expression_to_graph_expression(symbolic_sum) ``` -auxiliary_expr is_functioncall_of_type sin -auxiliary_expr has_arg x -y is_equal_to expr + + +#### Convenience-Expressions + +```{warning} +This is not yet implemented. However, see [formula representation](sec_formulas). ``` -One of the main goals of pyerk is to simplify the creation of triples which involves creating auxiliary items (such as evaluated expressions). This can be achieved by calling functions such as `pyerk.instance_of(...)`. A more sophisticated way is to overload the `__call__` method of entities. +While the operator approach is suitable to create the appropriate notes and edges in the knowledge graph it is not very convenient to write more complex formulas in that way. Thus pyirk offers a convenience mechanism based on the computer algebra package [Sympy](https://docs.sympy.org/dev/install.html). The function `builtin_entities.items_to_symbols()` creates a sympy symbol for every passed item (and keeps track of the associations). Then, a formula can be denoted using "usual" python syntax with operator signs `+`, `-`, `*`, `/`, and `**` which results in an instance of `sympy.core.expr.Expr`. These expressions can be passed, e.g., to `cm.new_equation` where they are converted back to pyirk-items. In other words the following two snippets are equivalent: +```python +# approach 1: using intermediate symbolic expressions +La, Lb, Lc = p.items_to_symbols(la, lb, lc) +cm.new_equation( La**2 + Lb**2, "==", Lc**2 ) + +# approach 0: without using intermediate symbolic expressions +sq = I1010["squared"] +plus = I1011["plus"] +cm.new_equation( plus(sq(La), sq(Lb)), "==", sq(Lc) ) +``` -(sec___call__mechanism)= -#### The `__call__` Method + + -The class `pyerk.Entity` implements the `__call__` method which formally makes all items and relations callable Python objects. However, by default no method `_custom_call` is implemented which +(sec_stubs)= +### Stubs (`I50["Stub"]`, `I000["some arbitrary label"]` and `R000["also"]`) +One challenge in formal knowledge representation is *Where to begin?*. Suppose you want to formalize some knowledge about triangles. It seems natural that you introduce the class *triangle* as a subclass of *polygon*. However, the class polygon should also be a subclass of something and so on. +As modelling *all* knowledge is unfeasible at some points it is necessary to model incomplete entities (Ideally, theses are some relation-steps away from the relevant entities of the domain). To facilitate this there exists `I50["stub"]`. This item can be used as (base) class for any item which at the moment no further (taxonomic) information should be modeled. The name "stub" is inspired by Wikipedia's (stub-pages](https://en.wikipedia.org/wiki/Wikipedia:Stub). Example: -(sec_relations)= -### Relations +```python +I1234 = p.create_item( + R1__has_label="polygon", + R2__has_description="", + R3__is_subclass_of=p.I50["stub"], +) +``` + +In some situations it is desireable to use items and relations which do not yet exist. This can be done by `I000["dummy item]` and `R000["dummy relation"]`. Both entities can be used with **arbitrary labels** and can thus be used regarded as a special kind of comment. Example: + +```python +I1234 = p.create_item( + R1__has_label="polygon", + R2__has_description="", + R3__is_subclass_of=p.I000["general geometric figure"], + R000__has_dimension=2, +) + +``` + +This allows to focus a modeling session on the important items and relations and prevents to get distracted by introducing entities of subordinate relevance. + +It is quite probable that even mature irk-ontologies contain relations involving `I50`. Such items can be considered to constitute the "border of the domain of discourse". On the other hand, `I000` and `R000` should be used only temporarily and be replaced soon, e.g., by new instances/subclasses of `I50`. + + +(sec_quantification)= +### Universal and Existential Quantification + +Background, see . + +> commonly used quantifiers are ∀ (`$\forall$`) and ∃ (`$\exists$`). + +They are also called *universal quantifier* and *existential quantifier*. In Pyirk they can be expressed via + +- [Qualifiers](sec_qualifiers). In particular (defined in module `builtin_entities`): + - `univ_quant = QualifierFactory(R44["is universally quantified"])` + - usage (in OCSE): `cm.new_rel(cm.z, p.R15["is element of"], cm.HP, qualifiers=p.univ_quant(True))` + - `exis_quant = QualifierFactory(R66["is existentially quantified"])` + - usage (in OCSE): `cm.new_var(y=p.instance_of(p.I37["integer number"], qualifiers=[p.exis_quant(True)]))` +- (Sub)scopes: + ```python + # excerpt from test_core.py + with I7324["definition of something"].scope("premise") as cm: + with cm.universally_quantified() as cm2: + cm2.add_condition_statement(cm.x, p.R15["is element of"], my_set) + # ... + with I7324["definition of something"].scope("assertion") as cm: + # also pointless direct meaning, only to test contexts + with cm.existentially_quantified() as cm2: + z = cm2.new_condition_var(z=p.instance_of(p.I39["positive integer"])) + ``` + + +```{warning} +Despite having similar phonetics (and spelling) quantifiers (logic operators) and qualifiers (knowledge modeling technique, in triple-based knowledge graphs) are totally different concepts. However, qualifiers can (among many other use cases) be used to model universal or existential quantification of an statement. +``` + (sec_modules)= -### pyerk Modules and Packages +## Pyirk Modules and Packages -pyerk entities and statements are organized in pyerk *modules* (python files). Each module has to specify its own URI via the variable `__URI__`. The uri of an entity from that module is formed by `#`. Modules can be bundled together to form pyer *packages*. A pyerk package consits of a directory containing a file `erkpackage.toml` and at least one pyerk module. +Pyirk entities and statements are organized in Pyirk *modules* (python files). Each module has to specify its own URI via the variable `__URI__`. The uri of an entity from that module is formed by `#`. Modules can be bundled together to form pyirk *packages*. A Pyirk package consists of a directory containing a file `irkpackage.toml` and at least one Pyirk module. Modules can depend on other modules. A usual pattern is the following: ```python # in module control_theory1.py -import pyerk as p -mod = p.erkloader.load_mod_from_path("./math1.py", prefix="ma") +import pyirk as p +mod = p.irkloader.load_mod_from_path("./math1.py", prefix="ma") ``` Here the variable `mod` is the module object (like from ordinary python import) and allows to access to the complete namespace of that module: @@ -207,9 +433,9 @@ The prefix `"ma"` can also be used to refer to that module like here ```python # ... -res = A.ma__R8736__depends_polyonomially_on +res = A.ma__R8736__depends_polynomially_on ``` -Rationale: The attribute name `ma__R8736__depends_polyonomially_on` is handled as a string by Python (in the method `__getattr__`). While `mod.R8736` is the relation object we cannot use this syntax as attribute name. +Rationale: The attribute name `ma__R8736__depends_polynomially_on` is handled as a string by Python (in the method `__getattr__`). While `mod.R8736` is the relation object we cannot use this syntax as attribute name. See also {ref}`sec_keys`. diff --git a/pyerkconf-default.toml b/pyirkconf-default.toml similarity index 56% rename from pyerkconf-default.toml rename to pyirkconf-default.toml index 0e9fbfe..d91b9ef 100644 --- a/pyerkconf-default.toml +++ b/pyirkconf-default.toml @@ -2,13 +2,13 @@ # Note: the inverse mapping will be added automatically [SPARQL_PREFIX_MAPPING] - ":" = "" - "ct:" = "" - "ack:" = "" - "ma:" = "" + ":" = "" + "ct:" = "" + "ack:" = "" + "ma:" = "" -[pyerkdjango] +[pyirkdjango] # empty main_module -> load only builtins main_module = "" diff --git a/pyproject.toml b/pyproject.toml index 8bdfacf..181c323 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,8 +9,8 @@ build-backend = "setuptools.build_meta" [project] -name = "pyerk" -description = "Python based 'Easy Representation of Knowledge' (pyERK)" +name = "pyirk" +description = "Python based 'Imperative Representation of Knowledge' (PyIRK)" authors=[{name = "Carsten Knoll", email = "firstname.lastname@posteo.de"}] readme = "README.md" requires-python = ">=3.8" @@ -24,9 +24,9 @@ classifiers=[ "Programming Language :: Python :: 3", ] -# copied from the original setup.py +# copied (and name-adapted) from the original setup.py # long_description=""" -# (Python based) Easy Representation of Knowledge (pyERK). +# (Python based) Imperative Representation of Knowledge (PyIRK). # # - Inspired by OWL, but more expressive # - Inspired by Wikidata, but much simpler @@ -39,10 +39,10 @@ classifiers=[ # https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html [project.urls] -Homepage = "https://github.com/ackrep-org/pyerk-core/" +Homepage = "https://github.com/ackrep-org/pyirk-core/" [project.scripts] -pyerk = "pyerk.script:main" +pyirk = "pyirk.script:main" [tool.setuptools.packages.find] # note: `include-package-data = true` by default in pyproject.toml @@ -50,4 +50,4 @@ where = ["src"] [tool.setuptools.dynamic] dependencies = {file = ["requirements.txt"]} -version = {attr = "pyerk.__version__"} +version = {attr = "pyirk.__version__"} diff --git a/src/pyerk/release.py b/src/pyerk/release.py deleted file mode 100644 index ae6db5f..0000000 --- a/src/pyerk/release.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.11.0" diff --git a/src/pyerk/__init__.py b/src/pyirk/__init__.py similarity index 93% rename from src/pyerk/__init__.py rename to src/pyirk/__init__.py index 8c3b4cf..8407222 100644 --- a/src/pyerk/__init__.py +++ b/src/pyirk/__init__.py @@ -4,7 +4,7 @@ from .core import * from .builtin_entities import * from .settings import * - from . import erkloader + from . import irkloader from . import rdfstack from . import ruleengine from . import auxiliary as aux diff --git a/src/pyerk/auxiliary.py b/src/pyirk/auxiliary.py similarity index 83% rename from src/pyerk/auxiliary.py rename to src/pyirk/auxiliary.py index 83dc907..63fee65 100644 --- a/src/pyerk/auxiliary.py +++ b/src/pyirk/auxiliary.py @@ -8,7 +8,7 @@ from . import settings """ -Some auxiliary classes and functions for pyerk. +Some auxiliary classes and functions for pyirk. """ startup_workdir = os.path.abspath(os.getcwd()) @@ -114,100 +114,103 @@ def ensure_rdf_str_literal(arg, allow_none=True) -> Union[Literal, None]: return res -class PyERKError(Exception): +class PyIRKError(Exception): """ - raised in situations where some ERK-specific conditions are violated + raised in situations where some IRK-specific conditions are violated """ pass -class MultilingualityError(PyERKError): +class MultilingualityError(PyIRKError): pass -class EmptyURIStackError(PyERKError): +class EmptyURIStackError(PyIRKError): pass -class UnknownPrefixError(PyERKError): +class UnknownPrefixError(PyIRKError): pass -class UnknownURIError(PyERKError): +class UnknownURIError(PyIRKError): pass -class InvalidURIError(PyERKError): +class InvalidURIError(PyIRKError): pass -class InvalidPrefixError(PyERKError): +class InvalidPrefixError(PyIRKError): pass # used for syntax problems -class InvalidShortKeyError(PyERKError): +class InvalidShortKeyError(PyIRKError): pass -class InvalidGeneralKeyError(PyERKError): +class InvalidGeneralKeyError(PyIRKError): pass -class InconsistentLabelError(PyERKError): +class InconsistentLabelError(PyIRKError): pass # used for syntactically correct keys which could not be found -class ShortKeyNotFoundError(PyERKError): +class ShortKeyNotFoundError(PyIRKError): pass -class InvalidScopeNameError(PyERKError): +class InvalidScopeNameError(PyIRKError): pass -class InvalidScopeTypeError(PyERKError): +class InvalidScopeTypeError(PyIRKError): pass -class ModuleAlreadyLoadedError(PyERKError): +class ModuleAlreadyLoadedError(PyIRKError): pass -class SemanticRuleError(PyERKError): +class SemanticRuleError(PyIRKError): pass -class ExcplicitlyTriggeredTestException(PyERKError): +class ExplicitlyTriggeredTestException(PyIRKError): pass class InconsistentEdgeRelations(SemanticRuleError): pass +class InvalidObjectValue(SemanticRuleError): + pass + -class MissingQualifierError(PyERKError): +class MissingQualifierError(PyIRKError): pass -class AmbiguousQualifierError(PyERKError): +class AmbiguousQualifierError(PyIRKError): pass -class FunctionalRelationError(PyERKError): +class FunctionalRelationError(PyIRKError): pass -class UndefinedRelationError(PyERKError): +class UndefinedRelationError(PyIRKError): pass -class TaxonomicError(PyERKError): +class TaxonomicError(PyIRKError): pass -class RuleTermination(PyERKError): +class RuleTermination(PyIRKError): pass @@ -423,20 +426,36 @@ def byellow(txt): return f"{Fore.YELLOW}{Style.BRIGHT}{txt}{Style.RESET_ALL}" -def get_erk_root_dir() -> str: +def get_irk_root_dir() -> str: """ - Return the absolute path of the erk-root (assuming the directory structure documented in README.md) + Return the absolute path of the irk-root (assuming the directory structure documented in README.md) :return: """ current_dir = os.path.abspath(os.getcwd()) - # this allows to have a local-deployment copy of the erk-root which does not change on every edit of the + # this allows to have a local-deployment copy of the irk-root which does not change on every edit of the # knowledge base - # TODO: obsolete? (this should respect pyerkconf.toml) - if os.path.isfile(os.path.join(current_dir, "__erk-root__")): + # TODO: obsolete? (this should respect pyirkconf.toml) + if os.path.isfile(os.path.join(current_dir, "__irk-root__")): return current_dir dir_of_this_file = os.path.dirname(os.path.abspath(sys.modules.get(__name__).__file__)) - erk_root = os.path.abspath(os.path.join(dir_of_this_file, "..", "..", "..")) - return erk_root + irk_root = os.path.abspath(os.path.join(dir_of_this_file, "..", "..", "..")) + return irk_root + + +def get_irk_path(dirname=None): + + if dirname is None: + return get_irk_root_dir() + + dir_of_this_file = os.path.dirname(os.path.abspath(sys.modules.get(__name__).__file__)) + + # this assumes pyirk is installed with `pip install -e .` from the repo + pyirk_root = os.path.abspath(os.path.join(dir_of_this_file, "..", "..")) + if dirname == "pyirk-core-test_data": + return os.path.join(pyirk_root, "tests", "test_data") + + msg = f"unexpected dirname: {dirname}" + raise ValueError(msg) diff --git a/src/pyerk/builtin_entities.py b/src/pyirk/builtin_entities.py similarity index 92% rename from src/pyerk/builtin_entities.py rename to src/pyirk/builtin_entities.py index d5a7fdd..a41ce44 100644 --- a/src/pyerk/builtin_entities.py +++ b/src/pyirk/builtin_entities.py @@ -113,7 +113,7 @@ def get_taxonomy_tree(itm, add_self=True) -> list: parent_class = itm.R4__is_instance_of if (super_class is not None) and (parent_class is not None): - msg = f"currently not allowed together: R3__is_subclass_of and R4__is_instnace_of (Entity: {itm}" + msg = f"currently not allowed together: R3__is_subclass_of and R4__is_instance_of (Entity: {itm}" raise NotImplementedError(msg) if super_class: @@ -181,7 +181,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite Create an instance (R4) of an item. Try to obtain the label by inspection of the calling context (if r1 is None). :param cls_entity: the type of which an instance is created - :param r1: the label; if None use inspection to fetch it from the left hand side of the assingnment + :param r1: the label; if None use inspection to fetch it from the left hand side of the assignment :param r2: the description (optional) :param qualifiers: list of RawQualifiers (optional); will be passed to the R4__is_instance_of relation @@ -195,7 +195,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite class_scope = cls_entity.R20__has_defining_scope - # we have to determine if `cls_entity` is an instnace of I2_metaclass or a subclass of it + # we have to determine if `cls_entity` is an instance of I2_metaclass or a subclass of it is_instance_of_metaclass = allows_instantiation(cls_entity) @@ -252,7 +252,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite ######################################################################################################################## # -# Creattion of entities +# Creation of entities # ######################################################################################################################## @@ -285,7 +285,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite R22["is functional"].set_relation(R22["is functional"], True) R32["is functional for each language"].set_relation(R22["is functional"], True) -# Note that R1, R22, and R32 are used extensively to control the behavior in pyerk.core +# Note that R1, R22, and R32 are used extensively to control the behavior in pyirk.core R3 = create_builtin_relation("R3", R1="is subclass of", R22__is_functional=True) R4 = create_builtin_relation("R4", R1="is instance of", R22__is_functional=True) @@ -308,7 +308,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite # R10__has_range_of_result=... ) -# The short key R61 was choosen for historical and/or pragmatic reasons +# The short key R61 was chosen for historical and/or pragmatic reasons R61 = create_builtin_relation( key_str="R61", R1="does not have property", @@ -324,13 +324,13 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite "R18", R1="has usage hint", R2="specifies a hint (str) on how this relation should be used" ) -R16.set_relation(R18["has usage hint"], "this relation should be used on conrete instances, not on generic types") -R61.set_relation(R18["has usage hint"], "this relation should be used on conrete instances, not on generic types") +R16.set_relation(R18["has usage hint"], "this relation should be used on concrete instances, not on generic types") +R61.set_relation(R18["has usage hint"], "this relation should be used on concrete instances, not on generic types") R19 = create_builtin_relation( key_str="R19", R1="defines method", - R2="specifies that an entity has a special method (defined by executeable code)" + R2="specifies that an entity has a special method (defined by executable code)" # R10__has_range_of_result=callable !! ) @@ -359,7 +359,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite R20 = create_builtin_relation( key_str="R20", R1="has defining scope", - R2="specifies the scope *in* which an entity or relation edge is defined (e.g. the premise of a theorem)", + R2="specifies the scope *in* which an entity or statement is defined (e.g. the premise of a theorem)", R18=( "Notes: This relation is functional. But an Entity (e.g. a theorem) can be parent (via R21) of multiple " "scopes, (e.g. 'setting', 'premise', 'assertion'). Each of these items can 'contain' other items in the sense, " @@ -438,8 +438,8 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite ) I11 = create_builtin_item( key_str="I11", - R1="mathematical property", - R2__has_description="base class for all mathematical properties", + R1="general property", + R2__has_description="base class for all properties", R4__is_instance_of=I2["Metaclass"], R18__has_usage_hint=( "Actual properties are instances of this class (not subclasses). " @@ -447,11 +447,11 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite ), ) -# TODO: clearify the difference between the I12 and I18 +# TODO: clarify the difference between the I12 and I18 I12 = create_builtin_item( key_str="I12", R1__has_label="mathematical object", - R2__has_description="base class for any knowledge object of interrest in the field of mathematics", + R2__has_description="base class for any knowledge object of interest in the field of mathematics", R4__is_instance_of=I2["Metaclass"], ) @@ -471,12 +471,7 @@ def instance_of(cls_entity, r1: str = None, r2: str = None, qualifiers: List[Ite ) -I15 = create_builtin_item( - key_str="I15", - R1__has_label="implication proposition", - R2__has_description="proposition, where the premise (if-part) implies the assertion (then-part)", - R3__is_subclass_of=I14["mathematical proposition"], -) +# I15 is defined below I16 = create_builtin_item( @@ -640,7 +635,7 @@ def __init__(self, itm: Item, namespace: dict, scope: Item, parent_scope_cm=None def __enter__(self): """ - implicitly called in the head of the with statemet + implicitly called in the head of the with statement :return: """ ds.append_scope(self.scope) @@ -689,7 +684,7 @@ def _new_var(self, variable_name: str, variable_object: Entity) -> Entity: add_scope_to_defining_statement(variable_object, self.scope) - # this reflects a dessign assumption which might be generalized later + # this reflects a design assumption which might be generalized later assert isinstance(variable_object, Entity) # allow simple access to the variables → put them into dict (after checking that the name is still free) @@ -896,9 +891,9 @@ def _get_new_var_from_old(self, old_var: Item, strict=False) -> Item: if strict: msg = f"Unexpected: Could not find a copied item associated to {old_var}" - raise core.aux.PyERKError(msg) + raise core.aux.PyIRKError(msg) - # last ressort return the original variable (because it was an external var) + # last resort return the original variable (because it was an external var) return old_var def _copy_mapping(self, mapping_item: Item) -> Item: @@ -966,14 +961,14 @@ def new_equation(self, lhs: Item, rhs: Item) -> Item: eq = new_equation(lhs, rhs, scope=self.scope) return eq - # TODO: this makes self.new_equation obsolete, doesnt it? + # TODO: this makes self.new_equation obsolete, doesn't it? def new_math_relation(self, lhs: Item, rsgn: str, rhs: Item) -> Item: """ convenience method to create a math_relation-related StatementObject (aka "Statement") :param lhs: left hand side :param rsgn: relation sign - :param rhs: rght hand sign + :param rhs: right hand sign :return: new instance of """ @@ -987,7 +982,7 @@ def new_math_relation(self, lhs: Item, rsgn: str, rhs: Item) -> Item: class _proposition__CM(AbstractMathRelatedScopeCM): """ - Context manager taylored for mathematical theorems and definitions + Context manager tailored for mathematical theorems and definitions """ valid_subscope_types = {"UNIV_QUANT": float("inf"), "EXIS_QUANT": float("inf")} @@ -1263,7 +1258,7 @@ def _rule__scope(self: Item, scope_name: str): return cm -I15["implication proposition"].add_method(_proposition__scope, name="scope") +I14["mathematical proposition"].add_method(_proposition__scope, name="scope") def _get_subscopes(self): @@ -1274,7 +1269,7 @@ def _get_subscopes(self): return scope_rels -I15["implication proposition"].add_method(_get_subscopes, name="get_subscopes") +I14["mathematical proposition"].add_method(_get_subscopes, name="get_subscopes") I16["scope"].add_method(_get_subscopes, name="get_subscopes") @@ -1299,7 +1294,7 @@ def _get_subscope(self, name: str): return res[0] -I15["implication proposition"].add_method(_get_subscope, name="get_subscope") +I14["mathematical proposition"].add_method(_get_subscope, name="get_subscope") I16["scope"].add_method(_get_subscope, name="get_subscope") @@ -1329,21 +1324,59 @@ def _get_items_for_scope(self): I16["scope"].add_method(_get_items_for_scope, name="get_items_for_scope") +I15 = create_builtin_item( + key_str="I15", + R1__has_label="implication proposition", + R2__has_description="proposition, where the premise (if-part) implies the assertion (then-part)", + R3__is_subclass_of=I14["mathematical proposition"], +) + + I17 = create_builtin_item( key_str="I17", R1__has_label="equivalence proposition", - R2__has_description="proposition, which establishes the equivalence of two or more statements", + R2__has_description="proposition, which establishes the equivalence of two sets of statements (premise and assertion)", R3__is_subclass_of=I14["mathematical proposition"], + R18__has_usage_hint=( + "While for I15__implication_proposition the border between scopes 'setting' and premise is somewhat arbitrary " + "this is not the case for I17__equivalence_proposition because assertion and premise here are interchangeable. " + "The implication must be true in the other direction as well." + ), +) + + +I51 = create_builtin_item( + key_str="I51", + R1__has_label="data type", + R2__has_description="subclasses of this item model data types such as string", + R4__is_instance_of=I2["Metaclass"], + R18__has_usage_hint="This class is meant to be 'abstract': it is not intended to be instantiated itself", +) + + +I52 = create_builtin_item( + key_str="I52", + R1__has_label="string", + R2__has_description="equivalent entity of the python datatype `str`", + R3__is_subclass_of=I51["data type"], +) + + +I53 = create_builtin_item( + key_str="I53", + R1__has_label="bool", + R2__has_description="equivalent entity of the python datatype `bool`", + R3__is_subclass_of=I51["data type"], ) I18 = create_builtin_item( key_str="I18", - R1__has_label="mathematical expression", + R1__has_label="mathematical expression", # = math. term R2__has_description=( "mathematical expression, e.g. represented by a LaTeX-string; this might change in the future to MathMl" ), - R4__is_instance_of=I2["Metaclass"], + R3__is_subclass_of=I12["mathematical object"], ) @@ -1366,10 +1399,10 @@ def get_ui_short_representation(self) -> str: I18.add_method(get_ui_short_representation) del get_ui_short_representation R24["has LaTeX string"].set_relation(R8["has domain of argument 1"], I18["mathematical expression"]) -R24["has LaTeX string"].set_relation(R11["has range of result"], str) +R24["has LaTeX string"].set_relation(R11["has range of result"], I52["string"]) -# TODO: how does this relate to I21["mathmatical relation"]? +# TODO: how does this relate to I21["mathematical relation"]? # -> Latex expressions are for human readable representation # they should be used only as an addendum to semantic representations # TODO: Fix ocse_ct.I6091["control affinity"] @@ -1385,15 +1418,16 @@ def create_expression(latex_src: str, r1: str = None, r2: str = None) -> Item: return expression -# TODO: this item might be obsolete, we use a construction with RDF-Literals instead -# however currently it is uses for R11__has_range_of_result in the ocse (test data) I19 = create_builtin_item( key_str="I19", - R1__has_label="multilingual string literal", + R1__has_label="language-specified string literal", R2__has_description="used to encode strings that depend on natural languages", R4__is_instance_of=I2["Metaclass"], ) +R1.set_relation(R11, I19["language-specified string literal"]) +R2.set_relation(R11, I19["language-specified string literal"]) + I20 = create_builtin_item( key_str="I20", @@ -1416,7 +1450,7 @@ def create_expression(latex_src: str, r1: str = None, r2: str = None) -> Item: I21 = create_builtin_item( key_str="I21", R1__has_label="mathematical relation", - R2__has_description="establishes that two mathematical expressions (I18) are in a relation, e.g. equalness", + R2__has_description="establishes that two mathematical expressions (I18) are in a relation, e.g. equality", ) @@ -1471,7 +1505,7 @@ def create_expression(latex_src: str, r1: str = None, r2: str = None) -> Item: R2__has_description="mathematical relation that specifies that lhs is unequal to rhs", R3__is_subclass_of=I21["mathematical relation"], R18__has_usage_hint=( - "This item is different from inquality (I25): lhs and rhs need to be members of the same ordered set." + "This item is different from inequality (I25): There, lhs and rhs need to be members of the same ordered set." ), ) @@ -1545,7 +1579,7 @@ def create_expression(latex_src: str, r1: str = None, r2: str = None) -> Item: R1__has_label="has argument", R2__has_description='specifies the concrete argument item of an I32["evaluated mapping"] item', # todo: currently we only need univariate mappings. However, once we have multivariate mappings - # this needs be reflected here (maybe use qualifiers or a seperate relation for each argument) + # this needs be reflected here (maybe use qualifiers or a separate relation for each argument) ) @@ -1585,7 +1619,7 @@ def create_evaluated_mapping(mapping: Item, *args) -> Item: args_repr = ", ".join(arg_repr_list) target_class = mapping.R11__has_range_of_result - # TODO: this should be ensured by consistency check: for operatators R11 should be functional + # TODO: this should be ensured by consistency check: for operators R11 should be functional if target_class: assert len(target_class) == 1 target_class = target_class[0] @@ -1626,7 +1660,7 @@ def create_evaluated_mapping(mapping: Item, *args) -> Item: return ev_mapping -# todo: maybe the differece between asserted inheritance and inferred inheritance should be encoded via qualifiers +# todo: maybe the difference between asserted inheritance and inferred inheritance should be encoded via qualifiers R30 = create_builtin_relation( key_str="R30", R1__has_label="is secondary instance of", @@ -1648,14 +1682,14 @@ def create_evaluated_mapping(mapping: Item, *args) -> Item: ), # TODO: update or delete: R18__has_usage_hint=( - "The actual type of the relation can be tretrieved by the .proxyitem attribute of the " + "The actual type of the relation can be retrieved by the .proxyitem attribute of the " "corresponding Statement." ), ) def new_equation(lhs: Item, rhs: Item, doc=None, scope: Optional[Item] = None) -> Item: - """common speacial case of mathematical relation, also ensures backwards compatibility""" + """common special case of mathematical relation, also ensures backwards compatibility""" eq = new_mathematical_relation(lhs, "==", rhs, doc, scope) @@ -1742,7 +1776,7 @@ def get_proxy_item(stm: Statement, strict=True) -> Item: R35 = create_builtin_relation( key_str="R35", R1__has_label="is applied mapping of", - R2__has_description="specifies the mapping entitiy for which the subject is an application", + R2__has_description="specifies the mapping entity for which the subject is an application", R8__has_domain_of_argument_1=I32["evaluated mapping"], R22__is_functional=True, R18__has_usage_hint=( @@ -1762,12 +1796,12 @@ def get_proxy_item(stm: Statement, strict=True) -> Item: def new_tuple(*args, **kwargs) -> Item: """ - Create a new tuple entitiy + Create a new tuple entity :param args: :return: """ - # ensure this function is called with an active erk module (to define URIs of new instances ) + # ensure this function is called with an active irk module (to define URIs of new instances ) _ = core.get_active_mod_uri() scope = kwargs.pop("scope", None) @@ -1805,7 +1839,7 @@ def new_tuple(*args, **kwargs) -> Item: key_str="R46", R1__has_label="is secondary subclass of", R2__has_description=( - "specifies that the subject is an subclass of a class-item, in addtion to its unambiguous parent class." + "specifies that the subject is an subclass of a class-item, in addition to its unambiguous parent class." ), R18__has_usage_hint=( "Note that this relation is not functional. This construction allows to combine single (R3) " @@ -1956,7 +1990,7 @@ def new_tuple(*args, **kwargs) -> Item: "implies the statement `obj rel subj`" ), R8__has_domain_of_argument_1=I40["general relation"], - R9__has_domain_of_argument_2=bool, + R9__has_domain_of_argument_2=I53["bool"], R22__is_functional=True, ) @@ -1967,7 +2001,7 @@ def new_tuple(*args, **kwargs) -> Item: R43 = create_builtin_relation( key_str="R43", R1__has_label="is opposite of", - R2__has_description="specifies that the subject is the oposite of the object.", + R2__has_description="specifies that the subject is the opposite of the object.", R42__is_symmetrical=True, R8__has_domain_of_argument_1=I1["general item"], R9__has_domain_of_argument_2=I1["general item"], @@ -2004,15 +2038,15 @@ def new_tuple(*args, **kwargs) -> Item: "specifies that the subject represents an universally quantified variable (usually denoted by '∀')" ), R8__has_domain_of_argument_1=I1["general item"], - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R18__has_usage_hint=( "should be used as qualifier to specify the free variables in theorems and similar statements; " - "See also R66__is_existantially_quantified" + "See also R66__is_existentially_quantified" ), ) -# this qualifier is can be used to express universal quatification (mathematically expressed with ∀) of a relation +# this qualifier is can be used to express universal quantification (mathematically expressed with ∀) of a relation # e.g. `some_item.set_relation(p.R15["is element of"], other_item, qualifiers=univ_quant(True))` # means that the statements where `some_item` is used claim to hold for all elements of `other_item` (which should be # a set) @@ -2050,19 +2084,19 @@ def uq_instance_of(type_entity: Item, r1: str = None, r2: str = None) -> Item: # placed here for its obvious relation to universal quantification R66 = create_builtin_relation( key_str="R66", - R1__has_label="is existantially quantified", + R1__has_label="is existentially quantified", R2__has_description=( "specifies that the subject represents an existentially quantified variable (usually denoted by '∃')" ), R8__has_domain_of_argument_1=I1["general item"], - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R18__has_usage_hint=( "should be used as qualifier to specify the free variables in theorems and similar statements; " "See also R44__is_universally_quantified" ), ) -exis_quant = QualifierFactory(R66["is existantially quantified"]) +exis_quant = QualifierFactory(R66["is existentially quantified"]) R45 = create_builtin_relation( @@ -2080,7 +2114,7 @@ class ImplicationStatement: """ Context manager to model conditional statements. - Example from erk:/math/0.2#I7169["definition of identity matrix"] + Example from irk:/math/0.2#I7169["definition of identity matrix"] ``` with p.ImplicationStatement() as imp1: @@ -2107,7 +2141,7 @@ def __init__(self): def __enter__(self): """ - implicitly called in the head of the with-statemet + implicitly called in the head of the with-statement """ return self @@ -2164,7 +2198,7 @@ def consequent_relation(self, **kwargs): R42__is_symmetrical=True, R18__has_usage_hint=( "this might be used in two situations: a) to prevent accidental confusion during modeling; " - "b) to express a nontrivial fact of inequality, e.g. that a person has two childs and not just one with " + "b) to express a nontrivial fact of inequality, e.g. that a person has two children and not just one with " "two names." ), ) @@ -2200,7 +2234,7 @@ def close_class_with_R51(cls_item: Item): Set R51__instances_are_from for all current instances of a class. Note: this does not prevent the creation of further instances (because they can be related via R47__is_same_as to - the exising instances). + the existing instances). :returns: tuple-item containing all instances """ @@ -2244,14 +2278,14 @@ def set_multiple_statements(subjects: Union[list, tuple], predicate: Relation, o R1__has_label="is inverse functional", R2__has_description=("specifies that the inverse relation of the subject is functional"), # R8__has_domain_of_argument_1=I1["general item"], # unsure here - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], # TODO: model that this is (probably) equivalent to "owl:InverseFunctionalProperty" ) R54 = create_builtin_relation( key_str="R54", R1__has_label="is matched by rule", - R2__has_description=("specifies that subject entitiy is matched by a semantic rule"), + R2__has_description=("specifies that subject entity is matched by a semantic rule"), # R8__has_domain_of_argument_1=I1["general item"], # unsure here R11__has_range_of_result=I41["semantic rule"], R18__has_usage_hint="useful for debugging and testing semantic rules" @@ -2262,7 +2296,7 @@ def set_multiple_statements(subjects: Union[list, tuple], predicate: Relation, o key_str="R55", R1__has_label="uses as external entity", R2__has_description=( - "specifies that the subject (a setting-scope) uses the object entitiy as an external variable in its graph" + "specifies that the subject (a setting-scope) uses the object entity as an external variable in its graph" ), R8__has_domain_of_argument_1=I16["scope"], R11__has_range_of_result=I1["general item"], @@ -2284,10 +2318,10 @@ def set_multiple_statements(subjects: Union[list, tuple], predicate: Relation, o R57 = create_builtin_relation( key_str="R57", R1__has_label="is placeholder", - R2__has_description="specifies that the subject is a placeholder and might be replaced by other itmes", + R2__has_description="specifies that the subject is a placeholder and might be replaced by other items", # TODO: # R8__has_domain_of_argument_1=, - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R22__is_functional=True, ) @@ -2309,8 +2343,8 @@ def set_multiple_statements(subjects: Union[list, tuple], predicate: Relation, o "currently '2' is not implemented." ), R8__has_domain_of_argument_1=I1["general item"], - R11__has_range_of_result=int, - R18__has_usage_hint="used to adjust the meaning of a statement in the scopes of a I41__semantinc_rule", + R11__has_range_of_result=I37["integer number"], + R18__has_usage_hint="used to adjust the meaning of a statement in the scopes of a I41__semantic_rule", ) qff_has_rule_ptg_mode = QualifierFactory(R59["has rule-prototype-graph-mode"]) @@ -2323,7 +2357,7 @@ def set_multiple_statements(subjects: Union[list, tuple], predicate: Relation, o "`B rel C` also implies the statement `A rel C`" ), R8__has_domain_of_argument_1=I40["general relation"], - R9__has_domain_of_argument_2=bool, + R9__has_domain_of_argument_2=I53["bool"], R22__is_functional=True, ) @@ -2337,7 +2371,7 @@ def set_multiple_statements(subjects: Union[list, tuple], predicate: Relation, o "by edge matching." ), R8__has_domain_of_argument_1=I40["general relation"], - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R22__is_functional=True, ) @@ -2363,7 +2397,7 @@ def get_relation_properties_uris(): # TODO: this could be speed up by caching def get_relation_properties(rel_entity: Entity) -> List[str]: """ - return a sorted list of URIs, corrosponding to the relation properties corresponding to `rel_entity`. + return a sorted list of URIs, corresponding to the relation properties corresponding to `rel_entity`. """ assert isinstance(rel_entity, Relation) or rel_entity.R4__is_instance_of == I40["general relation"] @@ -2384,7 +2418,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: R1__has_label="has SPARQL source", R2__has_description=("specifies that the subject (a scope) is featured by some unique SPARQL source code"), R8__has_domain_of_argument_1=I16["scope"], - R11__has_range_of_result=str, + R11__has_range_of_result=I52["string"], R22__is_functional=True, ) @@ -2403,7 +2437,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: R1__has_label="has scope type", R2__has_description=("specifies the subject (a scope) has a certain type (currently 'OR', 'AND')"), R8__has_domain_of_argument_1=I16["scope"], - R11__has_range_of_result=str, + R11__has_range_of_result=I52["string"], R22__is_functional=True, ) @@ -2416,7 +2450,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: "relation." ), R8__has_domain_of_argument_1=I16["scope"], - R11__has_range_of_result=str, + R11__has_range_of_result=I52["string"], R22__is_functional=True, R18__has_usage_hint="used inside OR-subscopes of semantic rules", ) @@ -2434,7 +2468,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: "associates a template text to the subject (a rule), which can be processed by the ruleengine." ), R8__has_domain_of_argument_1=I41["semantic rule"], - R11__has_range_of_result=str, + R11__has_range_of_result=I52["string"], R32__is_functional_for_each_language=True, R18__has_usage_hint="used to generate explaining reports of rule results", ) @@ -2447,7 +2481,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: "specifies the number of weakly connected 'main components' of the prototype graph of a semantic rule" ), R8__has_domain_of_argument_1=I41["semantic rule"], - R11__has_range_of_result=int, + R11__has_range_of_result=I37["integer number"], R22__is_functional=True, ) @@ -2459,7 +2493,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: "of the subjects (first) R11-value; to be used in rules" ), R8__has_domain_of_argument_1=I40["general relation"], - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R18__has_usage_hint="used to to control the behavior of rules with subjectivized predicates", R22__is_functional=True, ) @@ -2482,16 +2516,16 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: R1__has_label="is generally related to", R2__has_description=("specifies that the subject is 'somehow' related to the object"), R8__has_domain_of_argument_1=I45["general entity"], - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R18__has_usage_hint="used to model relationships which are not (yet) possible to model otherwise", ) R73 = create_builtin_relation( key_str="R73", - R1__has_label="conceptually dependends", + R1__has_label="conceptually depends", R2__has_description=("specifies that the object is needed to define the subject"), R8__has_domain_of_argument_1=I45["general entity"], - R11__has_range_of_result=bool, + R11__has_range_of_result=I53["bool"], R18__has_usage_hint=( "Used to model directed relationships which are not (yet) possible to model otherwise. " "See R72__is_generally_related_to." @@ -2504,7 +2538,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: I47 = create_builtin_item( key_str="I47", R1__has_label="constraint rule", - R2__has_description="rule that specifies which constraints a set of entites has to fulfill", + R2__has_description="rule that specifies which constraints a set of entities has to fulfill", R3__is_subclass_of=I41["semantic rule"] ) @@ -2556,17 +2590,51 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: I50 = create_builtin_item( key_str="I50", R1__has_label="stub", - R2__has_description="instances of this class represent incompletly modelled items (like wikipedia stub-articles)", - R3__is_subclass_of=I2["Metaclass"], + R2__has_description="instances of this class represent incompletely modelled items (like wikipedia stub-articles)", + R3__is_subclass_of=I2["Metaclass"], # could be also R4 here but does not matter because stubs are very unspecific R18__has_usage_hint="This class can be used to preliminarily introduce items and refine them later", ) -# next keys: I51, R76 +R77 = create_builtin_relation( + key_str="R77", + R1__has_label="has alternative label", + R2__has_description="specifies alternative labels for entities in the sense of 'also called ...'", + R8__has_domain_of_argument_1=I45["general entity"], + R11__has_range_of_result=I19["language-specified string literal"], # the labels should have a language specified + R18__has_usage_hint="allows multiple values per language (in contrast to R1__has_label)" +) + + +R78 = create_builtin_relation( + key_str="R78", + R1__has_label="is applicable to", + R2__has_description="specifies some property can be applied to some entity", + R8__has_domain_of_argument_1=I11["general property"], + # TODO: introduce some mechanism to distinguish whether a relation refers to the class in a direct or abstract sense + # (where abstract means that it actually refers to the instances of the class) + # example the mathematical property of symmetry might applicable to the class item ma.I9904["matrix"] but this statement + # actually means that an individual matrix might have this property + R11__has_range_of_result=I45["general entity"], +) + + +# I51, I52, I53, are defined above + + +I54 = create_builtin_item( + key_str="I54", + R1__has_label="mathematical property", + R2__has_description="base class for all mathematical properties", + R3__is_subclass_of=I11["general property"], +) + + +# next keys: I54, R79 # ###################################################################################################################### -# auxilliary entities +# auxiliary entities # ###################################################################################################################### @@ -2583,7 +2651,7 @@ def get_relation_properties(rel_entity: Entity) -> List[str]: R2__has_description="used during development as placeholder for relations which will be defined later", ) -# this allows to use I000("with any label") witout triggering an exception in I000.idoc +# this allows to use I000("with any label") without triggering an exception in I000.idoc I000._ignore_mismatching_adhoc_label = True # ... same for R000 R000._ignore_mismatching_adhoc_label = True @@ -2648,7 +2716,7 @@ def copy_statements(self, rel1: Relation, rel2: Relation): new_stm = stm.subject.set_relation(rel2, stm.object, prevent_duplicate=True) res.add_statement(new_stm) - # this function intentially does not return a new item; only called for its side-effects + # this function intentionally does not return a new item; only called for its side-effects return res diff --git a/src/pyerk/consistency_checking.py b/src/pyirk/consistency_checking.py similarity index 55% rename from src/pyerk/consistency_checking.py rename to src/pyirk/consistency_checking.py index 3e34aab..2deb541 100644 --- a/src/pyerk/consistency_checking.py +++ b/src/pyirk/consistency_checking.py @@ -1,5 +1,5 @@ """ -pyerk module for consitency checking. +pyirk module for consistency checking. """ @@ -14,18 +14,18 @@ from ipydex import IPS -class ErkConsistencyError(core.aux.PyERKError): +class IrkConsistencyError(core.aux.PyIRKError): pass -class ErkTypeError(ErkConsistencyError): +class IrkTypeError(IrkConsistencyError): pass -class WrongArgNumber(ErkTypeError): +class WrongArgNumber(IrkTypeError): pass -class WrongArgType(ErkTypeError): +class WrongArgType(IrkTypeError): pass @@ -50,6 +50,7 @@ def check_applied_operator(itm: Item): arg_type_items = [arg.R4__is_instance_of for arg in args] expected_arg_types = get_expected_arg_types(operator_itm) + # IPS() if len(arg_type_items) != len(expected_arg_types): msg = f"While checking {itm}: got {len(arg_type_items)} arg(s) but " f"{len(expected_arg_types)} where expected" @@ -57,27 +58,55 @@ def check_applied_operator(itm: Item): # the lengths match, now check the types - for i, (actual_type, expected_type) in enumerate(zip(arg_type_items, expected_arg_types)): - if bi.is_subclass_of(actual_type, expected_type, allow_id=True): - continue - - # the main type does not match. One of the secondary types might still match - continue_outer_loop = False - for secondary_class_item in args[i].R30__is_secondary_instance_of: - if bi.is_subclass_of(secondary_class_item, expected_type, allow_id=True): - continue_outer_loop = True + for i, (actual_type, expected_type_list) in enumerate(zip(arg_type_items, expected_arg_types)): + secondary_class_items = args[i].R30__is_secondary_instance_of + for expected_type in expected_type_list: + res = check_type(actual_type, secondary_class_items, expected_type, raise_exception=False) + if res: + # typechecking was successful break - if continue_outer_loop: - continue + else: + # for loop did terminate without break none of the types where correct + msg = ( + f"expected one of {expected_type_list} but got {actual_type}, while checking type of " + f"arg{i+1} for {itm}\n{get_error_location()}" + ) + raise WrongArgType(msg) + - # handle some special cases (TODO: I41["semantic rule"]-instances for this, see zebra puzzle test data) - if bi.is_subclass_of(actual_type, bi.I34["complex number"], allow_id=True) and expected_type == bi.I18["mathematical expression"]: - continue +def check_type(actual_type, secondary_class_items, expected_type, raise_exception=True) -> bool: + """ + :param actual_type: core.Item + :param secondary_class_items: List[core.Item] + :param expected_type: core.Item; + """ + if bi.is_subclass_of(actual_type, expected_type, allow_id=True): + return True + + # the main type does not match. One of the secondary types might still match + continue_outer_loop = False + for secondary_class_item in secondary_class_items: + if bi.is_subclass_of(secondary_class_item, expected_type, allow_id=True): + continue_outer_loop = True + break + if continue_outer_loop: + return True - # if we reach this there was no match -> error - msg = f"expected {expected_type} but got {actual_type}, while checking type of arg{i+1} for {itm}\n{get_error_location()}" + # handle some special cases (TODO: I41["semantic rule"]-instances for this, see zebra puzzle test data) + if bi.is_subclass_of(actual_type, bi.I34["complex number"], allow_id=True) and expected_type == bi.I18["mathematical expression"]: + return True + + # if we reach this there was no match -> error + if raise_exception: + + # no information available about the item where we check the types + # if needed this exception should be caught and re-raised + msg = f"expected {expected_type} but got {actual_type}, while checking type" raise WrongArgType(msg) + return False + + def get_error_location(): # TODO: This function gives only useful results if error occurs during module loading @@ -93,7 +122,7 @@ def get_error_location(): f = f.f_back else: # break was not reached - msg = "" + msg = "" return msg fname = os.path.split(fi.filename)[-1] code_context = "\n".join(fi.code_context).strip() @@ -115,11 +144,11 @@ def get_expected_arg_types(itm: Item) -> Tuple[Item]: match domains: case [None, _, _]: msg = f"unexpected: R8__has_domain_of_argument_1 is undefined for operator {itm}\n{get_error_location()}" - raise ErkTypeError(msg) + raise IrkTypeError(msg) case [_, None, a3] if a3 is not None: msg = f"inconsistency for operator {itm}: domain for arg3 defined but not for arg2\n{get_error_location()}" - raise ErkTypeError(msg) + raise IrkTypeError(msg) case [a1, None, None]: arity = 1 case [a1, a2, None]: @@ -128,7 +157,7 @@ def get_expected_arg_types(itm: Item) -> Tuple[Item]: arity = 3 case _: msg = f"unexpected domain structure for {itm}: {domains}\n {get_error_location()}" - raise ErkTypeError(msg) + raise IrkTypeError(msg) domains = domains[:arity] @@ -136,15 +165,17 @@ def get_expected_arg_types(itm: Item) -> Tuple[Item]: res = [] for i, dom in enumerate(domains): assert isinstance(dom, list) - if len(dom) != 1: - msg = f"multi-valued domains are not yet supported (arg {i+1}) " f"of {itm}" - raise core.aux.NotYetFinishedError(msg) - res.append(dom[0]) + if len(dom) == 0: + f"empty list is not allowed in domain specification (R8/R9/R10) of {itm}" + raise core.aux.InvalidObjectValue(msg) + else: + # append the list (multi valued domain) + res.append(dom) return res -def enable_consitency_checking(): +def enable_consistency_checking(): core.register_hook("post-finalize-item", check) diff --git a/src/pyerk/core.py b/src/pyirk/core.py similarity index 94% rename from src/pyerk/core.py rename to src/pyirk/core.py index 94fb7d0..13caa01 100644 --- a/src/pyerk/core.py +++ b/src/pyirk/core.py @@ -1,5 +1,5 @@ """ -Core module of pyerk +Core module of pyirk """ import os import sys @@ -19,12 +19,12 @@ import pydantic import re -from pyerk import auxiliary as aux -from pyerk import settings -from pyerk.auxiliary import ( +from pyirk import auxiliary as aux +from pyirk import settings +from pyirk.auxiliary import ( InvalidURIError, InvalidPrefixError, - PyERKError, + PyIRKError, EmptyURIStackError, InvalidShortKeyError, UnknownPrefixError, @@ -36,44 +36,12 @@ activate_ips_on_exception() -""" - TODO: - - report should link entities to django - - model sets as type? and elements as instances? - manually trigger reload in gui - - Caylay-Hamilton-Theorem - qualifier relations, e.g. for universal quantification - - Lyapunov stability theorem - visualizing the results - has implementation (application to actual instances) - - - multiple assignments via list - natural language representation of ordered atomic statements - Labels (als Listeneinträge) - DOMAIN und RANGE - - unittests ✓ - Sanity-check: `R1__part_of` muss einen Fehler werfen +allowed_literal_types = (str, bool, float, int, complex, Literal) - content: dynamical_system can_be_represented_by mathematical_model - → Herausforderung: in OWL sind relationen nur zwischen Instanzen zulässig. - Damit ist die Angabe von DOMAIN und RANGE, relativ klar. Wenn die Grenze zwischen Klasse und Instanz verschwimmt - ist das nicht mehr so klar: Jede Instanz der Klasse ??? - - anders: ist eine n:m Zuordnung von Instanzen der Klasse - zu Instanzen der Klasse - - komplexere Aussagen: - alle steuerbaren linearen ODE-systeme sind flach - -""" - -allowed_literal_types = (str, bool, float, int, complex) +# Relations with R11__has_range_of_result=I19["multilingual string literal"] (due to multilinguality support) +# Here we should automatically identify those relations which have R11__has_range_of_result=I19["multilingual string literal"] +# for now these are hardcoded (which is also faster) +RELKEYS_WITH_LITERAL_RANGE = ("R1", "R2", "R77") # copied from yamlpyowl project @@ -159,7 +127,7 @@ def __call__(self, *args, **kwargs): def idoc(self, adhoc_label: str): """ idoc means "inline doc". This function allows to attach a label to entities when using them in code - because it returns just the Entity-object itself. Thus one can use the following expressions interchageably: + because it returns just the Entity-object itself. Thus one can use the following expressions interchangeably: `I1234` and `I1234.idoc("human readable item name")` Note that there is a shortcut to this function: `I1234["human readable item name"] @@ -183,7 +151,7 @@ def idoc(self, adhoc_label: str): if adhoc_label_str not in all_labels_dict: msg = ( - f"Mismatiching label for Entity {self.short_key}! Got '{adhoc_label}' but valid labels are: " + f"Mismatching label for Entity {self.short_key}! Got '{adhoc_label}' but valid labels are: " f" {all_labels}.\n\n" f"Note: in index-labeled key notation the language of the labels is ignored for convenience." ) @@ -373,6 +341,10 @@ def _get_relation_contents(self, rel_uri: str, lang_indicator=None): @classmethod def add_method_to_class(cls, func): + """ + Used to add methods to the class from the builtin_entities module. + This mechanism (adding the method later) allows to keep the dependency monodirectional + """ setattr(cls, func.__name__, func) def add_method(self, func: callable, name: Optional[str] = None): @@ -417,7 +389,7 @@ def _set_relations_from_init_kwargs(self, **kwargs): else: self.set_relation(key, value) - def set_mutliple_relations( + def set_multiple_relations( self, relation: Union["Relation", str], obj_seq: Union[tuple, list], *args, **kwargs ) -> List["Statement"]: """ @@ -465,19 +437,24 @@ def set_relation( if obj in existing_objects: return None - if isinstance(relation, Relation): + if not isinstance(relation, Relation): + msg = f"unexpected type: {type(relation)} of relation object {relation}, with {self} as subject" + raise TypeError(msg) - # handle R32__is_functional_for_each_language - if relation.R32 and not isinstance(obj, Literal): - obj = Literal(obj, lang=settings.DEFAULT_DATA_LANGUAGE) + if isinstance(obj, (list, tuple)): + msg = f"Sequences like ({type(obj)}) are not allowed in `.set_relation`. Use `.set_multiple_relations`." + raise TypeError(msg) - if isinstance(obj, (Entity, *allowed_literal_types)) or obj in allowed_literal_types: - return self._set_relation(relation.uri, obj, scope=scope, qualifiers=qualifiers, proxyitem=proxyitem) - else: - msg = f"Unsupported type ({type(obj)}) of {obj}, while setting relation {relation.short_key} of {self}" - raise TypeError(msg) + # handle R32__is_functional_for_each_language + enforce_literal_as_type = relation.short_key in RELKEYS_WITH_LITERAL_RANGE or relation.R32 + + if enforce_literal_as_type and not isinstance(obj, Literal): + obj = Literal(obj, lang=settings.DEFAULT_DATA_LANGUAGE) + + if isinstance(obj, (Entity, *allowed_literal_types)) or obj in allowed_literal_types: + return self._set_relation(relation.uri, obj, scope=scope, qualifiers=qualifiers, proxyitem=proxyitem) else: - msg = f"unexpected type: {type(relation)} of relation object {relation}, with {self} as subject" + msg = f"Unsupported type ({type(obj)}) of {obj}, while setting relation {relation.short_key} of {self}" raise TypeError(msg) def _set_relation( @@ -498,12 +475,12 @@ def _set_relation( if isinstance(rel_content, Entity): corresponding_entity = rel_content corresponding_literal = None - else: + elif isinstance(rel_content, allowed_literal_types): corresponding_entity = None - - if not isinstance(rel_content, (str, int, float, complex)): - rel_content = repr(rel_content) corresponding_literal = rel_content + else: + msg = f"unexpected type: {type(rel_content)} for object {rel_content}" + raise TypeError(msg) if qualifiers is None: qualifiers = [] @@ -665,10 +642,10 @@ def overwrite_statement(self, rel_key_str_or_uri: str, new_obj: "Entity", qualif if isinstance(stm, list): if len(stm) == 0: msg = f"Unexpectedly found empty statement list for entity {self} and relation {rel}" - raise aux.PyERKError(msg) + raise aux.PyIRKError(msg) if len(stm) > 1: msg = f"Unexpectedly found length-{len(stm)} statement list for entity {self} and relation {rel}" - raise aux.PyERKError(msg) + raise aux.PyIRKError(msg) stm = stm[0] assert isinstance(stm, Statement) @@ -699,7 +676,7 @@ def wrap_function_with_search_uri_context(func, uri=None): if uri is None: fi = inspect.getframeinfo(frame.f_back) msg = f"could not find `__URI__` in module {fi.filename}" - raise aux.PyERKError(msg) + raise aux.PyIRKError(msg) @functools.wraps(func) def wrapped_func(*args, **kwargs): @@ -775,7 +752,7 @@ def __init__(self): # dict like {uri1: , ...} self.uri_mod_dict = {} - # this flag (default False) might be changed during erkloader calls + # this flag (default False) might be changed during irkloader calls self.reuse_loaded_module = False # this list serves to keep track of nested scopes @@ -901,7 +878,7 @@ def set_statement(self, stm: "Statement") -> None: """ Insert a Statement into the relevant data structures of the DataStorage (self) - This method does not handle the dual realtion. It must be created and stored separately. + This method does not handle the dual relation. It must be created and stored separately. :param stm: Statement instance :return: @@ -985,9 +962,9 @@ def preprocess_query(self, query): # check sanity prefix, rest = e.split(":") prefix = prefix + ":" - erk_key, description = rest.split("__") + irk_key, description = rest.split("__") - entity_uri = prefix_dict.get(prefix) + erk_key + entity_uri = prefix_dict.get(prefix) + irk_key entity = self.get_entity_by_uri(entity_uri) label = description.replace("_", " ") @@ -1019,7 +996,7 @@ def remove_scope(self, scope): current_scope = self.get_current_scope() if current_scope != scope: msg = "Refuse to remove scope which is not the topmost on the stack (i.e. the last in the list)" - raise PyERKError(msg) + raise PyIRKError(msg) self.scope_stack.pop() @@ -1027,8 +1004,8 @@ def get_current_scope(self): try: return self.scope_stack[-1] except IndexError: - msg = "unexepectedly found the scope stack empty" - raise PyERKError(msg) + msg = "unexpectedly found the scope stack empty" + raise PyIRKError(msg) ds = DataStore() @@ -1054,7 +1031,7 @@ class SType(Enum): """ CREATION = 0 - EXTENTION = 1 + EXTENSION = 1 UNDEFINED = 2 @@ -1118,16 +1095,16 @@ def process_key_str( mod_uri: str = None, ) -> ProcessedStmtKey: """ - In ERK there are the following kinds of keys: + In IRK there are the following kinds of keys: - a) short_key like `R1234` - b) name-labeled key like `R1234__my_relation` (consisting of a short_key, a delimiter (`__`) and a label) - c) prefixed short_key like `bi__R1234` - d) prefixed name-labeled key like `bi__R1234__my_relation` - - e) index-labeld key like `R1234["my relation"]` - - f) prefixed index-labeld key like `bi__R1234["my relation"]` + - e) index-labeled key like `R1234["my relation"]` + - f) prefixed index-labeled key like `bi__R1234["my relation"]` - See also: userdoc/overview.html#keys-in-pyerk + See also: userdoc/overview.html#keys-in-pyirk Also, the leading character indicates the entity type (EType). @@ -1147,7 +1124,7 @@ def process_key_str( match1 = re_prefix_shortkey_suffix.match(key_str) - errmsg = f"unxexpected key_str: `{key_str}` (maybe a literal or syntax error)" + errmsg = f"unexpected key_str: `{key_str}` (maybe a literal or syntax error)" if not match1: raise aux.InvalidGeneralKeyError(errmsg) @@ -1185,7 +1162,7 @@ def process_key_str( res.etype = EType.RELATION res.vtype = VType.ENTITY else: - msg = f"unxexpected shortkey: '{res.short_key}' (maybe a literal)" + msg = f"unexpected shortkey: '{res.short_key}' (maybe a literal)" raise aux.InvalidShortKeyError(msg) if resolve_prefix: @@ -1269,7 +1246,7 @@ def _resolve_prefix(pr_key: ProcessedStmtKey, passed_mod_uri: str = None) -> Non # if res_entity is still None no entity could be found msg = ( f"No entity could be found for short_key {pr_key.short_key}, neither in active module " - f"({active_mod_uri}) nor in builin_entities ({settings.BUILTINS_URI})" + f"({active_mod_uri}) nor in builtin_entities ({settings.BUILTINS_URI})" ) raise aux.ShortKeyNotFoundError(msg) else: @@ -1331,7 +1308,7 @@ def check_processed_key_label(pkey: ProcessedStmtKey) -> None: def ilk2nlk(ilk: str) -> str: """ - convert index labled key (R1234["my relation"]) to name labled key (R1234__my_relation) + convert index labeled key (R1234["my relation"]) to name labeled key (R1234__my_relation) """ assert isinstance(ilk, str) @@ -1386,7 +1363,7 @@ def get_active_mod_uri(strict: bool = True) -> Union[str, None]: res = _uri_stack[-1] except IndexError: msg = ( - "Unexpected: empty uri_stack. Be sure to use uri_contex manager or similar technique " + "Unexpected: empty uri_stack. Be sure to use uri_context manager or similar technique " "when creating entities" ) if strict: @@ -1419,8 +1396,7 @@ def process_kwargs_for_entity_creation(entity_key: str, kwargs: dict) ->(dict, d new_key = processed_key.short_key # handle those relations which might come with multiple languages - # (related to R32__is_functional_for_each_language) - if new_key in ("R1", "R2"): + if new_key in RELKEYS_WITH_LITERAL_RANGE: value_list = lang_related_kwargs[new_key] if len(value_list) == 0: valid_languages = (None, settings.DEFAULT_DATA_LANGUAGE) @@ -1503,7 +1479,7 @@ def create_item(key_str: str = "", **kwargs) -> Item: assert itm.uri not in ds.items, f"Problematic (duplicated) uri: {itm.uri}" ds.items[itm.uri] = itm - # acces the defaultdict(list) + # access the defaultdict(list) ds.entities_created_in_mod[mod_uri].append(itm.uri) process_lang_related_kwargs_for_entity_creation(itm, item_key, lang_related_kwargs) @@ -1586,7 +1562,7 @@ def register_hook(type_str: str, func: callable) -> None: # for now we want unique numbers for keys for relations and items etc (although this is not necessary) class KeyManager: """ - Class for a flexible and comprehensible key management. Every pyerk module must have its own (passed via) + Class for a flexible and comprehensible key management. Every pyirk module must have its own (passed via) """ # TODO: the term "maxval" is misleading because it will be used in range where the upper bound is exclusive @@ -1615,7 +1591,7 @@ def pop(self, index: int = -1) -> int: def _generate_key_numbers(self) -> None: """ - Creates a reaservoir of keynumbers, e.g. for automatically created entities. Due to the hardcoded seed value + Creates a reservoir of keynumbers, e.g. for automatically created entities. Due to the hardcoded seed value these numbers are stable between runs of the software, which simplifies development and debugging. This function is also called after unloading a module because the respective keys are "free" again @@ -1628,7 +1604,7 @@ def _generate_key_numbers(self) -> None: assert self.key_reservoir is None - # passing seed (arg `x`) ensures "reproducible randomness" accross runs + # passing seed (arg `x`) ensures "reproducible randomness" across runs if not self.keyseed: # use hardcoded fallback self.keyseed = 1750 @@ -1714,8 +1690,10 @@ def __call__(self, obj): class Statement: + # Note: in earlier versions this class was called "RelationEdge"; + # some old comments might refer to this """ - Models a conrete (instantiated/applied) relation between entities. This is basically a dict. + Models a concrete (instantiated/applied) relation between entities. This is basically a dict. """ def __init__( @@ -1733,7 +1711,7 @@ def __init__( :param relation: :param relation_tuple: - :param role: RelationRole.SUBJECT for normal and RelationRole.OBJECT for inverse edges + :param role: RelationRole.SUBJECT for normal and RelationRole.OBJECT for inverse statements :param corresponding_entity: This is the entity on the "other side" of the relation (depending of `role`) or None in case that other side is a literal :param corresponding_literal: This is the literal on the "other side" of the relation (depending of `role`) or @@ -1749,7 +1727,7 @@ def __init__( self.base_uri = mod_uri self.uri = f"{aux.make_uri(self.base_uri, self.short_key)}" self.relation = relation - self.rsk = relation.short_key # to conviniently access this attribute in visualization + self.rsk = relation.short_key # to conveniently access this attribute in visualization self.relation_tuple = relation_tuple self.subject = relation_tuple[0] self.predicate = relation_tuple[1] @@ -2137,7 +2115,7 @@ def __init__(self, uri_stack: list, uri: str, prefix: str = None): def __enter__(self): """ - implicitly called in the head of the with statemet + implicitly called in the head of the with statement :return: """ self.uri_stack.append(self.uri) @@ -2175,7 +2153,7 @@ def __init__(self, uri: str, prefix: str = None): def unload_mod(mod_uri: str, strict=True) -> None: """ - Delete all references to entities comming from a module with `mod_id` + Delete all references to entities coming from a module with `mod_id` :param mod_uri: str; uri of the module, see its __URI__ attribute :param strict: boolean; raise Exception if module seems be not loaded @@ -2237,7 +2215,7 @@ def unload_mod(mod_uri: str, strict=True) -> None: def _unlink_entity(uri: str, remove_from_mod=False) -> None: """ - Remove the occurrence of this the respective entitiy from all relevant data structures + Remove the occurrence of this the respective entity from all relevant data structures :param uri: entity uri :return: None @@ -2268,7 +2246,7 @@ def _unlink_entity(uri: str, remove_from_mod=False) -> None: re_dict = ds.statements.pop(entity.uri, {}) inv_re_dict = ds.inv_statements.pop(entity.uri, {}) - # in case res1 is a scope-item we delete all corressponding relation edges, otherwise nothing happens + # in case res1 is a scope-item we delete all corresponding relation edges, otherwise nothing happens scope_rels = ds.scope_statements.pop(uri, []) re_list = list(scope_rels) @@ -2277,7 +2255,7 @@ def _unlink_entity(uri: str, remove_from_mod=False) -> None: re_item_list = list(re_dict.items()) + list(inv_re_dict.items()) for rel_uri, local_re_list in re_item_list: - # rel_uri: uri of the relation (like "pyerk/foo#R1234") + # rel_uri: uri of the relation (like "pyirk/foo#R1234") # re_list: list of Statement instances re_list.extend(local_re_list) @@ -2307,7 +2285,7 @@ def replace_and_unlink_entity(old_entity: Entity, new_entity: Entity): res = RuleResult() - from pyerk import builtin_entities as bi + from pyirk import builtin_entities as bi # these predicates should not be replaced omit_uris = aux.uri_set( @@ -2346,7 +2324,7 @@ def replace_and_unlink_entity(old_entity: Entity, new_entity: Entity): # prevent the creation of a duplicated statement existing_objs = new_entity.get_relations(predicate.uri, return_obj=True) if not obj in existing_objs: - # it is possible that predicate is functional and new_entitiy.predicate has a value + # it is possible that predicate is functional and new_entity.predicate has a value # different from obj. this is OK if one of them is a placeholder if len(existing_objs) == 1 and predicate.R22__is_functional: existing_obj = existing_objs[0] @@ -2355,7 +2333,7 @@ def replace_and_unlink_entity(old_entity: Entity, new_entity: Entity): continue elif not existing_obj.R57__is_placeholder and not obj.R57__is_placeholder: msg = ( - f"confilicting statement for functional predicate {predicate} and non-placeholder " + f"conflicting statement for functional predicate {predicate} and non-placeholder " f"objects: {obj} (of old_entity) and {existing_obj} of new_entity, while replacing" f"{old_entity} (old) with {new_entity} (new)." ) @@ -2379,7 +2357,7 @@ def replace_and_unlink_entity(old_entity: Entity, new_entity: Entity): return res -def register_mod(uri: str, keymanager: KeyManager, check_uri=True): +def register_mod(uri: str, keymanager: KeyManager, check_uri=True, prefix=None): frame = get_caller_frame(upcount=1) path = os.path.abspath(frame.f_globals["__file__"]) if check_uri: @@ -2396,12 +2374,17 @@ def register_mod(uri: str, keymanager: KeyManager, check_uri=True): # all modules should have their own key manager ds.uri_keymanager_dict[uri] = keymanager + # currently this is only used from within unittests as they create test data on the fly and + # not use irkloader for every tiny item + if prefix: + ds.uri_prefix_mapping.add_pair(key_a=uri, key_b=prefix) + def start_mod(uri): """ Register the uri for the _uri_stack. - Note: between start_mod and end_mod no it is not allowed to load other erk modules + Note: between start_mod and end_mod no it is not allowed to load other irk modules :param uri: :return: @@ -2422,7 +2405,7 @@ def get_language_of_str_literal(obj: Union[str, Literal]): return None -class LangaguageCode: +class LanguageCode: def __init__(self, langtag): assert langtag in settings.SUPPORTED_LANGUAGES @@ -2445,12 +2428,12 @@ def __rmatmul__(self, arg: str) -> Literal: return res -df = LangaguageCode(settings.DEFAULT_DATA_LANGUAGE) -en = LangaguageCode("en") -de = LangaguageCode("de") -fr = LangaguageCode("fr") -it = LangaguageCode("it") -es = LangaguageCode("es") +df = LanguageCode(settings.DEFAULT_DATA_LANGUAGE) +en = LanguageCode("en") +de = LanguageCode("de") +fr = LanguageCode("fr") +it = LanguageCode("it") +es = LanguageCode("es") class RuleResult: @@ -2523,6 +2506,16 @@ def rule(self): return self._rule +def is_true(subject: Entity, predicate: Relation, object) -> (bool, None): + assert isinstance(subject, Entity) + assert isinstance(predicate, Relation) + + res = subject.get_relations(predicate.uri, return_obj=True) + if isinstance(res, list): + res = res[0] + return res == object + + def format_entity_html(e: Entity): short_txt = f'{e.R1}' detailed_txt = f'{e.short_key}["{e.R1}"]' diff --git a/src/pyerk/io.py b/src/pyirk/io.py similarity index 96% rename from src/pyerk/io.py rename to src/pyirk/io.py index ca40788..5cbcce8 100644 --- a/src/pyerk/io.py +++ b/src/pyirk/io.py @@ -12,7 +12,7 @@ # noinspection PyUnresolvedReferences from ipydex import IPS # noqa -import pyerk as p +import pyirk as p def export_rdf_triples(fpath: str, **kwargs): @@ -57,7 +57,7 @@ def import_stms_from_rdf_triples(fpath: str): continue for elt in row: - new_row.append(p.rdfstack.convert_from_rdf_to_pyerk(elt)) + new_row.append(p.rdfstack.convert_from_rdf_to_pyirk(elt)) new_rows.append(new_row) res.new_stms = [] diff --git a/src/pyerk/erkloader.py b/src/pyirk/irkloader.py similarity index 75% rename from src/pyerk/erkloader.py rename to src/pyirk/irkloader.py index 284733f..3cd7d4d 100644 --- a/src/pyerk/erkloader.py +++ b/src/pyirk/irkloader.py @@ -2,7 +2,7 @@ import sys import os import inspect -import pyerk +import pyirk import pathlib import functools import addict @@ -40,40 +40,40 @@ def load_mod_from_path( """ :param modpath: file system path for the module to be loaded - :param prefix: prefix which can be used to replace the URI for convenice + :param prefix: prefix which can be used to replace the URI for convenience :param modname: - :param allow_reload: flag; if False, an error is raised if the module was already loades + :param allow_reload: flag; if False, an error is raised if the module was already loaded :param smart_relative: flag; if True, relative paths are interpreted w.r.t. the calling module (not w.r.t. current working path) :param reuse_loaded: flag; if True and the module was already loaded before, then just use this - if False:: reload; if None use the default action from pyerk.ds + if False:: reload; if None use the default action from pyirk.ds :return: """ - reuse_loaded_original = pyerk.ds.reuse_loaded_module + reuse_loaded_original = pyirk.ds.reuse_loaded_module match reuse_loaded: case True: reuse_loaded__actual = True - pyerk.ds.reuse_loaded_module = True + pyirk.ds.reuse_loaded_module = True case False: reuse_loaded__actual = False - pyerk.ds.reuse_loaded_module = False + pyirk.ds.reuse_loaded_module = False case None: - # use the (unchange) default - reuse_loaded__actual = pyerk.ds.reuse_loaded_module + # use the (unchanged) default + reuse_loaded__actual = pyirk.ds.reuse_loaded_module try: mod = _load_mod_from_path(modpath, prefix, modname, allow_reload, smart_relative, reuse_loaded__actual) except: if reuse_loaded is not None: # we had changed the default - pyerk.ds.reuse_loaded_module = reuse_loaded_original + pyirk.ds.reuse_loaded_module = reuse_loaded_original raise if reuse_loaded is not None: # we had changed the default - pyerk.ds.reuse_loaded_module = reuse_loaded_original + pyirk.ds.reuse_loaded_module = reuse_loaded_original return mod @@ -91,10 +91,10 @@ def _load_mod_from_path( # save some data structures in order to reenable them if something goes wrong - original_loaded_mod_uris = list(pyerk.ds.mod_path_mapping.a.keys()) + original_loaded_mod_uris = list(pyirk.ds.mod_path_mapping.a.keys()) if smart_relative is not None: - msg = "Using 'smart_relative' paths is deprecated since pyerk version 0.6.0. Please use real paths now." + msg = "Using 'smart_relative' paths is deprecated since pyirk version 0.6.0. Please use real paths now." raise DeprecationWarning(msg) smart_relative = False @@ -115,7 +115,7 @@ def _load_mod_from_path( # the path is either absolute or should be interpreted w.r.t. current working directory modpath = os.path.abspath(modpath) - old_mod_uri = pyerk.ds.mod_path_mapping.b.get(modpath) + old_mod_uri = pyirk.ds.mod_path_mapping.b.get(modpath) if old_mod_uri: if reuse_loaded: @@ -123,10 +123,10 @@ def _load_mod_from_path( return sys.modules[modname] if allow_reload: - pyerk.unload_mod(old_mod_uri) + pyirk.unload_mod(old_mod_uri) else: msg = f"Unintended attempt to reload module {old_mod_uri}" - raise pyerk.aux.ModuleAlreadyLoadedError(msg) + raise pyirk.aux.ModuleAlreadyLoadedError(msg) # the following code is newer and thus uses pathlib.Path # TODO: simplify the above code (after removing smart_relative) @@ -145,56 +145,56 @@ def _load_mod_from_path( assert modname not in sys.modules sys.modules[modname] = mod - old_len = len(pyerk.core._uri_stack) + old_len = len(pyirk.core._uri_stack) try: # noinspection PyUnresolvedReferences spec.loader.exec_module(mod) except Exception: - if len(pyerk.core._uri_stack) > old_len: + if len(pyirk.core._uri_stack) > old_len: # deactivate the failed module (because execution did not reach end_mod()) - failed_mod_uri = pyerk.core._uri_stack.pop() + failed_mod_uri = pyirk.core._uri_stack.pop() # remove all added entities, but tolerate errors (due to incomplete loading) - pyerk.unload_mod(failed_mod_uri, strict=False) + pyirk.unload_mod(failed_mod_uri, strict=False) # ensure that the current module is not lurking in sys.modules sys.modules.pop(modname, None) raise - if len(pyerk.core._uri_stack) > old_len: - failed_mod_uri = pyerk.core._uri_stack.pop() + if len(pyirk.core._uri_stack) > old_len: + failed_mod_uri = pyirk.core._uri_stack.pop() msg = ( - f"The module {failed_mod_uri} was not properly ended. Ensure that pyerk.end_mod() is called after " - "the last PyERK-statement." + f"The module {failed_mod_uri} was not properly ended. Ensure that pyirk.end_mod() is called after " + "the last PyIRK-statement." ) - raise pyerk.PyERKError(msg) + raise pyirk.PyIRKError(msg) mod_uri = getattr(mod, "__URI__") if mod_uri is None: msg = f"The module from path {modpath} could not be loaded. No valid `__URI__` attribute found." - raise pyerk.PyERKError(msg) + raise pyirk.PyIRKError(msg) - pyerk.aux.ensure_valid_baseuri(mod_uri) + pyirk.aux.ensure_valid_baseuri(mod_uri) - if mod_uri in pyerk.ds.uri_prefix_mapping.a: + if mod_uri in pyirk.ds.uri_prefix_mapping.a: _cleanup(mod_uri, modname, original_loaded_mod_uris) msg = f"While loading {modpath}: URI '{mod_uri}' was already registered." - raise pyerk.aux.InvalidURIError(msg) + raise pyirk.aux.InvalidURIError(msg) - if prefix in pyerk.ds.uri_prefix_mapping.b: + if prefix in pyirk.ds.uri_prefix_mapping.b: _cleanup(mod_uri, modname, original_loaded_mod_uris) msg = f"While loading {modpath}: prefix '{prefix}' was already registered." - raise pyerk.aux.InvalidPrefixError(msg) + raise pyirk.aux.InvalidPrefixError(msg) - pyerk.ds.uri_prefix_mapping.add_pair(mod_uri, prefix) + pyirk.ds.uri_prefix_mapping.add_pair(mod_uri, prefix) - pyerk.ds.uri_mod_dict[mod_uri] = mod + pyirk.ds.uri_mod_dict[mod_uri] = mod # the modnames are needed to keep sys.modules in sync - pyerk.ds.modnames[mod_uri] = modname + pyirk.ds.modnames[mod_uri] = modname mod.__fresh_load__ = True return mod @@ -205,11 +205,11 @@ def _cleanup(mod_uri, modname, original_loaded_mod_uris): Clean up some data structures if something went wrong during module load. This helps to keep the tests independent. """ - pyerk.unload_mod(mod_uri, strict=False) + pyirk.unload_mod(mod_uri, strict=False) sys.modules.pop(modname, None) # due to dependencies there might have been other modules loaded -> unload them - modules_to_unload = [uri for uri in pyerk.ds.mod_path_mapping.a if uri not in original_loaded_mod_uris] + modules_to_unload = [uri for uri in pyirk.ds.mod_path_mapping.a if uri not in original_loaded_mod_uris] for uri in modules_to_unload: - pyerk.unload_mod(uri) + pyirk.unload_mod(uri) diff --git a/src/pyerk/rdfstack.py b/src/pyirk/rdfstack.py similarity index 83% rename from src/pyerk/rdfstack.py rename to src/pyirk/rdfstack.py index e26299b..6f7794c 100644 --- a/src/pyerk/rdfstack.py +++ b/src/pyirk/rdfstack.py @@ -3,7 +3,7 @@ """ from typing import Union -from . import core as pyerk, auxiliary as aux +from . import core as pyirk, auxiliary as aux from .auxiliary import STATEMENTS_URI_PART, PREDICATES_URI_PART, QUALIFIERS_URI_PART # noinspection PyUnresolvedReferences @@ -19,11 +19,11 @@ from pyparsing import ParseException # noqa -ERK_URI = f"{pyerk.settings.BUILTINS_URI}{pyerk.settings.URI_SEP}" +IRK_URI = f"{pyirk.settings.BUILTINS_URI}{pyirk.settings.URI_SEP}" def _make_rel_uri_with_suffix(rel_uri: str, suffix: str): - pyerk.aux.ensure_valid_relation_uri(rel_uri) + pyirk.aux.ensure_valid_relation_uri(rel_uri) new_uri = rel_uri.replace("#", f"{suffix}#") assert len(new_uri) == len(rel_uri) + len(suffix) return new_uri @@ -42,16 +42,16 @@ def make_qualifier_uri(rel_uri: str): def serialize_object(obj): - if isinstance(obj, pyerk.Entity): + if isinstance(obj, pyirk.Entity): return URIRef(f"{obj.uri}") else: - assert isinstance(obj, pyerk.allowed_literal_types) + assert isinstance(obj, pyirk.allowed_literal_types) # no entity but a literal value return Literal(obj) -def get_statement_rows(stm: pyerk.Statement): +def get_statement_rows(stm: pyirk.Statement): row1 = [URIRef(stm.subject.uri), URIRef(make_statement_uri(stm.predicate.uri)), URIRef(stm.uri)] row2 = [URIRef(stm.uri), URIRef(make_predicate_uri(stm.predicate.uri)), serialize_object(stm.object)] @@ -73,13 +73,13 @@ def create_rdf_triples(add_qualifiers=False, add_statements=False, modfilter=Non if isinstance(modfilter, str): modfilter = set([modfilter]) - for stm_uri, stm in pyerk.ds.statement_uri_map.items(): + for stm_uri, stm in pyirk.ds.statement_uri_map.items(): if not check_uri_in_modfilter(stm.uri, modfilter): continue row = [] for i, entity in enumerate(stm.relation_tuple): - if isinstance(entity, pyerk.Statement): + if isinstance(entity, pyirk.Statement): # stm is a qualifier-statement which has another statement as subject assert i == 0 qualifier_statements.append(stm) @@ -103,7 +103,7 @@ def create_rdf_triples(add_qualifiers=False, add_statements=False, modfilter=Non if not check_uri_in_modfilter(qstm.uri, modfilter): continue - qstm: pyerk.Statement + qstm: pyirk.Statement subj_stm, pred, obj = qstm.relation_tuple if qstm.uri not in processed_statements: @@ -134,10 +134,9 @@ def check_uri_in_modfilter(uri, modfilter): def check_subclass(entity, class_item): - # wip! - # Hier müsste man prüfen ob es eine instanz-subklassen*-Beziehung gibt - # das wird insbesondere dann spannend, wenn es sowas wie pseudo-mehrfachvererbung gibt + # TODO (use the implemented functionality) + # Here we should check wether there is an instance subclass-relation res = [] res.extend(aux.ensure_list(entity.R4__is_instance_of)) res.extend(aux.ensure_list(entity.R3__is_subclass_of)) @@ -150,23 +149,23 @@ def check_subclass(entity, class_item): def perform_sparql_query(qsrc: str, return_raw=False) -> Sparql_results_type: - if pyerk.ds.rdfgraph is None: - pyerk.ds.rdfgraph = create_rdf_triples() + if pyirk.ds.rdfgraph is None: + pyirk.ds.rdfgraph = create_rdf_triples() - res = pyerk.ds.rdfgraph.query(qsrc) + res = pyirk.ds.rdfgraph.query(qsrc) if return_raw: return res else: - res2 = aux.apply_func_to_table_cells(convert_from_rdf_to_pyerk, res) + res2 = aux.apply_func_to_table_cells(convert_from_rdf_to_pyirk, res) res2.vars = res.vars return res2 -def convert_from_rdf_to_pyerk(rdfnode) -> object: +def convert_from_rdf_to_pyirk(rdfnode) -> object: if isinstance(rdfnode, URIRef): uri = rdfnode.toPython() - entity_object = pyerk.ds.get_entity_by_uri(uri) + entity_object = pyirk.ds.get_entity_by_uri(uri) elif isinstance(rdfnode, Literal): entity_object = rdfnode.value elif rdfnode is None: @@ -183,7 +182,7 @@ def convert_from_rdf_to_pyerk(rdfnode) -> object: def get_sparql_example_query(): qsrc = f""" - PREFIX : <{ERK_URI}> + PREFIX : <{IRK_URI}> SELECT ?s ?o WHERE {{ ?s :R5 ?o. @@ -194,8 +193,8 @@ def get_sparql_example_query(): def get_sparql_example_query2(): qsrc = f""" - PREFIX : <{ERK_URI}> - PREFIX ocse: + PREFIX : <{IRK_URI}> + PREFIX ocse: SELECT ?s WHERE {{ ?s :R16 ocse:I7733. @@ -215,17 +214,17 @@ def check_all_relation_types(): n = list(res)[0][0] - res2 = aux.apply_func_to_table_cells(convert_from_rdf_to_pyerk, res) + res2 = aux.apply_func_to_table_cells(convert_from_rdf_to_pyirk, res) # IPS() raise aux.NotYetFinishedError """ - for rel_key, re_list in pyerk.ds.relation_statements.items(): + for rel_key, re_list in pyirk.ds.relation_statements.items(): for re in re_list: - re: pyerk.Statement + re: pyirk.Statement subj, pred, obj = re.relation_tuple - pred: pyerk.Relation + pred: pyirk.Relation # TODO: get rid of ensure_list here expected_domain1_list = aux.ensure_list(pred.R8) diff --git a/src/pyirk/release.py b/src/pyirk/release.py new file mode 100644 index 0000000..ea370a8 --- /dev/null +++ b/src/pyirk/release.py @@ -0,0 +1 @@ +__version__ = "0.12.0" diff --git a/src/pyerk/reportgenerator.py b/src/pyirk/reportgenerator.py similarity index 87% rename from src/pyerk/reportgenerator.py rename to src/pyirk/reportgenerator.py index 0808496..61d85a7 100644 --- a/src/pyerk/reportgenerator.py +++ b/src/pyirk/reportgenerator.py @@ -12,9 +12,9 @@ import tomli as tomllib -from . import erkloader -import pyerk as p -from pyerk.erkloader import preserve_cwd +from . import irkloader +import pyirk as p +from pyirk.irkloader import preserve_cwd from . import settings @@ -26,7 +26,7 @@ def generate_report(reportconf_path: str): class ReportGenerator: """ - Omnipotent class that manages the report-generation. Assumend to be a singleton. + Omnipotent class that manages the report-generation. Assumed to be a singleton. """ @preserve_cwd @@ -61,8 +61,8 @@ def load_modules(self) -> list: for prefix, path in lmdict.items(): if path.startswith("$"): - path = path[1:].replace("__erk-root__", p.aux.get_erk_root_dir()) - mod = erkloader.load_mod_from_path(path, prefix=prefix) + path = path[1:].replace("__irk-root__", p.aux.get_irk_root_dir()) + mod = irkloader.load_mod_from_path(path, prefix=prefix) res.append(mod) return res @@ -113,7 +113,7 @@ def generate_report(self): # this is a function to be easier testable def resolve_entities_in_nested_data(data): """ - process data: detect and resolve pyerk key strings, leaving everything else unchanged (assuming literals) + process data: detect and resolve pyirk key strings, leaving everything else unchanged (assuming literals) :param data: one of (dict, str, list, int, float) @@ -127,9 +127,9 @@ def resolve_entities_in_nested_data(data): if isinstance(data, str): if data.startswith(":"): - erk_key_str = data[1:] - entity = p.ds.get_entity_by_key_str(erk_key_str) - assert entity is not None, f"unknown key_str: {erk_key_str}" + irk_key_str = data[1:] + entity = p.ds.get_entity_by_key_str(irk_key_str) + assert entity is not None, f"unknown key_str: {irk_key_str}" return entity else: return data diff --git a/src/pyerk/ruleengine.py b/src/pyirk/ruleengine.py similarity index 97% rename from src/pyerk/ruleengine.py rename to src/pyirk/ruleengine.py index e9ed8a4..5ef534d 100644 --- a/src/pyerk/ruleengine.py +++ b/src/pyirk/ruleengine.py @@ -33,9 +33,9 @@ # todo: replace bi. with p. etc from . import builtin_entities as bi from . import settings -import pyerk as p +import pyirk as p -LITERAL_BASE_URI = "erk:/tmp/literals" +LITERAL_BASE_URI = "irk:/tmp/literals" VERBOSITY = False @@ -259,7 +259,7 @@ def apply(self) -> core.RuleResult: def _apply(self) -> core.RuleResult: """ - Perform the actual application of the rule (either via aubgraph monomorphism or via SPARQL query) + Perform the actual application of the rule (either via subgraph monomorphism or via SPARQL query) """ # TODO: remove this when implementing the AlgorithmicRuleApplicationWorker @@ -335,7 +335,7 @@ def get_all_node_relations(self) -> dict: res = defaultdict(list) # core.ds.statements - # {'erk:/builtins#R1': {'erk:/builtins#R1': [S(...), ...], ...}, ..., 'erk:/builtins#I1': {...}} + # {'irk:/builtins#R1': {'irk:/builtins#R1': [S(...), ...], ...}, ..., 'irk:/builtins#I1': {...}} for subj_uri, stm_dict in core.ds.statements.items(): entity = core.ds.get_entity_by_uri(subj_uri, strict=False) if not isinstance(entity, core.Entity): @@ -368,7 +368,7 @@ def get_all_node_relations(self) -> dict: def _make_literal(self, value) -> str: """ - create (if neccessary) and return an uri for an literal value + create (if necessary) and return an uri for an literal value """ if uri := self.literals.b.get(value): @@ -468,7 +468,7 @@ def repl(m): print(re.sub(r"(?m)^", repl, qsrc)) raise - res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyerk, res) + res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyirk, res) result_maps = [] for row in res2: @@ -509,8 +509,8 @@ def _process_result_map(self, result_maps) -> core.RuleResult: # res_dict represents one situation where the assertions should be applied # it's a dict {: , ...} like # { - # 0: , - # 1: , + # 0: , + # 1: , # 2: # } @@ -564,7 +564,7 @@ def _process_result_map(self, result_maps) -> core.RuleResult: else: asserted_new_items.append(None) - # some of the functions might have returned None (called becaus of their side effects) + # some of the functions might have returned None (called because of their side effects) # these pairs are sorted out below (via continue) # augment the dict with entries like {"fiat0": } @@ -598,7 +598,7 @@ def _process_result_map(self, result_maps) -> core.RuleResult: # rules should not affect items inside scopes (maybe this will be more precise in the future) continue - # check if relation already exists and should be ommitted + # check if relation already exists and should be omitted if cntnr.omit_if_existing: if new_obj in new_subj.get_relations(rel.uri, return_obj=True): continue @@ -720,7 +720,7 @@ def prepare_consequent_functions(self) -> (List[callable], List[Tuple[int]], Lis raise core.aux.SemanticRuleError(msg) func_list.append(fiat_factory) - # now pepare the arguments + # now prepare the arguments arg_nodes = [] for arg in call_args: if isinstance(arg, p.allowed_literal_types): @@ -832,8 +832,8 @@ def match_subgraph_P(self) -> List[dict]: res.append(r) if i >= self.max_subgraph_monomorphisms: break - # res is a list of dicts like:[{'erk:/test/zebra02#Ia1158': 0, 'erk:/tmp/literals#0': 1}, ...] - # for some reason the order of that list is not stable accross multiple runs + # res is a list of dicts like:[{'irk:/test/zebra02#Ia1158': 0, 'irk:/tmp/literals#0': 1}, ...] + # for some reason the order of that list is not stable across multiple runs # ensure stable order for stable test results; for comparing dicts they are converted to json-strings res.sort(key=json.dumps) @@ -846,8 +846,8 @@ def match_subgraph_P(self) -> List[dict]: # new_res is a list of dicts like # [{ - # 0: , - # 1: , + # 0: , + # 1: , # 2: # }, ... ] @@ -882,7 +882,7 @@ def _node_matcher(self, n1d: dict, n2d: dict) -> bool: :return: boolean matching result a pair of nodes should match if - - n2 is an external entitiy for self and the uris match + - n2 is an external entity for self and the uris match - n2 is not an external entity (no further restrictions) see also: function edge_matcher @@ -1063,7 +1063,7 @@ def _create_psg_edges(self) -> None: self.ensure_node_of_P(n2) elif isinstance(obj, core.allowed_literal_types): if subjectivized_predicate: - # Note: if subjectivized_predicate the literal should occur in the prototype graphe + # Note: if subjectivized_predicate the literal should occur in the prototype graph pass else: # normally handle the literal -> create a wrapper node @@ -1178,15 +1178,15 @@ def unlink_unwanted_statements(self, stm_list: List[core.Statement]): def edge_matcher(e1d: AtlasView, e2d: AtlasView) -> bool: """ - :param e1d: attribute data of edgees from "main graph" (see RuleApplicator) - :param e2d: attribute data of edgees from "prototype graph" (see RuleApplicator) + :param e1d: attribute data of edges from "main graph" (see RuleApplicator) + :param e2d: attribute data of edges from "prototype graph" (see RuleApplicator) because we compare MultiDigraphs we get `AtlasView`-instances, i.e. read-only dicts like AtlasView({0: inner_dict0, 1: inner_dict1, ...}). Keys are edge-indices for that 'multi-edge', values are like inner_dict0 = { 'itm1': , 'itm2': , - 'rel_uri': 'erk:/builtins#R8', + 'rel_uri': 'irk:/builtins#R8', 'rel_entity': } @@ -1301,7 +1301,7 @@ def get_explanation(self, bindinfo: List[Tuple[p.Entity]]): The bindinfo contains pairs of (, ) tuples. This function uses this information together with the rule-specific explanation_text_template (like "{p1} {rel1} {p2}") to create strings like: - "person1 has_neighbour person2". + "person1 has_neighbor person2". """ if self.explanation_text_template: format_kwargs = {} @@ -1416,7 +1416,7 @@ def crpr(obj): jinja_FILTERS["crpr"] = crpr -# Note this function will be called very often -> check for speedup possibilites +# Note this function will be called very often -> check for speedup possibilities def compare_relation_statements(rel1: core.Relation, stm_list: List[core.Statement], stm_data: Container = None): """ decide whether a given relation fulfills all given statements @@ -1469,7 +1469,7 @@ def is_node_for_simple_graph(entity: core.Entity) -> bool: obj = r20_rels[0].relation_tuple[-1] assert obj.R4__is_instance_of == bi.I16["scope"] - # TODO: maybe add some exceptions (allowed scopes for inferrencing) here + # TODO: maybe add some exceptions (allowed scopes for inferencing) here return False @@ -1568,7 +1568,7 @@ def hardcoded_I830(zb, consequent_function: callable, *args): final_result = p.core.RuleResult() for result_args in result_list: - assert len(args) == 1 # this is like ("{} has too many `R50__is_differnt_from` statements",) + assert len(args) == 1 # this is like ("{} has too many `R50__is_different_from` statements",) tmp_res = consequent_function(None, *args, result_args[0]) # TODO: add result.extend_with_binding_info(cfr, res_dict), see above @@ -1584,7 +1584,7 @@ def hardcoded_I840(zb, consequent_function: callable, *args): h_list = p.get_instances_of(zb.I7435["human"], filter=p.is_relevant_item) rel_list = p.ds.get_subjects_for_relation(zb.R2850["is functional activity"].uri, filter=True) - # filter out the two person-person-activities (TODO: test if this is neccessary) + # filter out the two person-person-activities (TODO: test if this is necessary) # otherwise we would have 7 statements per person rel_list = [ r @@ -1651,8 +1651,9 @@ def get_single_predicate_report(self, pred): def get_predicates_report(self, predicate_list): """ - Gather data for each relevant predicate how many possibilities of subject-object-pairs exisit, which do + Gather data for each relevant predicate how many possibilities of subject-object-pairs exist, which do not contradict an `oppo_pred`-statement, where `oppo_pred` is 'R43__is_opposite_of' the considered predicate. + ("oppo" means opposite) """ pred_report = Container() @@ -1698,11 +1699,11 @@ class HypothesisReasoner: def __init__(self, zb, base_uri): self.zb = zb # the base module (currently zebra-puzzle base data) self.base_uri = base_uri - self.contex_uri = f"{base_uri}/{self.uri_suffix}" + self.context_uri = f"{base_uri}/{self.uri_suffix}" def register_module(self): keymanager = p.KeyManager() - p.register_mod(self.contex_uri, keymanager, check_uri=False) + p.register_mod(self.context_uri, keymanager, check_uri=False) def hypothesis_reasoning_step(self, rule_list): # generate hypothesis @@ -1733,7 +1734,7 @@ def hypothesis_reasoning_step(self, rule_list): for subj, pred, obj in result.stm_triples[1:]: # test the consequences of an hypothesis inside an isolated module (which can be deleted if it failed) self.register_module() - with p.uri_context(uri=self.contex_uri): + with p.uri_context(uri=self.context_uri): stm = subj.set_relation(pred, obj) k = 0 if VERBOSITY: @@ -1753,4 +1754,4 @@ def hypothesis_reasoning_step(self, rule_list): print(p.aux.byellow("This hypothesis led to a contradiction:"), stm) # delete all statements from this context - p.unload_mod(self.contex_uri) + p.unload_mod(self.context_uri) diff --git a/src/pyerk/script.py b/src/pyirk/script.py similarity index 63% rename from src/pyerk/script.py rename to src/pyirk/script.py index b5a0bd7..3b204c3 100644 --- a/src/pyerk/script.py +++ b/src/pyirk/script.py @@ -1,10 +1,13 @@ """ -Command line interface for erk package +Command line interface for irk package """ import os import argparse from pathlib import Path import re +from typing import Tuple +import ast +import inspect try: # this will be part of standard library for python >= 3.11 @@ -13,7 +16,7 @@ import tomli as tomllib -from . import core, erkloader, rdfstack +from . import core, irkloader, rdfstack from . import visualization from . import reportgenerator from . import auxiliary as aux @@ -31,7 +34,7 @@ def create_parser(): generate the cli docs. """ - parser = argparse.ArgumentParser(description="command line interface to ERK (emergent representation of knowledge)") + parser = argparse.ArgumentParser(description="command line interface to IRK (imperative representation of knowledge)") parser.add_argument( "inputfile", help="input file", @@ -60,13 +63,13 @@ def create_parser(): parser.add_argument( "-lp", "--load-package", - help="load erk package (represented by erkpackage.tomle file)", + help="load irk package (represented by irkpackage.tomle file)", default=None, metavar=("PACKAGE_TOML_PATH"), ) - # background: in earlier versions default erk-module paths were specified wrt the path of the - # pyerk.core python module (and thus not wrt the current working dir). + # background: in earlier versions default irk-module paths were specified wrt the path of the + # pyirk.core python module (and thus not wrt the current working dir). # This flag served to switch to "real" paths (interpreted wrt the current working directory) # This behavior is now deprecated parser.add_argument( @@ -137,6 +140,13 @@ def create_parser(): metavar="path_to_mod" ) + parser.add_argument( + "-utd", + "--update-test-data", + help="create a subset of the irkpackage (e.g. OCSE) and store it in the `test_data` dir of pyirk-core", + metavar="path_to_irk_package" + ) + parser.add_argument("--dbg", help="start debug routine", default=None, action="store_true") parser.add_argument( @@ -181,8 +191,8 @@ def main(): exit() # typical calls to generate new keys: - # pyerk --new-keys 30 --load-mod ../knowledge-base/rules/rules1.py rl - # short version: pyerk -nk 100 -l rules1.py rl + # pyirk --new-keys 30 --load-mod ../knowledge-base/rules/rules1.py rl + # short version: pyirk -nk 100 -l rules1.py rl if args.new_keys: if not args.load_mod: print(aux.byellow("No module loaded. Nothing to do.")) @@ -199,54 +209,57 @@ def main(): return if not aux.ensure_valid_uri(key, strict=False): - uri = aux.make_uri(settings.BUILTINS_URI, key) + entity = core.ds.get_entity_by_key_str(key) + uri = entity.uri else: uri = key aux.ensure_valid_uri(uri) visualization.visualize_entity(uri, write_tmp_files=True) elif args.start_django: try: - import pyerkdjango.core + import pyirkdjango.core except ImportError: - print(aux.bred("Error:"), "the module pyerkdjango seems not to be installed.") + print(aux.bred("Error:"), "the module pyirkdjango seems not to be installed.") # exit(10) raise - pyerkdjango.core.start_django() + pyirkdjango.core.start_django() elif args.start_django_shell: try: - import pyerkdjango.core + import pyirkdjango.core except ImportError: - print(aux.bred("Error:"), "the module pyerkdjango seems not to be installed.") + print(aux.bred("Error:"), "the module pyirkdjango seems not to be installed.") # exit(10) raise - pyerkdjango.core.start_django_shell() + pyirkdjango.core.start_django_shell() elif args.insert_keys_for_placeholders: insert_keys_for_placeholders(args.insert_keys_for_placeholders) + elif args.update_test_data: + update_test_data(args.update_test_data) else: print("nothing to do, see option `--help` for more info") -def process_package(pkg_path: str) -> erkloader.ModuleType: +def process_package(pkg_path: str) -> Tuple[irkloader.ModuleType, str]: if os.path.isdir(pkg_path): - pkg_path = os.path.join(pkg_path, "erkpackage.toml") + pkg_path = os.path.join(pkg_path, "irkpackage.toml") with open(pkg_path, "rb") as fp: - erk_conf_dict = tomllib.load(fp) - ocse_main_rel_path = erk_conf_dict["main_module"] - main_module_prefix = erk_conf_dict["main_module_prefix"] - ocse_main_mod_path = Path(pkg_path).parent.joinpath(ocse_main_rel_path).as_posix() + irk_conf_dict = tomllib.load(fp) + main_rel_path = irk_conf_dict["main_module"] + main_module_prefix = irk_conf_dict["main_module_prefix"] + main_mod_path = Path(pkg_path).parent.joinpath(main_rel_path).as_posix() - mod = erkloader.load_mod_from_path(modpath=ocse_main_mod_path, prefix=main_module_prefix) + mod = irkloader.load_mod_from_path(modpath=main_mod_path, prefix=main_module_prefix) return mod, main_module_prefix -def process_mod(path: str, prefix: str, relative_to_workdir: bool = False) -> erkloader.ModuleType: +def process_mod(path: str, prefix: str, relative_to_workdir: bool = False) -> irkloader.ModuleType: if not relative_to_workdir: - msg = "using mod paths which are not relative to workdir is deprecated since pyerk version 0.6.0" + msg = "using mod paths which are not relative to workdir is deprecated since pyirk version 0.6.0" raise DeprecationWarning(msg) smart_relative = None - mod1 = erkloader.load_mod_from_path(path, prefix=prefix, smart_relative=smart_relative) + mod1 = irkloader.load_mod_from_path(path, prefix=prefix, smart_relative=smart_relative) # perform sanity check # rdfstack.check_all_relation_types() @@ -259,21 +272,21 @@ def debug(): To interactively examine modules (builtin and others) use `--interactive-session` """ - ERK_ROOT_DIR = aux.get_erk_root_dir() - TEST_DATA_PATH = os.path.join(ERK_ROOT_DIR, "erk-data", "ocse", "control_theory1.py") - mod1 = erkloader.load_mod_from_path(TEST_DATA_PATH, prefix="ct") # noqa + IRK_ROOT_DIR = aux.get_irk_root_dir() + TEST_DATA_PATH = os.path.join(IRK_ROOT_DIR, "irk-data", "ocse", "control_theory1.py") + mod1 = irkloader.load_mod_from_path(TEST_DATA_PATH, prefix="ct") # noqa ds = core.ds ds.rdfgraph = rdfstack.create_rdf_triples() qsrc = rdfstack.get_sparql_example_query2() res = ds.rdfgraph.query(qsrc) - z = aux.apply_func_to_table_cells(rdfstack.convert_from_rdf_to_pyerk, res) # noqa + z = aux.apply_func_to_table_cells(rdfstack.convert_from_rdf_to_pyirk, res) # noqa IPS() def create_auto_complete_file(): lines = [] - default_pkg_fname = "erkpackage.toml" + default_pkg_fname = "irkpackage.toml" if len(core.ds.uri_mod_dict) == 0: if os.path.exists(default_pkg_fname): print(f"Loading {default_pkg_fname}") @@ -397,14 +410,135 @@ def replace_dummy_enties_by_label(modpath): fp.write(txt) +def update_test_data(pkg_path): + """ + Background: see devdocs + """ + import glob + mod, prefix = process_package(pkg_path) + mod_cont = path_to_ast_container(inspect.getfile(mod)) + + test_data_root = core.aux.get_irk_path("pyirk-core-test_data") + target_dir = os.path.join(test_data_root, "ocse_subset") + template_dir = os.path.join(target_dir, "templates") + + template_files = glob.glob(os.path.join(template_dir, "*__template.py")) + for template_path in template_files: + rendered_template_txt = process_template(template_path) + fname = os.path.split(template_path)[-1].replace("__template", "") + target_path = os.path.join(target_dir, fname) + with open(target_path, "w") as fp: + fp.write(rendered_template_txt) + print(f"File written: {target_path}") + + +def process_template(template_path): + + templ_ast_cont = path_to_ast_container(template_path) + + # extract the uri-line + uri_line = templ_ast_cont.line_data["__URI__"] + tmp_locals = {} + exec(uri_line, {}, tmp_locals) + uri = tmp_locals["__URI__"] + + original_mod_path = inspect.getfile(core.ds.uri_mod_dict[uri]) + + mod_ast_cont = path_to_ast_container(original_mod_path) + + insert_key_lines = templ_ast_cont.line_data["insert_entities"].strip().split("\n") + assert insert_key_lines[0].strip() == "insert_entities = [" + assert insert_key_lines[-1].strip() == "]" + + insert_key_lines = insert_key_lines[1:-1] + + lines_to_insert = [] + + for line in insert_key_lines: + line = line.strip().strip(",") + if not line: + continue + elif line.startswith("#"): + continue + elif line.startswith("raw__"): + # handle raw lines + lines_to_insert.append(line[len("raw__"):]) + lines_to_insert.append("\n"*3) + continue + elif line.startswith("with__"): + # handle context managers + short_key = line + elif line.startswith("def__"): + short_key = line[len("def__"):] + elif line.startswith("class__"): + short_key = line[len("class__"):] + else: + # assume pyirk entity + short_key = core.process_key_str(line, check=False).short_key + + original_content = mod_ast_cont.line_data[short_key] + if not isinstance(original_content, str) or original_content == "": + short_template_path, fname = os.path.split(template_path) + short_template_path = os.path.split(short_template_path)[-1] + short_template_path = os.path.join(short_template_path, fname) + msg = ( + f"could not find associated data for short_key {short_key} while processing " + f"template line `{line}` in template {short_template_path}." + ) + raise KeyError(msg) + lines_to_insert.append(original_content) + lines_to_insert.append("\n") + + new_insert_txt = "".join(lines_to_insert) + + rendered_template = templ_ast_cont.txt.replace(templ_ast_cont.line_data["insert_entities"], new_insert_txt) + return rendered_template + + +def path_to_ast_container(mod_path: str) -> core.aux.Container: + + with open(mod_path) as fp: + lines = fp.readlines() + + txt = "".join(lines) + c = core.aux.Container(ast=ast.parse(txt), lines=lines, line_data={}, txt=txt) + + for elt in c.ast.body: + if isinstance(elt, ast.Assign): + name = elt.targets[0].id + elif isinstance(elt, (ast.FunctionDef, ast.ClassDef)): + name = elt.name + elif isinstance(elt, ast.With): + first_line = lines[elt.lineno-1] + # assume form like `with I9907.scope("setting") as cm:` + idx = first_line.index(" as ") + # create name string like `with__I9907.scope("setting")` + name = f"with__{first_line[len('with '):idx]}" + else: + continue + + assert isinstance(name, str) + + # subtract 1 because the line numberse are human-oriented (1-indexed) + src_txt = "".join(lines[elt.lineno-1:elt.end_lineno]) + c.line_data[name] = src_txt + + return c + + +def get_lines_for_short_key(short_key: str) -> str: + pass + + + def interactive_session(loaded_mod, prefix): """ Start an interactive IPython session where the (optinally) loaded mod is available under its prefix name. - Also: perepare interactive pyerk-module -- a namespacew for experimentally creating entities. + Also: perepare interactive pyirk-module -- a namespacew for experimentally creating entities. """ - import pyerk as p # noqa + import pyirk as p # noqa - __URI__ = "erk:/_interactive" + __URI__ = "irk:/_interactive" keymanager = p.KeyManager() p.register_mod(__URI__, keymanager, check_uri=False) diff --git a/src/pyerk/settings.py b/src/pyirk/settings.py similarity index 74% rename from src/pyerk/settings.py rename to src/pyirk/settings.py index 6e405ce..93ecf3a 100644 --- a/src/pyerk/settings.py +++ b/src/pyirk/settings.py @@ -1,4 +1,4 @@ -# This is the settings module of pyerk (backend). It is assumed to take precedence over django settings. +# This is the settings module of pyirk (backend). It is assumed to take precedence over django settings. import os import sys @@ -10,7 +10,7 @@ except ModuleNotFoundError: import tomli as tomllib -logger = logging.getLogger("pyerk") +logger = logging.getLogger("pyirk") # for now we only support a subset of languages with wich the authors are familiar # if you miss a language, please consider contributing @@ -30,24 +30,24 @@ source_dir = os.path.dirname(os.path.abspath(sys.modules.get(__name__).__file__)) TEMPLATE_PATH = os.path.join(source_dir, "templates") -BUILTINS_URI = "erk:/builtins" +BUILTINS_URI = "irk:/builtins" URI_SEP = "#" -# todo: some time in the future pyerk should become indendent from the OCSE +# todo: some time in the future pyirk should become indendent from the OCSE # for now it is convenient to have the URI stored here -OCSE_URI = "erk:/ocse/0.2" +OCSE_URI = "irk:/ocse/0.2" -# this is relevant to look for pyerk-data to load (specified by a configuration file) -BASE_DIR = os.getenv("PYERK_BASE_DIR", "") +# this is relevant to look for pyirk-data to load (specified by a configuration file) +BASE_DIR = os.getenv("PYIRK_BASE_DIR", "") if not BASE_DIR: BASE_DIR = "./" BASE_DIR = os.path.abspath(BASE_DIR) -confpath = os.getenv("PYERK_CONF_PATH", "") +confpath = os.getenv("PYIRK_CONF_PATH", "") if not confpath: - confpath = os.path.join(BASE_DIR, "erkpackage.toml") + confpath = os.path.join(BASE_DIR, "irkpackage.toml") try: with open(confpath, "rb") as fp: diff --git a/src/pyerk/templates/base.css b/src/pyirk/templates/base.css similarity index 100% rename from src/pyerk/templates/base.css rename to src/pyirk/templates/base.css diff --git a/src/pyerk/templates/l1_rule-application-report-stm.html b/src/pyirk/templates/l1_rule-application-report-stm.html similarity index 100% rename from src/pyerk/templates/l1_rule-application-report-stm.html rename to src/pyirk/templates/l1_rule-application-report-stm.html diff --git a/src/pyerk/templates/report-template.tex b/src/pyirk/templates/report-template.tex similarity index 100% rename from src/pyerk/templates/report-template.tex rename to src/pyirk/templates/report-template.tex diff --git a/src/pyerk/templates/rule-application-report-page.html b/src/pyirk/templates/rule-application-report-page.html similarity index 96% rename from src/pyerk/templates/rule-application-report-page.html rename to src/pyirk/templates/rule-application-report-page.html index b32731a..0a48e8f 100644 --- a/src/pyerk/templates/rule-application-report-page.html +++ b/src/pyirk/templates/rule-application-report-page.html @@ -46,7 +46,7 @@
-

ERK: Emergent Representation of Knowledge

+

IRK: Imperative Representation of Knowledge

diff --git a/src/pyerk/visualization.py b/src/pyirk/visualization.py similarity index 89% rename from src/pyerk/visualization.py rename to src/pyirk/visualization.py index 71526c2..3b96dd4 100644 --- a/src/pyerk/visualization.py +++ b/src/pyirk/visualization.py @@ -1,24 +1,23 @@ """ -This module contains code for the visualization of ERK-entities. +This module contains code for the visualization of IRK-entities. """ from typing import Union, List, Tuple, Optional import os import urllib +from rdflib import Literal import networkx as nx import nxv # for graphviz visualization of networkx graphs # TODO: this should be a relative import of the *package* -import pyerk as p +import pyirk as p from ipydex import IPS, activate_ips_on_exception activate_ips_on_exception() # TODO: make this a dict to speedup lookup # tuple of Relation keys which are not displayed by default -REL_BLACKLIST = ("erk:/builtins#R1", "erk:/builtins#R2") - -# from semantictools import core as smt +REL_BLACKLIST = ("irk:/builtins#R1", "irk:/builtins#R2") from abc import ABC @@ -72,14 +71,14 @@ def _perform_label_segmentation(self) -> None: """ handle label formatting (segmentation into multiple lines and later wrapping by html tags) - labels of Nodes should be centered (dot file should contain r"\n" bewteen segments) - hower neither using "\n" nor r"\n" inside the nxv-node-labels leads to the desired results + labels of Nodes should be centered (dot file should contain r"\n" between segments) + however neither using "\n" nor r"\n" inside the nxv-node-labels leads to the desired results thus: use a dummy which will be replaced later """ # TODO: replace this by prefixed short_key - unformated_repr_str = f'{self.short_key}["{self.label}"]' - self.label_segment_keys, self.label_segments = create_label_segments(unformated_repr_str, maxlen=self.maxlen) + unformatted_repr_str = f'{self.short_key}["{self.label}"]' + self.label_segment_keys, self.label_segments = create_label_segments(unformatted_repr_str, maxlen=self.maxlen) self.label_segment_items = zip(self.label_segment_keys, self.label_segments) # wrap the each key with curly braces to allow application of .format(...) later] @@ -295,7 +294,7 @@ def create_label_segments(label: str, maxlen: int) -> Tuple[List[str], List[str] first_part_split_index = maxlen - i - # rstrip to eliminate trainling spaces but not dashes etc + # rstrip to eliminate trailing spaces but not dashes etc new_line = first_part[:first_part_split_index].rstrip() res_segments.append(new_line) rest = rest[first_part_split_index:] @@ -392,7 +391,11 @@ def create_complete_graph( i = 0 relation_dict: dict for item_uri, relation_dict in p.ds.statements.items(): - item = p.ds.get_entity_by_uri(item_uri) + item = p.ds.get_entity_by_uri(item_uri, strict=None) + if item is None: + # this is the case for some statements which are subject of a qualifier relation + assert item_uri in p.ds.statement_uri_map + continue if not isinstance(item, p.Item) or item.short_key in ["I000"]: continue # count only items @@ -404,7 +407,8 @@ def create_complete_graph( pass else: node = create_node(item, url_template) - G.add_node(node, label=item.short_key, color=get_color_for_item(item)) + label_str = f"{item.short_key}\n{item.R1__has_label}" + G.add_node(node, label=label_str, color=get_color_for_item(item)) added_items_nodes[item_uri] = node # iterate over relation edges @@ -475,10 +479,28 @@ def render_graph_to_dot(G: nx.DiGraph) -> str: return dot_data +def svg_replace(raw_svg_data: str, REPLACEMENTS: dict) -> str: + assert isinstance(raw_svg_data, str) + + # prevent some latex stuff to interfere with the handing of the `REPLACEMENTS` + # TODO: handle the whole problme more elegantly + + latex_replacements = [(r"\dot{x}", "__LATEX1__")] + for orig, subs in latex_replacements: + raw_svg_data = raw_svg_data.replace(orig, subs) + + svg_data1: str = raw_svg_data.format(**REPLACEMENTS) + + for orig, subs in latex_replacements: + svg_data1 = svg_data1.replace(subs, orig) + + return svg_data1 + + def visualize_entity(uri: str, url_template="", write_tmp_files: bool = False) -> str: """ - :param uri: entity uri (like "erk:/my/module#I0123") + :param uri: entity uri (like "irk:/my/module#I0123") :param url_template: url template for creation of a-tags (html links) for the labels :param write_tmp_files: boolean flag whether to write debug output @@ -492,7 +514,7 @@ def visualize_entity(uri: str, url_template="", write_tmp_files: bool = False) - for old, new in NEWLINE_REPLACEMENTS: dot_data0 = dot_data0.replace(old, new) - # work arround curly braces in first and last line + # work around curly braces in first and last line dot_lines = dot_data0.split("\n") inner_dot_code = "\n".join(dot_lines[1:-1]) @@ -500,8 +522,8 @@ def visualize_entity(uri: str, url_template="", write_tmp_files: bool = False) - # noinspection PyUnresolvedReferences,PyProtectedMember raw_svg_data = nxv._graphviz.run(dot_data, algorithm="dot", format="svg", graphviz_bin=None) - - svg_data1: str = raw_svg_data.decode("utf8").format(**REPLACEMENTS) + raw_svg_data = raw_svg_data.decode("utf8") + svg_data1 = svg_replace(raw_svg_data, REPLACEMENTS) if write_tmp_files: # for debugging @@ -519,6 +541,12 @@ def visualize_entity(uri: str, url_template="", write_tmp_files: bool = False) - return svg_data1 +def get_label(entity): + res = entity.get("label", "undefined label") + if isinstance(res, Literal): + return res.value + return res + def visualize_all_entities(url_template="", write_tmp_files: bool = False) -> str: G = create_complete_graph(url_template) @@ -540,7 +568,7 @@ def visualize_all_entities(url_template="", write_tmp_files: bool = False) -> st "width": 1.3, "fontsize": 10, "color": d.get("color", "black"), - "label": d.get("label", "undefined label"), + "label": get_label(d), "shape": d.get("shape", "circle"), # see also AbstractNode.shape }, # u: node1, v: node1, d: its attribute dict @@ -560,7 +588,7 @@ def visualize_all_entities(url_template="", write_tmp_files: bool = False) -> st "color": d.get("color", "black"), "width": 0.3, "fontsize": 2, - "label": d.get("label", "undefined label"), + "label": get_label(d), "fillcolor": "#45454533", }, edge=lambda u, v, d: { @@ -579,12 +607,12 @@ def visualize_all_entities(url_template="", write_tmp_files: bool = False) -> st dot_data = raw_dot_data # noinspection PyUnresolvedReferences,PyProtectedMember raw_svg_data = nxv._graphviz.run(dot_data, algorithm="dot", format="svg", graphviz_bin=None) - svg_data1: str = raw_svg_data.decode("utf8").format(**REPLACEMENTS) + svg_data1 = svg_replace(raw_svg_data.decode("utf8"), REPLACEMENTS) if write_tmp_files: # for debugging - dot_fpath = "./t#mp_dot.txt" + dot_fpath = "./tmp_dot.txt" with open(dot_fpath, "w") as txtfile: txtfile.write(dot_data) print("File written:", os.path.abspath(dot_fpath)) diff --git a/tests/settings.py b/tests/settings.py index b0fcb67..1eabded 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -6,8 +6,8 @@ # noinspection PyUnresolvedReferences from ipydex import IPS, activate_ips_on_exception, set_trace # noqa -import pyerk as p -import pyerk.io +import pyirk as p +import pyirk.io # ensure reproducible results @@ -18,21 +18,16 @@ current_dir = os.path.dirname(os.path.abspath(sys.modules.get(__name__).__file__)) -ERK_ROOT_DIR = p.aux.get_erk_root_dir() +IRK_ROOT_DIR = p.aux.get_irk_root_dir() # path for basic (staged) test data -TEST_DATA_DIR1 = pjoin(ERK_ROOT_DIR, "pyerk-core", "tests", "test_data") +TEST_DATA_DIR1 = pjoin(IRK_ROOT_DIR, "pyirk-core", "tests", "test_data") +TEST_DATA_DIR_OCSE = pjoin(TEST_DATA_DIR1, "ocse_subset") -# path for "realistic" test data -# taking this from envvar allows to flexibly use other test-data during debugging -TEST_DATA_PARENT_PATH = os.getenv("PYERK_TEST_DATA_PARENT_PATH", default=pjoin(ERK_ROOT_DIR, "erk-data-for-unittests")) - - -TEST_DATA_REPO_PATH = pjoin(TEST_DATA_PARENT_PATH, "ocse") -TEST_DATA_PATH2 = pjoin(TEST_DATA_REPO_PATH, "control_theory1.py") -TEST_DATA_PATH_MA = pjoin(TEST_DATA_REPO_PATH, "math1.py") -TEST_DATA_PATH3 = pjoin(TEST_DATA_REPO_PATH, "agents1.py") +TEST_DATA_PATH2 = pjoin(TEST_DATA_DIR1, "ocse_subset", "control_theory1.py") +TEST_DATA_PATH_MA = pjoin(TEST_DATA_DIR1, "ocse_subset", "math1.py") +TEST_DATA_PATH3 = pjoin(TEST_DATA_DIR1, "ocse_subset", "agents1.py") TEST_DATA_PATH_ZEBRA01 = pjoin(TEST_DATA_DIR1, "zebra01.py") TEST_DATA_PATH_ZEBRA02 = pjoin(TEST_DATA_DIR1, "zebra02.py") TEST_DATA_PATH_ZEBRA_BASE_DATA = pjoin(TEST_DATA_DIR1, "zebra_base_data.py") @@ -41,19 +36,19 @@ # useful to get the currently latest sha strings: # git log --pretty=oneline | head -TEST_DATA_REPO_COMMIT_SHA = "8898efd919cfefa959eca59e19dd3888cb1ea9de" # (2023-11-05 15:29:28) +TEST_DATA_REPO_COMMIT_SHA = "11b9f9fe14cef7248fd0d9f31c7412516aa92aa9" # (2023-11-07 16:20:00) # TODO: make this more robust (e.g. search for config file or environment variable) # TODO: put link to docs here (directory layout) -TEST_ACKREP_DATA_FOR_UT_PATH = pjoin(ERK_ROOT_DIR, "..", "ackrep", "ackrep_data_for_unittests") +TEST_ACKREP_DATA_FOR_UT_PATH = pjoin(IRK_ROOT_DIR, "..", "ackrep", "ackrep_data_for_unittests") os.environ["UNITTEST"] = "True" # UNLOAD_MODS is True by default but could be set to False via env var. -# This is sometimes useful to prevent the deletion of entites by tear_down() -UNLOAD_MODS = not (os.getenv("PYERK_NOT_UNLOAD_MODS") == "True") +# This is sometimes useful to prevent the deletion of entities by tear_down() +UNLOAD_MODS = not (os.getenv("PYIRK_NOT_UNLOAD_MODS") == "True") -__URI__ = TEST_BASE_URI = "erk:/local/unittest" +__URI__ = TEST_BASE_URI = "irk:/local/unittest" # this serves to print the test-method-name before it is executed (useful for debugging, see setUP below) @@ -63,7 +58,7 @@ WRITE_TMP_FILES = False -class HouskeeperMixin: +class HousekeeperMixin: """ Class to provide common functions for all our TestCase subclasses """ diff --git a/tests/test_consistency_check.py b/tests/test_consistency_check.py index 6e00d1a..b0d56ed 100644 --- a/tests/test_consistency_check.py +++ b/tests/test_consistency_check.py @@ -8,19 +8,19 @@ # noinspection PyUnresolvedReferences from ipydex import IPS, activate_ips_on_exception, set_trace # noqa -import pyerk as p +import pyirk as p from .settings import ( TEST_BASE_URI, - HouskeeperMixin, + HousekeeperMixin, TEST_DATA_PATH2, ) # noinspection PyPep8Naming -class Test_01_CC(HouskeeperMixin, unittest.TestCase): +class Test_01_CC(HousekeeperMixin, unittest.TestCase): def create_operators(self): @@ -80,7 +80,7 @@ def test_a01__cc_basics(self): def test_a02__cc_enable_checking(self): I0111 = self.create_operators() - p.cc.enable_consitency_checking() + p.cc.enable_consistency_checking() with p.uri_context(uri=TEST_BASE_URI): real_number = p.instance_of(p.I35["real number"]) @@ -162,9 +162,9 @@ def raise_error_for_item(_anchor_item: p.Item, rule: p.Item, arg: p.Item): :param _anchor_item: auxiliary graph node to which the condition function is attached it is passed automatically (not needed here) :param rule: the rule which has this function as a consequent_func - :param arg: the item which triggerred the rule + :param arg: the item which triggered the rule """ - raise p.cc.ErkConsistencyError(f"Rule {rule} failed for arg {arg}.") + raise p.cc.IrkConsistencyError(f"Rule {rule} failed for arg {arg}.") with I502.scope("assertion") as cm: cm.new_consequent_func(raise_error_for_item, cm.rule, cm.x, anchor_item=None) @@ -223,7 +223,7 @@ def create_constraint_violation_item(anchor_item, main_arg, rule): @unittest.expectedFailure def test_b01__cc_constraint_violation_rules(self): - ct = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + ct = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") I501 = self._define_tst_rules(ct) with p.uri_context(uri=TEST_BASE_URI): @@ -239,7 +239,7 @@ def test_b01__cc_constraint_violation_rules(self): def test_c01__cc_matrix_dimensions(self): - ct = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + ct = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") c = self._define_tst_rules(ct) I501, I502, I503 = c.I501, c.I502, c.I503 with p.uri_context(uri=TEST_BASE_URI): @@ -274,7 +274,7 @@ def test_c01__cc_matrix_dimensions(self): # # test the rule which raises an exception - with self.assertRaises(p.cc.ErkConsistencyError): + with self.assertRaises(p.cc.IrkConsistencyError): p.ruleengine.apply_semantic_rule(I502["raise exception on invalid mat mul dimensions"], TEST_BASE_URI) # @@ -288,3 +288,30 @@ def test_c01__cc_matrix_dimensions(self): self.assertTrue(p.is_instance_of, p.I48["constraint violation"]) self.assertEqual(A2B.R74__has_constraint_violation, []) + + def test_c02__cc_multi_valued_domain(self): + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct").ma + with p.uri_context(uri=TEST_BASE_URI): + I1000 = p.create_item( + R1__has_label="negation", + R2__has_description="negation operator", + R4__is_instance_of=ma.I4895["mathematical operator"], + R8__has_domain_of_argument_1=(ma.I9904["matrix"], p.I35["real number"]), + R11__has_range_of_result=ma.I9904["matrix"], + ) + + x = p.instance_of(p.I35["real number"]) + r = p.instance_of(p.I36["rational number"]) + i = p.instance_of(p.I37["integer number"]) + c = p.instance_of(p.I34["complex number"]) + M = p.instance_of(ma.I9904["matrix"]) + S = p.instance_of(ma.I9906["square matrix"]) + + p.cc.check(I1000(x)) + p.cc.check(I1000(r)) + p.cc.check(I1000(i)) + p.cc.check(I1000(M)) + + with self.assertRaises(p.cc.WrongArgType): + # type error for arg2 + p.cc.check(I1000(c)) diff --git a/tests/test_core.py b/tests/test_core.py index 00dee10..24ebfe0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -9,52 +9,36 @@ # noinspection PyUnresolvedReferences from ipydex import IPS, activate_ips_on_exception, set_trace # noqa -import pyerk as p -import pyerk.visualization as visualization -import pyerk.io -from pyerk.auxiliary import uri_set +import pyirk as p +import pyirk.visualization as visualization +import pyirk.io +from pyirk.auxiliary import uri_set import git -import pyerk.reportgenerator as rgen +import pyirk.reportgenerator as rgen from .settings import ( - ERK_ROOT_DIR, + IRK_ROOT_DIR, TEST_DATA_DIR1, - TEST_DATA_PARENT_PATH, - TEST_DATA_REPO_PATH, TEST_DATA_PATH2, TEST_DATA_PATH_MA, TEST_DATA_PATH3, TEST_DATA_PATH_ZEBRA_BASE_DATA, TEST_DATA_PATH_ZEBRA02, TEST_MOD_NAME, - TEST_DATA_REPO_COMMIT_SHA, # TEST_ACKREP_DATA_FOR_UT_PATH, TEST_BASE_URI, WRITE_TMP_FILES, - HouskeeperMixin, + HousekeeperMixin, ) -class Test_00_Core(HouskeeperMixin, unittest.TestCase): - def test_a0__ensure_expected_test_data(self): - """ - Construct a list of all sha-strings which where commited in the current branch and assert that - the expected string is among them. This heuristics assumes that it is OK if the data-repo is newer than - expected. But the tests fails if it is older (or on a unexpeced branch). - """ - - repo = git.Repo(TEST_DATA_REPO_PATH) - log_list = repo.git.log("--pretty=oneline").split("\n") - msg = f"Unexpected: could not find commit hash {TEST_DATA_REPO_COMMIT_SHA} in repo {TEST_DATA_REPO_PATH}" - sha_list = [line.split(" ")[0] for line in log_list] - - self.assertIn(TEST_DATA_REPO_COMMIT_SHA, sha_list, msg=msg) +class Test_00_Core(HousekeeperMixin, unittest.TestCase): - def test_a1__dependencyies(self): + def test_a1__dependencies(self): # this tests checks some dependencies which are prone to cause problems (e.g. due to recent api-changes) pydantic_version = version.parse(p.pydantic.__version__) @@ -106,7 +90,7 @@ def test_b1__process_key_str(self): with self.assertRaises(p.aux.InvalidGeneralKeyError): res = p.process_key_str("some_prefix__I000__double_label_['redundant']", check=False) - def test_b2__uri_contex_manager(self): + def test_b2__uri_context_manager(self): """ Test defined behavior of errors occur in uri_context :return: @@ -124,10 +108,10 @@ def test_b2__uri_contex_manager(self): L2 = len(p.ds.relations) L3 = len(p.ds.statement_uri_map) try: - _ = p.erkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod0_with_errors.py"), prefix="tm0") + _ = p.irkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod0_with_errors.py"), prefix="tm0") except ValueError: pass - # assert that no enties remain in the data structures + # assert that no entities remain in the data structures self.assertEqual(len(p.ds.entities_created_in_mod), 1) self.assertEqual(L1, len(p.ds.items)) self.assertEqual(L2, len(p.ds.relations)) @@ -164,10 +148,10 @@ def test_b4__uri_attr_of_entities(self): self.assertEqual(rel.uri, f"{TEST_BASE_URI}#{rel.short_key}") def test_c1__load_multiple_modules(self): - mod1 = p.erkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod1.py"), prefix="tm1") + mod1 = p.irkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod1.py"), prefix="tm1") # test recursive module loading - self.assertEqual(mod1.foo_mod.__URI__, "erk:/pyerk/testmodule3") + self.assertEqual(mod1.foo_mod.__URI__, "irk:/pyirk/testmodule3") # test validity of R2000 statements (created in tmod1 with a relation from tmod3) stm1, stm2 = mod1.I1000.get_relations("bar__R2000") @@ -180,36 +164,34 @@ def test_c1__load_multiple_modules(self): def test_c02__exception_handling(self): - os.environ["PYERK_TRIGGER_TEST_EXCEPTION"] = "True" + os.environ["PYIRK_TRIGGER_TEST_EXCEPTION"] = "True" - with self.assertRaises(p.aux.ExcplicitlyTriggeredTestException): - mod1 = p.erkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod1.py"), prefix="tm1") + with self.assertRaises(p.aux.ExplicitlyTriggeredTestException): + mod1 = p.irkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod1.py"), prefix="tm1") # this was a bug: if the module is loaded for the second time exception is not handled correctly - with self.assertRaises(p.aux.ExcplicitlyTriggeredTestException): - mod1 = p.erkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod1.py"), prefix="tm1") + with self.assertRaises(p.aux.ExplicitlyTriggeredTestException): + mod1 = p.irkloader.load_mod_from_path(pjoin(TEST_DATA_DIR1, "tmod1.py"), prefix="tm1") - os.environ.pop("PYERK_TRIGGER_TEST_EXCEPTION") + os.environ.pop("PYIRK_TRIGGER_TEST_EXCEPTION") -# noinspection PyPep8Naming -class Test_01_Core(HouskeeperMixin, unittest.TestCase): +@unittest.skipIf(os.environ.get("CI"), "Skipping directory structure tests on CI") +class Test_01_Core(HousekeeperMixin, unittest.TestCase): def test_a01__directory_structure(self): - pyerk_dir = pjoin(ERK_ROOT_DIR, "pyerk-core") - django_gui_dir = pjoin(ERK_ROOT_DIR, "pyerk-django") + pyirk_dir = pjoin(IRK_ROOT_DIR, "pyirk-core") + django_gui_dir = pjoin(IRK_ROOT_DIR, "pyirk-django") - self.assertTrue(os.path.isdir(pyerk_dir)) - # since there is no reason to have the django gui in this repos CI: - if os.environ.get("CI") != "true": - self.assertTrue(os.path.isdir(django_gui_dir)) - self.assertTrue(os.path.isdir(TEST_DATA_PARENT_PATH)) + self.assertTrue(os.path.isdir(pyirk_dir)) + if not os.path.isdir(django_gui_dir): + print("unexpected: {django_gui_dir} not found") def test_a01__test_independence(self): """ The first test ensures, that TestCases do not influence each other """ - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") self.tearDown() @@ -256,10 +238,10 @@ def test_a03_tear_down(self): self.assertEqual(len(non_builtin_entities), 0) # noinspection PyUnresolvedReferences - # (above noinspection is necessary because of the @-operator which is undecleared for strings) + # (above noinspection is necessary because of the @-operator which is undeclared for strings) def test_b00__core1_basics(self): - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") - self.assertEqual(mod1.I3749.R1.value, "Cayley-Hamilton theorem") + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + self.assertEqual(mod1.ma.I3749.R1.value, "Cayley-Hamilton theorem") def_eq_item = mod1.I6886.R6__has_defining_mathematical_relation self.assertEqual(def_eq_item.R4__is_instance_of, p.I18["mathematical expression"]) @@ -308,7 +290,7 @@ def test_a02__load_settings(self): """ ensure that the default settingsfile is loaded correctly """ - # this is a variable which should be present in every pyerkconf file + # this is a variable which should be present in every pyirkconf file conf = p.settings.CONF # self.assertTrue(len(conf) != 0) @@ -331,7 +313,7 @@ def test_c01__ct_loads_math(self): :return: """ - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") self.assertIn("ma", p.ds.uri_prefix_mapping.b) itm1 = p.ds.get_entity_by_key_str("ma__I5000__scalar_zero") self.assertEqual(itm1, mod1.ma.I5000["scalar zero"]) @@ -347,9 +329,9 @@ def test_c04__evaluated_mapping(self): res = p.ds.statements.get("S6229") self.assertIsNone(res) - ct = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + ct = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") with p.uri_context(uri=TEST_BASE_URI): - poly1 = p.instance_of(ct.I4239["abstract monovariate polynomial"]) + poly1 = p.instance_of(ct.ma.I4239["abstract monovariate polynomial"]) # test that an arbitrary item is *not* callable self.assertRaises(TypeError, ct.ma.I2738["field of complex numbers"], 0) @@ -370,19 +352,19 @@ def test_c04__evaluated_mapping(self): def test_c05__evaluated_mapping2(self): - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") with p.uri_context(uri=TEST_BASE_URI): - h = p.instance_of(mod1.I9923["scalar field"]) - f = p.instance_of(mod1.I9841["vector field"]) - x = p.instance_of(mod1.I1168["point in state space"]) + h = p.instance_of(mod1.ma.I9923["scalar field"]) + f = p.instance_of(mod1.ma.I9841["vector field"]) + x = p.instance_of(mod1.ma.I1168["point in state space"]) Lderiv = mod1.I1347["Lie derivative of scalar field"] # this creates a new item (and thus must be executed with a non-empty uri stack, i.e. within this context) h2 = Lderiv(h, f, x) - self.assertEqual(h2.R4__is_instance_of, mod1.I9923["scalar field"]) + self.assertEqual(h2.R4__is_instance_of, mod1.ma.I9923["scalar field"]) arg_tup = h2.R36__has_argument_tuple self.assertEqual(arg_tup.R4__is_instance_of, p.I33["tuple"]) @@ -408,8 +390,8 @@ def test_c06__tuple(self): def test_c07__scope_vars(self): # this tests for a bug with labels of scope vars - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") - def_itm = p.ds.get_entity_by_key_str("ct__I9907__definition_of_square_matrix") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + def_itm = p.ds.get_entity_by_key_str("ma__I9907__definition_of_square_matrix") matrix_instance = def_itm.M self.assertEqual(matrix_instance.R1.value, "M") @@ -424,7 +406,7 @@ def test_c07b__nested_scopes_of_propositions(self): ) my_set = p.instance_of(p.I13["mathematical set"]) - my_prop = p.instance_of(p.I11["mathematical property"]) + my_prop = p.instance_of(p.I54["mathematical property"]) with I7324["definition of something"].scope("setting") as cm: x = cm.new_var(x=p.instance_of(p.I39["positive integer"])) @@ -525,7 +507,7 @@ def test_c07c__scope_copying(self): """ test to copy statements from one scope to another """ - ct = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + ct = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") with p.uri_context(uri=TEST_BASE_URI): I0111 = p.create_item( R1__has_label = "definition of something", @@ -533,7 +515,7 @@ def test_c07c__scope_copying(self): ) my_set = p.instance_of(p.I13["mathematical set"]) - my_prop = p.instance_of(p.I11["mathematical property"]) + my_prop = p.instance_of(p.I54["mathematical property"]) # create some variables and relations with I0111["definition of something"].scope("setting") as cm: @@ -546,7 +528,7 @@ def test_c07c__scope_copying(self): f = cm.new_var(f=p.instance_of(ct.ma.I9841["vector field"])) LfV = cm.new_var(LfV=ct.I1347["Lie derivative of scalar field"](V, f, x)) - # TODO: this does not occure in I0111_setting at all (!!) + # TODO: this does not occur in I0111_setting at all (!!) with p.ImplicationStatement() as imp1: imp1.antecedent_relation(lhs=x, rsgn="==", rhs=y) imp1.consequent_relation(lhs=y, rsgn=">=", rhs=x) @@ -589,18 +571,18 @@ def test_c08__relations_with_sequence_as_argument(self): with p.uri_context(uri=TEST_BASE_URI): Ia001 = p.create_item(R1__has_label="test item") - # check that assigning sequences is not allowed - with self.assertRaises(TypeError): - Ia001.set_relation(p.R5["is part of"], [p.I4["Mathematics"], p.I5["Engineering"]]) + # check that assigning sequences is not allowed + with self.assertRaises(TypeError): + Ia001.set_relation(p.R5["is part of"], [p.I4["Mathematics"], p.I5["Engineering"]]) with p.uri_context(uri=TEST_BASE_URI): # check that assigning sequences is possible with explicit method. - Ia001.set_mutliple_relations(p.R5["is part of"], [p.I4["Mathematics"], p.I5["Engineering"]]) + Ia001.set_multiple_relations(p.R5["is part of"], [p.I4["Mathematics"], p.I5["Engineering"]]) rel_objs = Ia001.get_relations("R5", return_obj=True) self.assertEqual(rel_objs, [p.I4, p.I5]) - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") itm = p.ds.get_entity_by_key_str("ct__I4466__Systems_Theory") # construction: R5__is_part_of=[p.I4["Mathematics"], p.I5["Engineering"]] res = itm.R5 @@ -609,7 +591,7 @@ def test_c08__relations_with_sequence_as_argument(self): self.assertIn(p.I5["Engineering"], res) def test_c09__is_instance_of_generalized_metaclass(self): - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") itm1 = p.ds.get_entity_by_key_str("I2__Metaclass") itm2 = p.ds.get_entity_by_key_str("I12__mathematical_object") @@ -661,8 +643,8 @@ def test_c09b__is_instance_of(self): def test_c10__qualifiers(self): - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH3, prefix="ag") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH3, prefix="ag") itm1: p.Item = p.ds.get_entity_by_key_str("ag__I2746__Rudolf_Kalman") stm1, stm2 = itm1.get_relations("ag__R1833__has_employer")[:2] @@ -678,13 +660,13 @@ def test_c10__qualifiers(self): self.assertEqual(len(stm2.dual_statement.qualifiers), 1) def test_c11__equation(self): - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") # get item via prefix and key - itm1: p.Item = p.ds.get_entity_by_key_str("ct__I3749__Cayley_Hamilton_theorem") + itm1: p.Item = p.ds.get_entity_by_key_str("ma__I3749__Cayley_Hamilton_theorem") # get item via key and uri - itm2: p.Item = p.ds.get_entity_by_key_str("I3749__Cayley_Hamilton_theorem", mod_uri=mod1.__URI__) + itm2: p.Item = p.ds.get_entity_by_key_str("I3749__Cayley_Hamilton_theorem", mod_uri=mod1.ma.__URI__) self.assertEqual(itm1, itm2) @@ -716,7 +698,7 @@ def test_c11__equation(self): def test_c12__process_key_str(self): - # first, check label consistency in builtin_enities + # first, check label consistency in builtin_entities # note these keys do not to exist pkey1 = p.process_key_str("I0008234") @@ -740,11 +722,11 @@ def test_c12__process_key_str(self): self.assertRaises(ValueError, p.process_key_str, "R2__has_description_XYZ") # now, check label consistency in the test data - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) def test_c12a__process_key_str2(self): - ct = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + ct = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") p.ds.get_entity_by_key_str("ct__R7641__has_approximation") == ct.R7641["has approximation"] @@ -756,19 +738,19 @@ def test_c12a__process_key_str2(self): with self.assertRaises(p.aux.ShortKeyNotFoundError): _ = p.create_item(key_str="I0125", R1="some label", R7641__has_approximation=e0) - # second: use prefix to adresse the correct relation + # second: use prefix to address the correct relation e1 = p.create_item(key_str="I0125", R1="some label", ct__R7641__has_approximation=e0) - # third: create a relation which has a short key collission with a relation from the ct module + # third: create a relation which has a short key collision with a relation from the ct module _ = p.create_relation(key_str="R7641", R1="some test relation") e2 = p.create_item( key_str="I0126", R1="some label", ct__R7641__has_approximation=e0, R7641__some_test_relation="foo" ) - # this is the verbose way to adress a builtin relation + # this is the verbose way to address a builtin relation self.assertEqual(e1.bi__R1.value, "some label") - # this is the (necessary) way to adress a relation from an external module + # this is the (necessary) way to address a relation from an external module self.assertEqual(e1.ct__R7641[0], e0) self.assertEqual(e2.ct__R7641[0], e0) @@ -778,10 +760,10 @@ def test_c12a__process_key_str2(self): with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): - # adress the relation with correct prefix + # address the relation with correct prefix self.assertEqual(e2.ut__R7641__some_test_relation[0], "foo") - # adress the relation without prefix (but with activated unittet module) + # address the relation without prefix (but with activated unittest module) self.assertEqual(e2.R7641__some_test_relation[0], "foo") # activate different module and use attribute without prefix @@ -826,10 +808,10 @@ def test_c14__visualization1(self): ) self.assertGreater(res_graph.number_of_nodes(), 6) - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) # do not use something like "Ia3699" here directly because this might change when mod1 changes - auto_item: p.Item = mod1.I3749["Cayley-Hamilton theorem"].A + auto_item: p.Item = mod1.ma.I3749["Cayley-Hamilton theorem"].A res_graph: visualization.nx.DiGraph = visualization.create_nx_graph_from_entity(auto_item.uri) self.assertGreater(res_graph.number_of_nodes(), 7) @@ -839,8 +821,8 @@ def test_c15__visualization2(self): res = visualization.visualize_entity(p.u("I21__mathematical_relation"), write_tmp_files=WRITE_TMP_FILES) - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) - auto_item: p.Item = mod1.I3749["Cayley-Hamilton theorem"].P + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) + auto_item: p.Item = mod1.ma.I3749["Cayley-Hamilton theorem"].P res = visualization.visualize_entity(auto_item.uri, write_tmp_files=WRITE_TMP_FILES) s1 = 'R35' @@ -851,7 +833,7 @@ def test_c15__visualization2(self): self.assertIn(s3, res) def test_d01__wrap_function_with_uri_context(self): - ma = p.erkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): A = p.instance_of(ma.I9906["square matrix"]) @@ -878,7 +860,7 @@ def test_func(): def test_d02__custom_call_post_process1(self): - ma = p.erkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): A = p.instance_of(ma.I9906["square matrix"]) @@ -889,12 +871,13 @@ def test_d02__custom_call_post_process1(self): d = ma.I5359["determinant"](M) self.assertTrue(M.R4__is_instance_of, ma.I1935["polynomial matrix"]) - self.assertTrue(M.ma__R8736__depends_polyonomially_on, s) - self.assertTrue(d.ma__R8736__depends_polyonomially_on, s) + # TODO fix typo in OCSE and regenerate test data + self.assertTrue(M.ma__R8736__depends_polynomially_on, s) + self.assertTrue(d.ma__R8736__depends_polynomially_on, s) def test_d02b__signature_inheritance(self): - ct = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") + ct = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct") with p.uri_context(uri=TEST_BASE_URI): P = p.instance_of(ct.ma.I4240["matrix polynomial"]) self.assertEqual(P.R8__has_domain_of_argument_1, [ct.ma.I9906["square matrix"]]) @@ -902,7 +885,7 @@ def test_d02b__signature_inheritance(self): def test_d03__replace_entity(self): - ma = p.erkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): A = p.instance_of(ma.I9906["square matrix"]) @@ -928,7 +911,7 @@ def test_d03__replace_entity(self): def test_d03b__replace_entity(self): - ma = p.erkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): A = p.instance_of(ma.I9906["square matrix"]) @@ -954,7 +937,7 @@ def test_d04__invalid_prefix(self): n4a = len(sys.modules) with self.assertRaises(p.aux.InvalidPrefixError): - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zb") + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zb") n1b = len(p.ds.mod_path_mapping.a) n2b = len(p.ds.entities_created_in_mod) @@ -1013,7 +996,7 @@ def test_d07__replace_statement(self): itm.overwrite_statement("R4__is_instance_of", p.I2["Metaclass"]) self.assertEqual(itm.R4__is_instance_of, p.I2["Metaclass"]) - def test_d08__unlink_enities(self): + def test_d08__unlink_entities(self): with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): @@ -1114,21 +1097,21 @@ def test_d13__check_type(self): # created with json.dumps and textwrap.fill(json_str, width=100) raw_data = """ - {"erk:/ocse/0.2/zebra_base_data#R8216": [[5, "erk:/ocse/0.2/zebra_base_data#I4037"], [5, - "erk:/ocse/0.2/zebra_base_data#I9848"], [5, "erk:/ocse/0.2/zebra_base_data#I3132"], [5, - "erk:/ocse/0.2/zebra_base_data#I2552"], [5, "erk:/ocse/0.2/zebra_base_data#I5931"]], - "erk:/ocse/0.2/zebra_base_data#R9040": [[5, "erk:/ocse/0.2/zebra_base_data#I4037"], [5, - "erk:/ocse/0.2/zebra_base_data#I9848"], [5, "erk:/ocse/0.2/zebra_base_data#I3132"], [5, - "erk:/ocse/0.2/zebra_base_data#I2552"], [5, "erk:/ocse/0.2/zebra_base_data#I5931"]], - "erk:/ocse/0.2/zebra_base_data#R5611": [[5, "erk:/ocse/0.2/zebra_base_data#I4037"], [5, - "erk:/ocse/0.2/zebra_base_data#I9848"], [5, "erk:/ocse/0.2/zebra_base_data#I3132"], [5, - "erk:/ocse/0.2/zebra_base_data#I2552"], [5, "erk:/ocse/0.2/zebra_base_data#I5931"]], - "erk:/ocse/0.2/zebra_base_data#R8098": [[5, "erk:/ocse/0.2/zebra_base_data#I4037"], [5, - "erk:/ocse/0.2/zebra_base_data#I9848"], [5, "erk:/ocse/0.2/zebra_base_data#I3132"], [5, - "erk:/ocse/0.2/zebra_base_data#I2552"], [5, "erk:/ocse/0.2/zebra_base_data#I5931"]], - "erk:/ocse/0.2/zebra_base_data#R8592": [[5, "erk:/ocse/0.2/zebra_base_data#I4037"], [5, - "erk:/ocse/0.2/zebra_base_data#I9848"], [5, "erk:/ocse/0.2/zebra_base_data#I3132"], [5, - "erk:/ocse/0.2/zebra_base_data#I2552"], [5, "erk:/ocse/0.2/zebra_base_data#I5931"]]} + {"irk:/ocse/0.2/zebra_base_data#R8216": [[5, "irk:/ocse/0.2/zebra_base_data#I4037"], [5, + "irk:/ocse/0.2/zebra_base_data#I9848"], [5, "irk:/ocse/0.2/zebra_base_data#I3132"], [5, + "irk:/ocse/0.2/zebra_base_data#I2552"], [5, "irk:/ocse/0.2/zebra_base_data#I5931"]], + "irk:/ocse/0.2/zebra_base_data#R9040": [[5, "irk:/ocse/0.2/zebra_base_data#I4037"], [5, + "irk:/ocse/0.2/zebra_base_data#I9848"], [5, "irk:/ocse/0.2/zebra_base_data#I3132"], [5, + "irk:/ocse/0.2/zebra_base_data#I2552"], [5, "irk:/ocse/0.2/zebra_base_data#I5931"]], + "irk:/ocse/0.2/zebra_base_data#R5611": [[5, "irk:/ocse/0.2/zebra_base_data#I4037"], [5, + "irk:/ocse/0.2/zebra_base_data#I9848"], [5, "irk:/ocse/0.2/zebra_base_data#I3132"], [5, + "irk:/ocse/0.2/zebra_base_data#I2552"], [5, "irk:/ocse/0.2/zebra_base_data#I5931"]], + "irk:/ocse/0.2/zebra_base_data#R8098": [[5, "irk:/ocse/0.2/zebra_base_data#I4037"], [5, + "irk:/ocse/0.2/zebra_base_data#I9848"], [5, "irk:/ocse/0.2/zebra_base_data#I3132"], [5, + "irk:/ocse/0.2/zebra_base_data#I2552"], [5, "irk:/ocse/0.2/zebra_base_data#I5931"]], + "irk:/ocse/0.2/zebra_base_data#R8592": [[5, "irk:/ocse/0.2/zebra_base_data#I4037"], [5, + "irk:/ocse/0.2/zebra_base_data#I9848"], [5, "irk:/ocse/0.2/zebra_base_data#I3132"], [5, + "irk:/ocse/0.2/zebra_base_data#I2552"], [5, "irk:/ocse/0.2/zebra_base_data#I5931"]]} """.replace("\n","") data = json.loads(raw_data) @@ -1138,8 +1121,8 @@ def test_d13__check_type(self): self.assertFalse(p.check_type(data, Dict[str, List[int]], strict=False)) self.assertFalse(p.check_type(data, Dict[str, List[List[int]]], strict=False)) - # pydantic has nontrivial behavior abour Unions. This requires smart_unions=True - q = [5, 'erk:/ocse/0.2/zebra_base_data#I9848'] + # pydantic has nontrivial behavior about Unions. This requires smart_unions=True + q = [5, 'irk:/ocse/0.2/zebra_base_data#I9848'] p.check_type(q, List[Union[int, str]]) # this is what we actually want to test (note that the inner list could be specified more precisely) @@ -1184,6 +1167,7 @@ def myhook3(itm: p.Item): # ensure expected number of hooks (after re-initialization) self.assertEqual(sum([len(lst) for lst in p.ds.hooks.values()]), 0) + # TODO: obsolete? def test_d15__setattr(self): return @@ -1196,8 +1180,56 @@ def test_d15__setattr(self): self.assertTrue(R301.uri in itm1.get_relations()) + def test_d16__IntegerRangeElement(self): + # the original definition of the IRE had the problem that it only was + # applicable in the same module where it was defined + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") + + with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): + I9223 = p.create_item( + R1__has_label="definition of zero matrix", + R2__has_description="the defining statement of what a zero matrix is", + R4__is_instance_of=p.I20["mathematical definition"], + ) + + with I9223["definition of zero matrix"].scope("setting") as cm: + cm.new_var(M=p.uq_instance_of(ma.I9904["matrix"])) + + with I9223["definition of zero matrix"].scope("premise") as cm: + with ma.IntegerRangeElement(start=1, stop=10) as i: + with ma.IntegerRangeElement(start=1, stop=10) as j: + + # create an auxiliary variable (not part part of the graph) + M_ij = ma.I3240["matrix element"](cm.M, i, j) + cm.new_equation(lhs=M_ij, rhs=ma.I5000["scalar zero"]) + + def test_e01__I14_subclass_scopes(self): + # test whether the scope method is inherited correctly + with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): + prop = p.instance_of(p.I17["equivalence proposition"]) + scp1 = prop.scope("setting") + scp2 = prop.scope("premise") + scp3 = prop.scope("assertion") + + I1000 = p.create_item( + R1__has_label="Cayley-Hamilton theorem", + R4__is_instance_of=p.I15["implication proposition"], + ) + + scp1 = I1000.scope("setting") + + I1001 = p.create_item( + R1__has_label="Cayley-Hamilton theorem", + R4__is_instance_of=p.I17["equivalence proposition"], + ) + scp1 = I1001.scope("setting") + + def test_e02__is_true(self): + ma = p.irkloader.load_mod_from_path(TEST_DATA_PATH_MA, prefix="ma") + self.assertTrue(p.is_true(ma.I5359, p.R4, ma.I4895)) + self.assertTrue(p.is_true(ma.I5359["determinant"], p.R4["is instance of"], ma.I4895["mathematical operator"])) -class Test_02_ruleengine(HouskeeperMixin, unittest.TestCase): +class Test_02_ruleengine(HousekeeperMixin, unittest.TestCase): def setUp(self): super().setUp() @@ -1211,16 +1243,16 @@ def setup_data1(self): key_str="I400", R1__has_label="subproperty rule 1", R2__has_description=( - # "specifies the 'transitivity' of I11_mathematical_property-instances via R17_issubproperty_of" + # "specifies the 'transitivity' of I54_mathematical_property-instances via R17_issubproperty_of" "specifies the 'transitivity' of R17_is_subproperty_of" ), R4__is_instance_of=p.I41["semantic rule"], ) with self.rule1["subproperty rule 1"].scope("setting") as cm: - cm.new_var(P1=p.instance_of(p.I11["mathematical property"])) - cm.new_var(P2=p.instance_of(p.I11["mathematical property"])) - cm.new_var(P3=p.instance_of(p.I11["mathematical property"])) + cm.new_var(P1=p.instance_of(p.I54["mathematical property"])) + cm.new_var(P2=p.instance_of(p.I54["mathematical property"])) + cm.new_var(P3=p.instance_of(p.I54["mathematical property"])) # # A = cm.new_var(sys=instance_of(I1["general item"])) # with self.rule1["subproperty rule 1"].scope("premise") as cm: @@ -1294,10 +1326,10 @@ def test_c04__ruleengine03(self): # ensures that the rule does not match itself self.assertEqual(len(res_graph), 0) - # in this erk module some properties have subproperties - _ = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct", modname=TEST_MOD_NAME) + # in this irk module some properties have subproperties + _ = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct", modname=TEST_MOD_NAME) - # create a new RuleApplicator because the overal graph changed + # create a new RuleApplicator because the overall graph changed ra = p.ruleengine.RuleApplicator(self.rule1) ra_worker = ra.ra_workers[0] res_graph = ra_worker.match_subgraph_P() @@ -1306,7 +1338,7 @@ def test_c04__ruleengine03(self): def test_c05__ruleengine04(self): self.setup_data1() - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct", modname=TEST_MOD_NAME) + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, prefix="ct", modname=TEST_MOD_NAME) self.assertEqual(len(mod1.I9642["local exponential stability"].get_relations("R17__is_subproperty_of")), 1) ra = p.ruleengine.RuleApplicator(self.rule1, mod_context_uri=TEST_BASE_URI) res = ra.apply() @@ -1328,7 +1360,7 @@ def test_c06__ruleengine05(self): _ = p.ruleengine.apply_all_semantic_rules() -class Test_03_Multilinguality(HouskeeperMixin, unittest.TestCase): +class Test_03_Multilinguality(HousekeeperMixin, unittest.TestCase): def test_a01__label(self): teststring1 = "this is english text" @ p.en @@ -1468,7 +1500,7 @@ def test_b1__multilingual_relations1(self): self.assertEqual(desc2.language, "de") - # use the labels of different languages in index-labeld key notation + # use the labels of different languages in index-labeled key notation # first: without explicitly specifying the language tmp1 = itm["test-label in english"] @@ -1517,14 +1549,14 @@ def test_b1__multilingual_relations1(self): # multiple values to R1 can be passed using a list R1__has_label="test-label2", # this is now interpreted as de-label R1__has_label__en="test-label2-en", - R2__has_description="test beschreibung auf deutsch", + R2__has_description="test Beschreibung auf deutsch", ) # in case of ordinary strings they should be used if no value is available for current language self.assertEqual(p.settings.DEFAULT_DATA_LANGUAGE, "de") self.assertEqual(itm2.R1, "test-label2" @ p.de) - self.assertEqual(itm2.R2, "test beschreibung auf deutsch" @ p.de) + self.assertEqual(itm2.R2, "test Beschreibung auf deutsch" @ p.de) p.settings.DEFAULT_DATA_LANGUAGE = "en" self.assertEqual(itm2.R1.value, "test-label2-en") @@ -1533,14 +1565,14 @@ def test_b1__multilingual_relations1(self): self.assertEqual(itm2.R2, None) # TODO: decide whether this behavior (returning some other lang) would be better - # self.assertEqual(itm2.R2, "test beschreibung auf deutsch" @ p.de) + # self.assertEqual(itm2.R2, "test Beschreibung auf deutsch" @ p.de) # test for correct error message with p.uri_context(uri=TEST_BASE_URI, prefix="ut"): itm1 = p.instance_of(p.I1["general item"]) - # this should cause no error (because of differnt language) + # this should cause no error (because of different language) itm1.set_relation(p.R1["has label"], "neues Label" @ p.de) with self.assertRaises(p.aux.FunctionalRelationError): @@ -1556,20 +1588,49 @@ def test_b2__multilingual_relations2(self): labels = R300.get_relations("R1", return_obj=True) self.assertEqual(labels, ["default rel-label"@p.df, "deutsches rel-label"@p.de]) + # ensure the correct range for the hardcoded relations + for short_key in p.RELKEYS_WITH_LITERAL_RANGE: + rel = p.ds.get_entity_by_uri(p.aux.make_uri(p.builtin_entities.__URI__, short_key)) + self.assertIn(p.I19["language-specified string literal"], rel.R11__has_range_of_result) + + # test R77__has_alternative_label + + I1000 = p.create_item( + R1__has_label="foo", + R1__has_label__de="foo-de", + R77__has_alternative_label="bar", + R77__has_alternative_label__de="baz", + ) + + self.assertEqual(I1000.R77__has_alternative_label, ["bar"@p.df, "baz"@p.de]) + + # TODO: this should be automatically converted to default language + # I1000.R77__has_alternative_label = "more foo" + + I1000.R77__has_alternative_label = "more foo"@p.en + self.assertEqual(I1000.R77__has_alternative_label, ["bar"@p.df, "baz"@p.de, "more foo"@p.df]) + + + I1000.set_multiple_relations("R77__has_alternative_label", ["foo-it"@p.it, "bar-es"@p.es]) + self.assertEqual( + I1000.R77__has_alternative_label, + ["bar"@p.df, "baz"@p.de, "more foo"@p.df, "foo-it"@p.it, "bar-es"@p.es] + ) + -class Test_Z_Core(HouskeeperMixin, unittest.TestCase): +class Test_Z_Core(HousekeeperMixin, unittest.TestCase): """ - Collection of test that should be executed last (because they seem to influence othter tests). + Collection of test that should be executed last (because they seem to influence other tests). This is achieved by putting "ZZ" in the name (assuming that test classes are executed in alphabetical order). """ def test_sparql_query(self): # This test seems somehow to influence later tests - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) p.ds.rdfgraph = p.rdfstack.create_rdf_triples() qsrc = p.rdfstack.get_sparql_example_query() res = p.ds.rdfgraph.query(qsrc) - res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyerk, res) + res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyirk, res) # Note: this might fail if more `R5__has_part` relations are used expected_result = [ @@ -1580,7 +1641,7 @@ def test_sparql_query(self): def test_c01__sparql_query2(self): # TODO: replace by Model entity once it exists - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) with p.uri_context(uri=TEST_BASE_URI): m1 = p.instance_of(mod1.I7641["general system model"], r1="test_model 1", r2="a test model") @@ -1593,7 +1654,7 @@ def test_c01__sparql_query2(self): p.ds.rdfgraph = p.rdfstack.create_rdf_triples() qsrc = f""" - PREFIX : <{p.rdfstack.ERK_URI}> + PREFIX : <{p.rdfstack.IRK_URI}> PREFIX ct: <{mod1.__URI__}#> SELECT ?s ?o WHERE {{ @@ -1601,7 +1662,7 @@ def test_c01__sparql_query2(self): }} """ res = p.ds.rdfgraph.query(qsrc) - res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyerk, res) + res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyirk, res) expected_result = [ [m2["test_model 2"], None], @@ -1609,7 +1670,7 @@ def test_c01__sparql_query2(self): self.assertEqual(res2, expected_result) def test_c02__sparql_zz_preprocessing(self): - mod1 = p.erkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) + mod1 = p.irkloader.load_mod_from_path(TEST_DATA_PATH2, TEST_MOD_NAME) with p.uri_context(uri=TEST_BASE_URI): m1 = p.instance_of(mod1.I7641["general system model"], r1="test_model 1", r2="a test model") @@ -1631,7 +1692,7 @@ def test_c02__sparql_zz_preprocessing(self): for condition in condition_list: qsrc_corr = f""" - PREFIX : <{p.rdfstack.ERK_URI}> + PREFIX : <{p.rdfstack.IRK_URI}> PREFIX ct: <{mod1.__URI__}#> SELECT ?s ?o WHERE {{ @@ -1640,10 +1701,10 @@ def test_c02__sparql_zz_preprocessing(self): """ q = p.ds.preprocess_query(qsrc_corr) res = p.ds.rdfgraph.query(q) - res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyerk, res) + res2 = p.aux.apply_func_to_table_cells(p.rdfstack.convert_from_rdf_to_pyirk, res) self.assertGreater(len(res2), 0) - # syntactically incorrect querys: + # syntactically incorrect queries: condition_list = [ "?s :R16__wrong ct:I7864__controllability.", ] @@ -1653,7 +1714,7 @@ def test_c02__sparql_zz_preprocessing(self): for condition, msg in zip(condition_list, msg_list): qsrc_incorr_1 = f""" - PREFIX : <{p.rdfstack.ERK_URI}> + PREFIX : <{p.rdfstack.IRK_URI}> PREFIX ct: <{mod1.__URI__}#> SELECT ?s ?o WHERE {{ @@ -1666,8 +1727,8 @@ def test_c02__sparql_zz_preprocessing(self): @unittest.skipIf(os.environ.get("CI"), "Skipping report tests on CI to prevent dependencies") -class Test_06_reportgenerator(HouskeeperMixin, unittest.TestCase): - @p.erkloader.preserve_cwd +class Test_06_reportgenerator(HousekeeperMixin, unittest.TestCase): + @p.irkloader.preserve_cwd def tearDown(self) -> None: super().tearDown() os.chdir(pjoin(TEST_DATA_DIR1, "reports")) @@ -1686,13 +1747,13 @@ def test_c01__resolve_entities_in_nested_data(self): data1exp = {"key1": some_list, "key2": p.I1} self.assertEqual(reind(data1), data1exp) - mod2 = p.erkloader.load_mod_from_path(TEST_DATA_PATH3, prefix="ag") + mod2 = p.irkloader.load_mod_from_path(TEST_DATA_PATH3, prefix="ag") data1 = {"key1": ':ag__I2746["Rudolf Kalman"]', "key2": {"nested_key": ':ag__R1833["has employer"]'}} data1exp = {"key1": mod2.I2746, "key2": {"nested_key": mod2.R1833}} self.assertEqual(reind(data1), data1exp) - @p.erkloader.preserve_cwd + @p.irkloader.preserve_cwd def test_c02__report_generation1(self): reportconf_path1 = pjoin(TEST_DATA_DIR1, "reports", "reportconf.toml") @@ -1706,7 +1767,7 @@ def test_c02__report_generation1(self): self.assertEqual(len(rg.authors), 2) -class Test_07_import_export(HouskeeperMixin, unittest.TestCase): +class Test_07_import_export(HousekeeperMixin, unittest.TestCase): def test_b01__rdf_export(self): @@ -1758,7 +1819,7 @@ def test_b03__zebra_puzzle_import(self): """ match persons which have four negative statements of the same kind (test statement relations) """ - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") self.assertEqual(zb.I9848["Norwegian"].zb__R8098__has_house_color, None) self.assertEqual(zb.I9848["Norwegian"].zb__R1055__has_not_house_color, []) @@ -1772,8 +1833,8 @@ def test_b03__zebra_puzzle_import(self): self.assertEqual(len(zb.I9848["Norwegian"].zb__R1055__has_not_house_color), 4) def test_b04__zebra_puzzle_unlinked_items(self): - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") # persons1 ... person12 exists -> if they are unlinked within a module this is not yet reflected in the # rdf-data -> TODO: introduce a "Housekeeping" item for every module diff --git a/tests/test_data/README.md b/tests/test_data/README.md index 02345f6..e60196a 100644 --- a/tests/test_data/README.md +++ b/tests/test_data/README.md @@ -1 +1 @@ -Test data was moved to its own repository (see pyerk/README.md) for assumed diretory structure. +Test data was moved to its own repository (see pyirk/README.md) for assumed diretory structure. diff --git a/tests/test_data/erkpackage.toml b/tests/test_data/irkpackage.toml similarity index 54% rename from tests/test_data/erkpackage.toml rename to tests/test_data/irkpackage.toml index d21188e..31ef411 100644 --- a/tests/test_data/erkpackage.toml +++ b/tests/test_data/irkpackage.toml @@ -1,7 +1,7 @@ -# this file contains some configuration for the pyerk package +# this file contains some configuration for the pyirk package main_module = "zebra02.py" main_module_prefix = "zp" # zebra puzzle version = "0.1.0.0" -[pyerkdjango] +[pyirkdjango] diff --git a/tests/test_data/ocse_subset/README.md b/tests/test_data/ocse_subset/README.md new file mode 100644 index 0000000..5eafdda --- /dev/null +++ b/tests/test_data/ocse_subset/README.md @@ -0,0 +1,5 @@ +This directory contains an autogenerated subset of the OCSE (ontology of control systems engineering) which is used as real world testing data. + +Originally the "real" OCSE data was used for testing. This had the advantage of avoiding double maintaince effort (OCSE and testing data) but led to increased runtimes of the test suite with the growing number of entities and statements in the OCSE. + +Solution: automatically extract the relevant entities and statements from the real ocse. diff --git a/tests/test_data/ocse_subset/agents1.py b/tests/test_data/ocse_subset/agents1.py new file mode 100644 index 0000000..376c055 --- /dev/null +++ b/tests/test_data/ocse_subset/agents1.py @@ -0,0 +1,128 @@ +# Note: this is not the real module for this URI it is an autogenerated subset for testing + + +import pyirk as p + + +__URI__ = "irk:/ocse/0.2/agents" + +keymanager = p.KeyManager(keyseed=1239) +p.register_mod(__URI__, keymanager) +p.start_mod(__URI__) + +start_time = p.QualifierFactory(p.R48["has start time"]) +end_time = p.QualifierFactory(p.R49["has end time"]) + + +def create_person(given_name: str, family_name: str, r2: str, r33=None, r3474=None, r3475=None): + """ + This is a convenience function that simplifies the creation of items for humans + """ + item_key = p.get_key_str_by_inspection() + + r1 = f"{given_name} {family_name}" + item: p.Item = p.create_item( + item_key, + R1__has_label=r1, + R2__has_description=r2, + R4__is_instance_of=I7435["human"], + R7781__has_family_name=family_name, + R7782__has_given_name=given_name, + ) + + if r33: + assert isinstance(r33, str) + item.set_relation(p.R33["has corresponding wikidata entity"], r33) + if r3474: + assert isinstance(r3474, str) + item.set_relation(R3474["has ORCID"], r3474) + if r3475: + assert isinstance(r3475, str) + item.set_relation(R3475["has DBLP author ID"], r3475) + + return item + +I7435 = p.create_item( + R1__has_label="human", + R2__has_description="human being", + R4__is_instance_of=p.I2["Metaclass"], + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/entity/Q5", +) + +R7781 = p.create_relation( + R1__has_label="has family name", + R2__has_description="part of the full name of a person", + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/wiki/Property:P734", +) + +R7782 = p.create_relation( + R1__has_label="has given name", + R2__has_description="first name or another given name of this person", + R18__has_usage_hint=[ + "this relation is non-functional, i.e. a person can have multiple given names; order matters", + "if given name is unknown, it is acceptable to use initials here", + ], + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/wiki/Property:P735", +) + +R3474 = p.create_relation( + R1__has_label="has ORCID", + R2__has_description="specifies the orcid of a researcher", + R18__has_usage_hint="This can be used if no wikidata entry is yet available", + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/wiki/Property:P496", +) + +R3475 = p.create_relation( + R1__has_label="has DBLP author ID", + R2__has_description="specifies the DBLP author ID of a researcher", + R18__has_usage_hint="This can be used if neither wikidata nor ORCID is yet available", + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/wiki/Property:P2456", +) + +I2746 = create_person("Rudolf", "Kalman", "electrical engineer and mathematician") + +R1833 = p.create_relation( + R1__has_label="has employer", + R2__has_description="specifies for which entity (organization/person) the subject works", + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/entity/P108", +) + +I1342 = p.create_item( + R1__has_label="academic institution", + R2__has_description="educational institution dedicated to education and research", + R4__is_instance_of=p.I2["Metaclass"], + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/entity/Q4671277", +) + +I9942 = p.create_item( + R1__has_label="Stanford University", + R2__has_description="private research university in California, USA", + R4__is_instance_of=I1342["academic institution"], + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/entity/Q41506", +) + +I7301 = p.create_item( + R1__has_label="ETH Zürich", + R2__has_description="Swiss Federal Institute of Technology in Zürich", + R4__is_instance_of=I1342["academic institution"], + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/entity/Q11942", +) + +I2746["Rudolf Kalman"].set_relation(R1833["has employer"], I9942["Stanford University"], qualifiers=[start_time("1964"), end_time("1971")]) + + +I2746["Rudolf Kalman"].set_relation(R1833["has employer"], I7301["ETH Zürich"], qualifiers=[start_time("1973"), end_time("1997")]) + + +I4853 = create_person("Sophus", "Lie", "mathematician", r33="https://www.wikidata.org/wiki/Q30769") + +R6876 = p.create_relation( + R1__has_label="is named after", + R2__has_description="specifies that the subject is an eponym named after the object", + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/entity/P138", +) + +I2151 = create_person("Aleksandr", "Lyapunov", "mathematician and physicist") + + +p.end_mod() diff --git a/tests/test_data/ocse_subset/control_theory1.py b/tests/test_data/ocse_subset/control_theory1.py new file mode 100644 index 0000000..1ef9fa2 --- /dev/null +++ b/tests/test_data/ocse_subset/control_theory1.py @@ -0,0 +1,227 @@ +# Note: this is not the real module for this URI it is an autogenerated subset for testing +import pyirk as p + + +# noinspection PyUnresolvedReferences +from ipydex import IPS, activate_ips_on_exception # noqa + +ma = p.irkloader.load_mod_from_path("./math1.py", prefix="ma") +ag = ma.ag + + +__URI__ = "irk:/ocse/0.2/control_theory" + +keymanager = p.KeyManager() +p.register_mod(__URI__, keymanager) +p.start_mod(__URI__) + + +I1347 = p.create_item( + R1__has_label="Lie derivative of scalar field", + R2__has_description=( + "mathematical operation wich maps a scalar field h_1 to a new scalar field h_2, depending on a vector field f; " + "h2 can be interpreted as the time derivative of h_1 along the solution of the ode associated with f; " + "in other words: along the flow of f" + ), + R4__is_instance_of=ma.I4895["mathematical operator"], + R8__has_domain_of_argument_1=ma.I9923["scalar field"], + R9__has_domain_of_argument_2=ma.I9841["vector field"], + R10__has_domain_of_argument_3=ma.I1168["point in state space"], + R11__has_range_of_result=ma.I9923["scalar field"], + R13__has_canonical_symbol=r"$L$", + # TODO: complete defining equation + ag__R6876__is_named_after=ag.I4853["Sophus Lie"], +) + +I2928 = p.create_item( + R1__has_label="general model representation", + R2__has_description="general (mathematical) representation of a model of a dynamical system", + R4__is_instance_of=p.I2["Metaclass"], +) + +I6886 = p.create_item( + R1__has_label="general ode state space representation", + R2__has_description="explicit ODE system description of a dynamical system", + R3__is_subclass_of=I2928["general model representation"], + # TODO: this has to use create_equation (to be implemented) + R6__has_defining_mathematical_relation=p.create_expression(r"$\dot x = f(x, u)$"), +) + +I4466 = p.create_item( + R1__has_label="Systems Theory", + R2__has_description="academic field; might be regarded as part of applied mathematics", + R4__is_instance_of=p.I3["Field of science"], + R5__is_part_of=[p.I4["Mathematics"], p.I5["Engineering"]], +) + +I7641 = p.create_item( + R1__has_label="general system model", + R2__has_description="model of a dynamical system", + R4__is_instance_of=p.I2["Metaclass"], +) + +I5948 = p.create_item( + R1__has_label="dynamical system", + R2__has_description="system with the capability to change over time, optionally with explicit input and/or output", + R4__is_instance_of=p.I2["Metaclass"], # this means: this Item is an ordinary class +) + +R7641 = p.create_relation( + R1__has_label="has approximation", + R2__has_description="object or class which is an approximation of a dynamical system", + R8__has_domain_of_argument_1=I5948["dynamical system"], + R11__has_range_of_result=I7641["general system model"], +) + +I5356 = p.create_item( + R1__has_label="general system property", + R2__has_description="general property of a model of a dynamical system (not of its representation)", + R4__is_instance_of=p.I2["Metaclass"], +) + +I4101 = p.create_item( + R1__has_label="general time variance", + R2__has_description="states that the model of a dynamical system (i.e. its parameters) might change over time", + R4__is_instance_of=I5356["general system property"], +) + +I7733 = p.create_item( + R1__has_label="time invariance", + R2__has_description="states that the model of a dynamical system (i.e. its parameters) does not change over time", + R4__is_instance_of=I5356["general system property"], + R17__is_subproperty_of=I4101["general time variance"], + R77__has_alternative_label="autonomy", +) + +I1793 = p.create_item( + R1__has_label="general model representation property", + R2__has_description="general property of the representation of a model of a dynamical system \ + (not an intrinsic system property)", + R4__is_instance_of=p.I54["mathematical property"], +) + +I2827 = p.create_item( + R1__has_label="general nonlinearity", + R2__has_description="states that the system model equations might not be linear", + R4__is_instance_of=I1793["general model representation property"], +) + +I6091 = p.create_item( + R1__has_label="control affine", + R2__has_description="states that in the system model equations the input only appears linearly", + R4__is_instance_of=I1793["general model representation property"], + R6__has_defining_mathematical_relation=p.create_expression(r"$\dot{x}=f(x)+g(x)u$"), + R17__is_subproperty_of=I2827["general nonlinearity"], +) + +I5247 = p.create_item( + R1__has_label="polynomial", + R2__has_description="states that the system model equations are polynomial w.r.t. the state components", + R4__is_instance_of=I1793["general model representation property"], + R17__is_subproperty_of=I6091["control affine"], +) + +I4761 = p.create_item( + R1__has_label="linearity", + R2__has_description="states that the system model equations are linear", + R4__is_instance_of=I1793["general model representation property"], + R17__is_subproperty_of=I5247["polynomial"], +) + +I1898 = p.create_item( + R1__has_label="lti", + R2__has_description="states that the system model is linear and time-invariant", + R4__is_instance_of=I5356["general system property"], + R17__is_subproperty_of=[I4761["linearity"], I7733["time invariance"]], +) + +I2562 = p.create_item( + R1__has_label="general property of pde", + R2__has_description="general property of partial differential equations", + R3__is_subclass_of=I1793["general model representation property"], +) + +I2557 = p.create_item( + R1__has_label="quasilinearity", + R2__has_description="states that in a pde the highest order derivatives appear linearly, with their coefficients \ + being functions of the independent variables and their (lower order) derivatives", + R17__is_subproperty_of=I2562["general property of pde"], +) + +I3114 = p.create_item( + R1__has_label="semilinearity", + R2__has_description="states that in a pde the highest order derivatives appear linearly, with their coefficients \ + being functions of only the independent variables", + R17__is_subproperty_of=I2557["quasilinearity"], +) + +I3863 = p.create_item( + R1__has_label="linearity", + R2__has_description="states that in a pde the unknown function and all its derivatives appear linearly, with their coefficients \ + being functions of only the independent variables", + R17__is_subproperty_of=I3114["semilinearity"], +) + +I5236 = p.create_item( + R1__has_label="general trajectory property", + R2__has_description="general property of a trajectory", + R4__is_instance_of=p.I54["mathematical property"], +) + +I7207 = p.create_item( + R1__has_label="stability", + R2__has_description="tendency to stay close to some distinguished trajectory (e.g. equilibrium)", + R4__is_instance_of=p.I54["mathematical property"], +) + +I5082 = p.create_item( + R1__has_label="local attractiveness", + R2__has_description="states that all trajectories that start close enough to the trajectory in consideration will \ + converge to it", + R4__is_instance_of=I5236["general trajectory property"], +) + +I2931 = p.create_item( + R1__has_label="local Lyapunov stability", + R2__has_description="states that all trajectories that start close enough to the equilibrium will not leave a \ + certain neighborhood", + R4__is_instance_of=I5236["general trajectory property"], + R17__is_subproperty_of=I7207["stability"], + ag__R6876__is_named_after=ag.I2151["Aleksandr Lyapunov"], +) + +I4900 = p.create_item( + R1__has_label="local asymptotical stability", + R2__has_description="states that all trajectories that start close enough to the equilibrium remain close enough \ + and will converge to it", + R4__is_instance_of=I5236["general trajectory property"], + R17__is_subproperty_of=[I2931["local Lyapunov stability"], I5082["local attractiveness"]], +) + +I9642 = p.create_item( + R1__has_label="local exponential stability", + R2__has_description="states that an equilibrium is locally asymptotically stable and all trajectories converge at \ + least exponentially fast", + R4__is_instance_of=I5236["general trajectory property"], + R17__is_subproperty_of=I4900["local asymptotical stability"], +) + +I9210 = p.create_item( + R1__has_label="stabilizability", + R2__has_description="states that for the model of a dynamical system there exists a state feedback such that the \ + system is stable", + R4__is_instance_of=I5356["general system property"], +) + +I7864 = p.create_item( + R1__has_label="controllability", + R2__has_description="states that the state of the model of a dynamical system can be changed from any arbitrary \ + starting state to any desired state in a finite amount of time using an external input", + R4__is_instance_of=I5356["general system property"], + R17__is_subproperty_of=I9210["stabilizability"], +) + + + + +p.end_mod() diff --git a/tests/test_data/ocse_subset/irkpackage.toml b/tests/test_data/ocse_subset/irkpackage.toml new file mode 100644 index 0000000..ec35a31 --- /dev/null +++ b/tests/test_data/ocse_subset/irkpackage.toml @@ -0,0 +1,7 @@ +# this file contains some configuration for the pyirk package + +main_module = "control_theory1.py" +main_module_prefix = "ct" +version = "0.2.2.0" + +[pyirkdjango] diff --git a/tests/test_data/ocse_subset/math1.py b/tests/test_data/ocse_subset/math1.py new file mode 100644 index 0000000..ec08631 --- /dev/null +++ b/tests/test_data/ocse_subset/math1.py @@ -0,0 +1,508 @@ +# Note: this is not the real module for this URI it is an autogenerated subset for testing + +from typing import Union +import pyirk as p + +# noinspection PyUnresolvedReferences +from ipydex import IPS, activate_ips_on_exception # noqa + +ag = p.irkloader.load_mod_from_path("./agents1.py", prefix="ag") + +__URI__ = "irk:/ocse/0.2/math" + +keymanager = p.KeyManager() +p.register_mod(__URI__, keymanager) +p.start_mod(__URI__) + +I4895 = p.create_item( + R1__has_label="mathematical operator", + R2__has_description="general (unspecified) mathematical operator", + R3__is_subclass_of=p.I12["mathematical object"], +) + +I4895["mathematical operator"].add_method(p.create_evaluated_mapping, "_custom_call") + + +R3326 = p.create_relation( + R1__has_label="has dimension", + R2__has_description="specifies the dimension of a (dimensional) mathematical object", + R8__has_domain_of_argument_1=p.I12["mathematical object"], + R11__has_range_of_result=p.I38["non-negative integer"], + R22__is_functional=True, +) + +I5166 = p.create_item( + R1__has_label="vector space", + R2__has_description="type for a vector space", + R3__is_subclass_of=p.I13["mathematical set"], + R33__has_corresponding_wikidata_entity="https://www.wikidata.org/wiki/Q125977", + R41__has_required_instance_relation=R3326["has dimension"], +) + +I5167 = p.create_item( + R1__has_label="state space", + R2__has_description="type for a state space of a dynamical system (I6886)", + R3__is_subclass_of=I5166["vector space"], + + # this should be defined via inheritance from vector space + # TODO: test that this is the case + # R41__has_required_instance_relation=R3326["has dimension"], +) + +R5405 = p.create_relation( + R1__has_label="has associated state space", + R2__has_description="specifies the associated state space of the subject (e.g. a I9273__explicit...ode_system)", + R8__has_domain_of_argument_1=p.I12["mathematical object"], + R11__has_range_of_result=I5167["state space"], + R22__is_functional=True, +) + +I1168 = p.create_item( + R1__has_label="point in state space", + R2__has_description="type for a point in a given state space", + R3__is_subclass_of=p.I12["mathematical object"], + R41__has_required_instance_relation=R5405["has associated state space"], +) + +I9923 = p.create_item( + R1__has_label="scalar field", + R2__has_description="...", + R3__is_subclass_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I1168["point in state space"], + R11__has_range_of_result=p.I35["real number"], +) + +I7151 = p.create_item( + R1__has_label="vector", + R2__has_description="vector of (in general) complex numbers, i.e. vector over the field of complex numbers", + R3__is_subclass_of=p.I18["mathematical expression"], +) + +I9841 = p.create_item( + R1__has_label="vector field", + R2__has_description="...", + R3__is_subclass_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I1168["point in state space"], + R11__has_range_of_result=I7151["vector"], +) + +I1060 = p.create_item( + R1__has_label="general function", + R2__has_description="function that maps from some set (domain) into another (range);", + R3__is_subclass_of=p.I18["mathematical expression"], + R18__has_usage_hint="this is the base class for more specific types of functions", +) + +I1063 = p.create_item( + R1__has_label="scalar function", + R2__has_description="function that has one (in general complex) number as result", + R3__is_subclass_of=I1060["general function"], +) + +I4237 = p.create_item( + R1__has_label="monovariate rational function", + R2__has_description="...", + R3__is_subclass_of=I1063["scalar function"], +) + +I4237["monovariate rational function"].add_method(p.create_evaluated_mapping, "_custom_call") + + +I4239 = p.create_item( + R1__has_label="abstract monovariate polynomial", + R2__has_description=( + "abstract monovariate polynomial (argument might be a complex-valued scalar, a matrix, an operator, etc.)" + ), + R3__is_subclass_of=I4237["monovariate rational function"], +) + +I9904 = p.create_item( + R1__has_label="matrix", + R2__has_description="matrix of (in general) complex numbers, i.e. matrix over the field of complex numbers", + R3__is_subclass_of=p.I12["mathematical object"], +) + +I9905 = p.create_item( + R1__has_label="zero matrix", + R2__has_description="like its superclass but with all entries equal to zero", + R3__is_subclass_of=I9904["matrix"], +) + +R5939 = p.create_relation( + R1__has_label="has column number", + R2__has_description="specifies the number of columns of a matrix", + R8__has_domain_of_argument_1=I9904["matrix"], + R11__has_range_of_result=p.I38["non-negative integer"], + R22__is_functional=True, +) + +R5938 = p.create_relation( + R1__has_label="has row number", + R2__has_description="specifies the number of rows of a matrix", + R8__has_domain_of_argument_1=I9904["matrix"], + R11__has_range_of_result=p.I38["non-negative integer"], + R22__is_functional=True, +) + +I5177 = p.create_item( + R1__has_label="matmul", + R2__has_description=("matrix multiplication operator"), + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I9904["matrix"], + R9__has_domain_of_argument_2=I9904["matrix"], + R11__has_range_of_result=I9904["matrix"], +) + +I5000 = p.create_item( + R1__has_label="scalar zero", + R2__has_description="entity representing the zero-element in the set of complex numbers and its subsets", + R4__is_instance_of=p.I38["non-negative integer"], + R24__has_LaTeX_string="$0$", +) + +R3033 = p.create_relation( + R1__has_label="has type of elements", + R2__has_description=( + "specifies the item-type of the elements of a mathematical set; " + "should be a subclass of I12['mathematical object']" + ), + R8__has_domain_of_argument_1=p.I13["mathematical set"], + R11__has_range_of_result=p.I42["mathematical type (metaclass)"], +) + +I8133 = p.create_item( + R1__has_label="field of numbers", + R1__has_label__de="Zahlenkörper", + R2__has_description="general field of numbers; baseclass for the fields of real and complex numbers", + R3__is_subclass_of=p.I13["mathematical set"], +) + +I2738 = p.create_item( + R1__has_label="field of complex numbers", + R2__has_description="field of complex numbers", + R4__is_instance_of=I8133["field of numbers"], + R13__has_canonical_symbol=r"$\mathbb{C}$", + R3033__has_type_of_elements=p.I34["complex number"], +) + +I5807 = p.create_item( + R1__has_label="sign", + R2__has_description="returns the sign of a real number, i.e. on element of {-1, 0, 1}", + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=p.I35["real number"], + R11__has_range_of_result=p.I37["integer number"], +) + +I9906 = p.create_item( + R1__has_label="square matrix", + R2__has_description="a matrix for which the number of rows and columns are equal", + R3__is_subclass_of=I9904["matrix"], + # TODO: formalize the condition inspired by OWL +) + +I9907 = p.create_item( + R1__has_label="definition of square matrix", + R2__has_description="the defining statement of what a square matrix is", + R4__is_instance_of=p.I20["mathematical definition"], +) + +with I9907.scope("setting") as cm: + cm.new_var(M=p.uq_instance_of(I9904["matrix"])) + cm.new_var(nr=p.uq_instance_of(p.I39["positive integer"])) + + cm.new_var(nc=p.instance_of(p.I39["positive integer"])) + + cm.new_rel(cm.M, R5938["has row number"], cm.nr) + cm.new_rel(cm.M, R5939["has column number"], cm.nc) + +with I9907.scope("premise") as cm: + # number of rows == number of columns + cm.new_equation(lhs=cm.nr, rhs=cm.nc) + +with I9907.scope("assertion") as cm: + cm.new_rel(cm.M, p.R30["is secondary instance of"], I9906["square matrix"]) + +I9906["square matrix"].set_relation(p.R37["has definition"], I9907["definition of square matrix"]) + + +I6259 = p.create_item( + R1__has_label="sequence", + R2__has_description="common (secondary) base class of sequence of mathematical objects", + R3__is_subclass_of=p.I12["mathematical object"], +) + +I9739 = p.create_item( + R1__has_label="finite scalar sequence", + R2__has_description="base class of a finite sequence of (in general) complex numbers; can be indexed", + R3__is_subclass_of=I6259["sequence"], +) + +I4240 = p.create_item( + R1__has_label="matrix polynomial", + R2__has_description="monovariate polynomial of quadratic matrices", + R3__is_subclass_of=I4239["abstract monovariate polynomial"], + R8__has_domain_of_argument_1=I9906["square matrix"], + R11__has_range_of_result=I9906["square matrix"], +) + +R5940 = p.create_relation( + R1__has_label="has characteristic polynomial", + R2__has_description="specifies the characteristic polynomial of a square matrix A, i.e. det(s·I-A)", + R8__has_domain_of_argument_1=I9906["square matrix"], + R11__has_range_of_result=I4239["abstract monovariate polynomial"], +) + +I3058 = p.create_item( + R1__has_label="coefficients of characteristic polynomial", + R2__has_description="...", + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I9906["square matrix"], + R11__has_range_of_result=I9739["finite scalar sequence"], +) + +I3749 = p.create_item( + R1__has_label="Cayley-Hamilton theorem", + R2__has_description="establishes that every square matrix is a root of its own characteristic polynomial", + R4__is_instance_of=p.I15["implication proposition"], +) + +with I3749["Cayley-Hamilton theorem"].scope("setting") as cm: + cm.new_var(A=p.uq_instance_of(I9906["square matrix"])) + cm.new_var(n=p.uq_instance_of(p.I39["positive integer"])) + cm.new_var(coeffs_cp_A=I3058["coefficients of characteristic polynomial"](cm.A)) + + cm.new_var(P=p.instance_of(I4240["matrix polynomial"])) + + cm.new_var(Z=p.instance_of(I9905["zero matrix"])) + + cm.new_rel(cm.A, R5938["has row number"], cm.n) + cm.new_rel(cm.A, R5940["has characteristic polynomial"], cm.P) + cm.new_rel(cm.Z, R5938["has row number"], cm.n) + cm.new_rel(cm.Z, R5939["has column number"], cm.n) + cm.new_rel(cm.Z, p.R24["has LaTeX string"], r"\mathbf{0}") + +with I3749["Cayley-Hamilton theorem"].scope("assertion") as cm: + cm.new_equation(lhs=cm.P(cm.A), rhs=cm.Z) + +I5030 = p.create_item( + R1__has_label="variable", + R2__has_description="symbol which can represent another mathematical object", + R3__is_subclass_of=p.I12["mathematical object"], +) + +R8736 = p.create_relation( + R1__has_label="depends polynomially on", + R2__has_description="subject has a polynomial dependency object", + R8__has_domain_of_argument_1=p.I12["mathematical object"], + R11__has_range_of_result=I5030["variable"], + R18__has_usage_hint=("This relation is intentionally not functional to model multivariate polynomial dependency"), +) + +I1935 = p.create_item( + R1__has_label="polynomial matrix", + R2__has_description="matrix whose entries contain (scalar) polynomials", + R3__is_subclass_of=I9904["matrix"], + R50__is_different_from=I4240["matrix polynomial"], +) + +I7765 = p.create_item( + R1__has_label="scalar mathematical object", + R2__has_description="mathematical object which is or can be evaluated to a single (complex number)", + R3__is_subclass_of=p.I12["mathematical object"], +) + +I5359 = p.create_item( + R1__has_label="determinant", + R2__has_description="returns the determinant of a square matrix", + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I9906["square matrix"], + R11__has_range_of_result=I7765["scalar mathematical object"], +) + +def I5359_cc_pp(self, res, *args, **kwargs): + """ + Function which will be attached as custom-call-post-process-method to I5359["determinant"]. + + The I5359["determinant"] is an I4895__mathematical_operator. If it is called it creates an instance of + I32__evaluated_mapping. The the `_custom_call_post_process`-method (i.e. this function) of the operator is called. + + :param self: determinant operator item (to which this function will be attached) + :param res: instance of I7765["scalar mathematical object"] (determined by R11__has_range_of_result) + :param args: arg tuple () with which the mapping is called + """ + + assert len(args) == 1 + (matrix,) = args + + if poly_vars := matrix.R8736__depends_polynomially_on: + for var in poly_vars: + assert ("R4", I5030["variable"]) in p.get_taxonomy_tree(var) + res.set_relation(R8736["depends polynomially on"], var) + + return res + +I5359["determinant"].add_method(I5359_cc_pp, "_custom_call_post_process") + + +I6324 = p.create_item( + R1__has_label="canonical first order monic polynomial matrix", + R2__has_description="for a given square matrix A returns the polynomial matrix (s·I - A)", + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I9906["square matrix"], + R9__has_domain_of_argument_2=I5030["variable"], + R11__has_range_of_result=I1935["polynomial matrix"], +) + +def I6324_cc_pp(self, res, *args, **kwargs): + """ + :param self: mapping item (to which this function will be attached) + :param res: instance of I1935["polynomial matrix"] (determined by R11__has_range_of_result) + :param args: arg tuple (, ) with which the mapping is called + """ + + assert len(args) == 2 + matrix, var = args + + # check that `var` is an instance of I5030["variable"] + assert ("R4", I5030["variable"]) in p.get_taxonomy_tree(var) + res.set_relation(R8736["depends polynomially on"], var) + + return res + +I6324["canonical first order monic polynomial matrix"].add_method(I6324_cc_pp, "_custom_call_post_process") + + +I1195 = p.create_item( + R1__has_label="integer range", + R2__has_description="represents an integer range with start-, stop- and step-value", + R4__is_instance_of=p.I2["Metaclass"], +) + +R1616 = p.create_relation( + R1__has_label="has start value", + R2__has_description="...", + R8__has_domain_of_argument_1=I1195["integer range"], + R11__has_range_of_result=p.I37["integer number"], + R22__is_functional=True, +) + +R1617 = p.create_relation( + R1__has_label="has stop value", + R2__has_description="...", + R8__has_domain_of_argument_1=I1195["integer range"], + R11__has_range_of_result=p.I37["integer number"], + R22__is_functional=True, +) + +R1618 = p.create_relation( + R1__has_label="has step value", + R2__has_description="...", + R8__has_domain_of_argument_1=I1195["integer range"], + R11__has_range_of_result=p.I37["integer number"], + R22__is_functional=True, +) + +I6012 = p.create_item( + R1__has_label="integer range element", + R2__has_description="class whose instances represent an element from a specified range (I1195)", + R3__is_subclass_of=p.I37["integer number"], + R18__has_usage_hint=( + "Should always have an R3240__has_associated_range relation; " + "should be created via the context manager IntegerRangeElement (see below)" + ), +) + +class IntegerRangeElement: + """ + Context manager to model that a statement (or more) have an assertive claim for all elements of a sequence. + + ``` + with RangeElement(start=1, end=3) as i: + I456["some item"].R789_has_some_property(i) + ``` + + Has (roughly) the same meaning as + + ``` + I456["some item"].R789_has_some_property(1) + I456["some item"].R789_has_some_property(2) + I456["some item"].R789_has_some_property(3) + ``` + + Note, however, that the defining attributes of RangeElement, i.e. `start`, `stop`, `step` can be also variables. + + Behind the sc + + """ + + def __init__(self, start: Union[int, p.Item], stop: Union[int, p.Item], step: Union[int, p.Item] = 1): + self.start = start + self.stop = stop + self.step = step + + # these might serve to provide optional information to the range_element_item + self.r1 = None + self.r2 = None + + @staticmethod + def is_positive(i: Union[int, p.Item]) -> bool: + if isinstance(i, int): + return i > 0 + else: + return p.is_instance_of(i, p.I39["positive integer"]) + + @staticmethod + def is_nonnegative(i: Union[int, p.Item]) -> bool: + if isinstance(i, int): + return i >= 0 + else: + return p.is_instance_of(i, p.I38["non-negative integer"]) + + def __enter__(self): + """ + implicitly called in the head of the with statement + :return: + """ + + if self.is_positive(self.start) and self.is_positive(self.step): + class_item = p.I39["positive integer"] + elif self.is_nonnegative(self.start) and self.is_nonnegative(self.step): + class_item = p.I38["non-negative integer"] + else: + class_item = p.I37["integer number"] + + element = p.instance_of(class_item, self.r1, self.r2) + element.R30__is_secondary_instance_of = I6012["integer range element"] + + # run this explicitly in the context of this module (otherwise R1616 etc. is not defined) + with p.uri_context(uri=__URI__): + element.R1616__has_start_value = self.start + element.R1617__has_stop_value = self.stop + element.R1618__has_step_value = self.step + + element.finalize() + return element + + def __exit__(self, exc_type, exc_val, exc_tb): + # this is the place to handle exceptions + pass + +I3240 = p.create_item( + R1__has_label="matrix element", + R2__has_description=( + "mathematical operation wich maps a Matrix A, and two integers i, j to the scalar matrix entry A[i, j]. " + "Index counting starts at 1" + ), + R4__is_instance_of=I4895["mathematical operator"], + R8__has_domain_of_argument_1=I9904["matrix"], + R9__has_domain_of_argument_2=p.I39["positive integer"], + R10__has_domain_of_argument_3=p.I39["positive integer"], + R11__has_range_of_result=p.I34["complex number"], + R13__has_canonical_symbol=r"$\mathrm{elt}$", + R18__has_usage_hint=( + "This operator is assumed be used as callable , e.g. `A_3_6 = I3240['matrix element'](A, 3, 6)`" + ), +) + + +p.end_mod() diff --git a/tests/test_data/ocse_subset/templates/agents1__template.py b/tests/test_data/ocse_subset/templates/agents1__template.py new file mode 100644 index 0000000..352c9ee --- /dev/null +++ b/tests/test_data/ocse_subset/templates/agents1__template.py @@ -0,0 +1,36 @@ +# Note: this is not the real module for this URI it is an autogenerated subset for testing + + +import pyirk as p + + +__URI__ = "irk:/ocse/0.2/agents" + +keymanager = p.KeyManager(keyseed=1239) +p.register_mod(__URI__, keymanager) +p.start_mod(__URI__) + +start_time = p.QualifierFactory(p.R48["has start time"]) +end_time = p.QualifierFactory(p.R49["has end time"]) + + +insert_entities = [ + def__create_person, + I7435["human"], + R7781["has family name"], + R7782["has given name"], + R3474["has ORCID"], + R3475["has DBLP author ID"], + I2746["Rudolf Kalman"], + R1833["has employer"], + I1342["academic institution"], + I9942["Stanford University"], + I7301["ETH Zürich"], + raw__I2746["Rudolf Kalman"].set_relation(R1833["has employer"], I9942["Stanford University"], qualifiers=[start_time("1964"), end_time("1971")]), + raw__I2746["Rudolf Kalman"].set_relation(R1833["has employer"], I7301["ETH Zürich"], qualifiers=[start_time("1973"), end_time("1997")]), + I4853["Sophus Lie"], + R6876["is named after"], + I2151["Aleksandr Lyapunov"], +] + +p.end_mod() diff --git a/tests/test_data/ocse_subset/templates/control_theory1__template.py b/tests/test_data/ocse_subset/templates/control_theory1__template.py new file mode 100644 index 0000000..41eb6bc --- /dev/null +++ b/tests/test_data/ocse_subset/templates/control_theory1__template.py @@ -0,0 +1,52 @@ +# Note: this is not the real module for this URI it is an autogenerated subset for testing +import pyirk as p + + +# noinspection PyUnresolvedReferences +from ipydex import IPS, activate_ips_on_exception # noqa + +ma = p.irkloader.load_mod_from_path("./math1.py", prefix="ma") +ag = ma.ag + + +__URI__ = "irk:/ocse/0.2/control_theory" + +keymanager = p.KeyManager() +p.register_mod(__URI__, keymanager) +p.start_mod(__URI__) + + +insert_entities = [ +I1347["Lie derivative of scalar field"], +I2928["general model representation"], +I6886["general ode state space representation"], +I4466["Systems Theory"], +I7641["general system model"], +I5948["dynamical system"], +R7641["has approximation"], +I5356["general system property"], +I4101["general time variance"], +I7733["time invariance"], +I1793["general model representation property"], +I2827["general nonlinearity"], +I6091["control affine"], +I5247["polynomial"], +I4761["linearity"], +I1898["lti"], +I2562["general property of pde"], +I2557["quasilinearity"], +I3114["semilinearity"], +I3863["linearity"], +I5236["general trajectory property"], +I7207["stability"], +I5082["local attractiveness"], +I2931["local Lyapunov stability"], +I4900["local asymptotical stability"], +I9642["local exponential stability"], +I9210["stabilizability"], +I7864["controllability"], +] + + + +p.end_mod() diff --git a/tests/test_data/ocse_subset/templates/math1__template.py b/tests/test_data/ocse_subset/templates/math1__template.py new file mode 100644 index 0000000..af0e2b6 --- /dev/null +++ b/tests/test_data/ocse_subset/templates/math1__template.py @@ -0,0 +1,77 @@ +# Note: this is not the real module for this URI it is an autogenerated subset for testing + +from typing import Union +import pyirk as p + +# noinspection PyUnresolvedReferences +from ipydex import IPS, activate_ips_on_exception # noqa + +ag = p.irkloader.load_mod_from_path("./agents1.py", prefix="ag") + +__URI__ = "irk:/ocse/0.2/math" + +keymanager = p.KeyManager() +p.register_mod(__URI__, keymanager) +p.start_mod(__URI__) + +insert_entities = [ +I4895["mathematical operator"], +raw__I4895["mathematical operator"].add_method(p.create_evaluated_mapping, "_custom_call"), +R3326["has dimension"], +I5166["vector space"], +I5167["state space"], +R5405["has associated state space"], +I1168["point in state space"], +I9923["scalar field"], +I7151["vector"], +I9841["vector field"], +I1060["general function"], +I1063["scalar function"], +I4237["monovariate rational function"], +raw__I4237["monovariate rational function"].add_method(p.create_evaluated_mapping, "_custom_call"), +I4239["abstract monovariate polynomial"], +I9904["matrix"], +I9905["zero matrix"], +R5939["has column number"], +R5938["has row number"], +I5177["matmul"], +I5000["scalar zero"], +R3033["has type of elements"], +I8133["field of numbers"], +I2738["field of complex numbers"], +I5807["sign"], +I9906["square matrix"], +I9907["definition of square matrix"], +with__I9907.scope("setting"), +with__I9907.scope("premise"), +with__I9907.scope("assertion"), +raw__I9906["square matrix"].set_relation(p.R37["has definition"], I9907["definition of square matrix"]), +I6259["sequence"], +I9739["finite scalar sequence"], +I4240["matrix polynomial"], +R5940["has characteristic polynomial"], +I3058["coefficients of characteristic polynomial"], +I3749["Cayley-Hamilton theorem"], +with__I3749["Cayley-Hamilton theorem"].scope("setting"), +# this theorem has no premise +with__I3749["Cayley-Hamilton theorem"].scope("assertion"), +I5030["variable"], +R8736["depends polyonomially on"], +I1935["polynomial matrix"], +I7765["scalar mathematical object"], +I5359["determinant"], +def__I5359_cc_pp, +raw__I5359["determinant"].add_method(I5359_cc_pp, "_custom_call_post_process"), +I6324["canonical first order monic polynomial matrix"], +def__I6324_cc_pp, +raw__I6324["canonical first order monic polynomial matrix"].add_method(I6324_cc_pp, "_custom_call_post_process"), +I1195["integer range"], +R1616["has start value"], +R1617["has stop value"], +R1618["has step value"], +I6012["integer range element"], +class__IntegerRangeElement, +I3240["matrix element"], +] + +p.end_mod() diff --git a/tests/test_data/reports/reportconf.toml b/tests/test_data/reports/reportconf.toml index 3a966e8..098197a 100644 --- a/tests/test_data/reports/reportconf.toml +++ b/tests/test_data/reports/reportconf.toml @@ -1,4 +1,4 @@ -# this file specifies the content and the metadata of a pyerk-report +# this file specifies the content and the metadata of a pyirk-report # the goal is to generate a compile-ready tex document # For an introduction to toml syntax see https://learnxinyminutes.com/docs/toml/ @@ -11,8 +11,8 @@ journal = "default" # use absolute or relative paths here # (to simplify unittests, a path beginning with `$` will be preprocessed accordingly) -ct = "$__erk-root__/erk-data-for-unittests/ocse/control_theory1.py" -ag = "$__erk-root__/erk-data-for-unittests/ocse/agents1.py" +ct = "$__irk-root__/pyirk-core/tests/test_data/ocse_subset/control_theory1.py" +ag = "$__irk-root__/pyirk-core/tests/test_data/ocse_subset/agents1.py" # use an obviously fictious author combinations @@ -21,18 +21,18 @@ item = ":ag__I2746__Rudolf_Kalman" affiliation = ":ag__I7301__ETH_Zürich" [authors.2] -item = ":ag__I8124__Carsten_Knoll" -affiliation = ":ag__I2478__TU_Dresden" +item = ":ag__I4853__Sophus_Lie" +affiliation = ":ag__I7301__ETH_Zürich" # this is wrong and only for testing [content.title] -text = "Defining basic mathematical objects in pyerk" +text = "Defining basic mathematical objects in pyirk" [content.Abstract] text=''' -This report \textit{demonstrates} how to create semi-automatically generated reports based on the pyerk-framework. +This report \textit{demonstrates} how to create semi-automatically generated reports based on the pyirk-framework. The abstract can also contain \LaTeX{} source code such as formulas $y = \frac{\x^2}{2}.$ It also serves as fixture-data to develop and test the respective software framework. ''' @@ -51,8 +51,7 @@ This is optional text for the main part. However, the main part should mainly co entities = [ ':ma__I9905["zero matrix"]', - ':ma__I9223["definition of zero matrix"]', - ':ma__I1608["identity matrix"]', - ':ma__I7169["definition of identity matrix"]', + ':ma__I9739["finite scalar sequence"]', + ':ma__I4240["matrix polynomial"]', + ':ma__R5940["has characteristic polynomial"]', ] - diff --git a/tests/test_data/test_triples1.nt b/tests/test_data/test_triples1.nt index 5af2917..4daf7d1 100644 --- a/tests/test_data/test_triples1.nt +++ b/tests/test_data/test_triples1.nt @@ -1,53 +1,53 @@ - . - . - . - . - "generic instance of I35(\"real number\")" . - . - "x2" . - . - . - . - "true"^^ . - . - . - . - "generic instance of I35(\"real number\")" . - . - . - . - . - "x0" . - "generic instance of I35(\"real number\")" . - "true"^^ . - "generic instance of I35(\"real number\")" . - "test qualifier" . - . - "x0" . - . - . - . - . - "x1" . - . - . - . - "relation1" . - . - "generic instance of I35(\"real number\")" . - "generic instance of I35(\"real number\")" . - . - . - . - "relation1" . - . - . - "x1" . - . - "test qualifier" . - . - . - . - "x2" . - . + . + . + . + . + "generic instance of I35(\"real number\")" . + . + "x2" . + . + . + . + "true"^^ . + . + . + . + "generic instance of I35(\"real number\")" . + . + . + . + . + "x0" . + "generic instance of I35(\"real number\")" . + "true"^^ . + "generic instance of I35(\"real number\")" . + "test qualifier" . + . + "x0" . + . + . + . + . + "x1" . + . + . + . + "relation1" . + . + "generic instance of I35(\"real number\")" . + "generic instance of I35(\"real number\")" . + . + . + . + "relation1" . + . + . + "x1" . + . + "test qualifier" . + . + . + . + "x2" . + . diff --git a/tests/test_data/test_zebra_triples1.nt b/tests/test_data/test_zebra_triples1.nt index 280d05b..b94b2e1 100644 --- a/tests/test_data/test_zebra_triples1.nt +++ b/tests/test_data/test_zebra_triples1.nt @@ -1,26 +1,26 @@ - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/tests/test_data/test_zebra_triples2.nt b/tests/test_data/test_zebra_triples2.nt index 97fb1f2..db9e0e2 100644 --- a/tests/test_data/test_zebra_triples2.nt +++ b/tests/test_data/test_zebra_triples2.nt @@ -1,1863 +1,1863 @@ - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person5" . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person7" . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - "0"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - . - . - . - . - . - . - . - . - "house number of person5" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "0"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - "true"^^ . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - "house number of Englishman" . - "1"^^ . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - "true"^^ . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person11" . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "2"^^ . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "2"^^ . - . - . - . - . - . - . - . - . - . - . - "0"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of Ukrainian" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - "house number of person7" . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person4" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1-tuple: ( . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - "house number of person1" . - . - . - . - "2"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - "4"^^ . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - "true"^^ . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - "house number of Japanese" . - . - . - . - . - . - . - . - . - . - . - . - . - . - "5"^^ . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - . - "house number of Japanese" . - . - . - . - . - . - "house number of person10" . - . - . - . - . - . - . - . - . - "house number of Spaniard" . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - "0"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person4" . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - "1-tuple: ( . - . - . - . - . - . - "0"^^ . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person10" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - "true"^^ . - . - . - "true"^^ . - . - . - . - . - . - . - . - "house number of Englishman" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - "true"^^ . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person11" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of Spaniard" . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - "house number of person8" . - . - . - . - . - . - . - . - . - "house number of person1" . - . - . - . - "house number of Ukrainian" . - . - . - . - . - . - . - . - . - "4"^^ . - . - "0"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - "1"^^ . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - "house number of person3" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "2"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - "5"^^ . - "house number of person8" . - . - . - . - . - . - . - . - . - . - "house number of person3" . - . - . - . - . - . - . - . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person5" . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person7" . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + "0"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + . + . + . + . + . + . + . + . + "house number of person5" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "0"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + "true"^^ . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + "house number of Englishman" . + "1"^^ . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + "true"^^ . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person11" . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "2"^^ . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "2"^^ . + . + . + . + . + . + . + . + . + . + . + "0"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of Ukrainian" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + "house number of person7" . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person4" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1-tuple: ( . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + "house number of person1" . + . + . + . + "2"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + "4"^^ . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + "true"^^ . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + "house number of Japanese" . + . + . + . + . + . + . + . + . + . + . + . + . + . + "5"^^ . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + . + "house number of Japanese" . + . + . + . + . + . + "house number of person10" . + . + . + . + . + . + . + . + . + "house number of Spaniard" . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + "0"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person4" . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + "1-tuple: ( . + . + . + . + . + . + "0"^^ . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person10" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + "true"^^ . + . + . + "true"^^ . + . + . + . + . + . + . + . + "house number of Englishman" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + "true"^^ . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person11" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of Spaniard" . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + "house number of person8" . + . + . + . + . + . + . + . + . + "house number of person1" . + . + . + . + "house number of Ukrainian" . + . + . + . + . + . + . + . + . + "4"^^ . + . + "0"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + "1"^^ . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + "house number of person3" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "2"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + "5"^^ . + "house number of person8" . + . + . + . + . + . + . + . + . + . + "house number of person3" . + . + . + . + . + . + . + . diff --git a/tests/test_data/test_zebra_triples3.nt b/tests/test_data/test_zebra_triples3.nt index c661d02..530a9ab 100644 --- a/tests/test_data/test_zebra_triples3.nt +++ b/tests/test_data/test_zebra_triples3.nt @@ -1,2502 +1,2502 @@ - . - . - . - . - . - . - . - . - . - . - "4-tuple: ( . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - "3-tuple: (2, 4, 5)" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: ( . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - "2"^^ . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person1" . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - "generic instance of I33(\"tuple\")" . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - "generic instance of I8809(\"house number\")" . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - "1-tuple: ( . - . - . - . - . - "house number of person8" . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person5" . - . - "true"^^ . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "2-tuple: ( . - "3-tuple: ( . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - "1-tuple: ( . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - "5"^^ . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - "1-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "5"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person11" . - . - . - . - . - . - . - . - . - . - . - . - . - "1-tuple: ( . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - "house number of Japanese" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - "house number of person11" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - "true"^^ . - "true"^^ . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - "3-tuple: ( . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - . - "generic instance of I8809(\"house number\")" . - "1-tuple: ( . - "generic instance of I33(\"tuple\")" . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - "5"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - "house number of person4" . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - "house number of person3" . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of Englishman" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1-tuple: ( . - . - . - . - . - . - "5"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - "1"^^ . - . - . - . - . - . - "4"^^ . - "true"^^ . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - "5"^^ . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - "4"^^ . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person1" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "5"^^ . - "true"^^ . - . - . - . - "house number of person7" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - "house number of person4" . - "5"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of Japanese" . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of Ukrainian" . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of person7" . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "5"^^ . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - "true"^^ . - "generic instance of I8809(\"house number\")" . - . - "true"^^ . - "4"^^ . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - "1-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "2-tuple: ( . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "2"^^ . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - "house number of Ukrainian" . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - "house number of person8" . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "house number of Spaniard" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - "house number of Englishman" . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - "1-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - "generic instance of I33(\"tuple\")" . - "2"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "3"^^ . - . - . - . - . - . - . - . - . - . - "1"^^ . - "true"^^ . - . - . - . - . - "generic instance of I8809(\"house number\")" . - . - . - . - "true"^^ . - . - . - . - . - . - . - . - "4-tuple: ( . - . - . - . - . - . - "house number of person3" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "4"^^ . - . - . - . - . - . - . - . - . - "1"^^ . - . - . - . - . - . - . - . - . - "4-tuple: (1, 3, 4, 5)" . - . - . - . - "house number of Spaniard" . - . - . - . - . - . - . - . - "2"^^ . - "3-tuple: (2, 4, 5)" . - . - . - . - "house number of person5" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "generic instance of I33(\"tuple\")" . - . - "generic instance of I33(\"tuple\")" . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - "1-tuple: ( . - . - . - "true"^^ . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . - . + . + . + . + . + . + . + . + . + . + . + "4-tuple: ( . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + "3-tuple: (2, 4, 5)" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: ( . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + "2"^^ . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person1" . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + "generic instance of I33(\"tuple\")" . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + "generic instance of I8809(\"house number\")" . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + "1-tuple: ( . + . + . + . + . + "house number of person8" . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person5" . + . + "true"^^ . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "2-tuple: ( . + "3-tuple: ( . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + "1-tuple: ( . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + "5"^^ . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + "1-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "5"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person11" . + . + . + . + . + . + . + . + . + . + . + . + . + "1-tuple: ( . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + "house number of Japanese" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + "house number of person11" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + "3-tuple: ( . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + . + "generic instance of I8809(\"house number\")" . + "1-tuple: ( . + "generic instance of I33(\"tuple\")" . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + "5"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + "house number of person4" . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + "house number of person3" . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of Englishman" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1-tuple: ( . + . + . + . + . + . + "5"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + "1"^^ . + . + . + . + . + . + "4"^^ . + "true"^^ . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + "5"^^ . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + "4"^^ . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person1" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "5"^^ . + "true"^^ . + . + . + . + "house number of person7" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + "house number of person4" . + "5"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of Japanese" . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of Ukrainian" . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of person7" . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "5"^^ . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + "true"^^ . + "generic instance of I8809(\"house number\")" . + . + "true"^^ . + "4"^^ . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + "1-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "2-tuple: ( . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "2"^^ . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + "house number of Ukrainian" . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + "house number of person8" . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "house number of Spaniard" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + "house number of Englishman" . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + "1-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + "generic instance of I33(\"tuple\")" . + "2"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "3"^^ . + . + . + . + . + . + . + . + . + . + "1"^^ . + "true"^^ . + . + . + . + . + "generic instance of I8809(\"house number\")" . + . + . + . + "true"^^ . + . + . + . + . + . + . + . + "4-tuple: ( . + . + . + . + . + . + "house number of person3" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "4"^^ . + . + . + . + . + . + . + . + . + "1"^^ . + . + . + . + . + . + . + . + . + "4-tuple: (1, 3, 4, 5)" . + . + . + . + "house number of Spaniard" . + . + . + . + . + . + . + . + "2"^^ . + "3-tuple: (2, 4, 5)" . + . + . + . + "house number of person5" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "generic instance of I33(\"tuple\")" . + . + "generic instance of I33(\"tuple\")" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "1-tuple: ( . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/tests/test_data/tmod0_with_errors.py b/tests/test_data/tmod0_with_errors.py index 1c6cc7b..08ab638 100644 --- a/tests/test_data/tmod0_with_errors.py +++ b/tests/test_data/tmod0_with_errors.py @@ -1,10 +1,10 @@ """ -This module serves to test the errorhandling of erkloader. +This module serves to test the errorhandling of irkloader. """ -import pyerk as p +import pyirk as p -__URI__ = "erk:/pyerk/testmodule0_with_errors" +__URI__ = "irk:/pyirk/testmodule0_with_errors" keymanager = p.KeyManager() p.register_mod(__URI__, keymanager) p.start_mod(__URI__) diff --git a/tests/test_data/tmod1.py b/tests/test_data/tmod1.py index 441ed1f..0053ff0 100644 --- a/tests/test_data/tmod1.py +++ b/tests/test_data/tmod1.py @@ -1,10 +1,10 @@ -import pyerk as p +import pyirk as p # using "foo" and "bar" here to indicate that these strings are abitrary -foo_mod = p.erkloader.load_mod_from_path("./tmod3.py", prefix="bar") +foo_mod = p.irkloader.load_mod_from_path("./tmod3.py", prefix="bar") -__URI__ = "erk:/pyerk/testmodule1" +__URI__ = "irk:/pyirk/testmodule1" keymanager = p.KeyManager() p.register_mod(__URI__, keymanager) p.start_mod(__URI__) diff --git a/tests/test_data/tmod2_with_new_items.py b/tests/test_data/tmod2_with_new_items.py index d0cecb7..e48e0f9 100644 --- a/tests/test_data/tmod2_with_new_items.py +++ b/tests/test_data/tmod2_with_new_items.py @@ -1,6 +1,6 @@ -import pyerk as p +import pyirk as p -__URI__ = "erk:/pyerk/testmodule2" +__URI__ = "irk:/pyirk/testmodule2" keymanager = p.KeyManager() p.register_mod(__URI__, keymanager) p.start_mod(__URI__) diff --git a/tests/test_data/tmod3.py b/tests/test_data/tmod3.py index 6b9cf5e..213a58f 100644 --- a/tests/test_data/tmod3.py +++ b/tests/test_data/tmod3.py @@ -1,7 +1,7 @@ -import pyerk as p +import pyirk as p import os -__URI__ = "erk:/pyerk/testmodule3" +__URI__ = "irk:/pyirk/testmodule3" keymanager = p.KeyManager() p.register_mod(__URI__, keymanager) p.start_mod(__URI__) @@ -17,7 +17,7 @@ ) -if os.getenv("PYERK_TRIGGER_TEST_EXCEPTION", "False").lower() == "true": - raise p.aux.ExcplicitlyTriggeredTestException("this exception is intended") +if os.getenv("PYIRK_TRIGGER_TEST_EXCEPTION", "False").lower() == "true": + raise p.aux.ExplicitlyTriggeredTestException("this exception is intended") p.end_mod() diff --git a/tests/test_data/zebra01.py b/tests/test_data/zebra01.py index 085dd60..f27125a 100644 --- a/tests/test_data/zebra01.py +++ b/tests/test_data/zebra01.py @@ -8,10 +8,10 @@ """ -import pyerk as p +import pyirk as p -__URI__ = "erk:/ocse/0.2/zebra01" +__URI__ = "irk:/ocse/0.2/zebra01" keymanager = p.KeyManager(keyseed=1835) p.register_mod(__URI__, keymanager) @@ -240,7 +240,7 @@ def tuple_difference_factory(self, tuple_item1, tuple_item2): """ -key reservoir created with: `pyerk -l zebra01.py ag -nk 100` +key reservoir created with: `pyirk -l zebra01.py ag -nk 100` supposed keys: I3896 R3896 I4118 R4118 diff --git a/tests/test_data/zebra02.py b/tests/test_data/zebra02.py index d2dc13f..fc5d937 100644 --- a/tests/test_data/zebra02.py +++ b/tests/test_data/zebra02.py @@ -8,13 +8,13 @@ """ -import pyerk as p +import pyirk as p -zb = p.erkloader.load_mod_from_path("./zebra_base_data.py", prefix="zb", reuse_loaded=True) -zr = p.erkloader.load_mod_from_path("./zebra_puzzle_rules.py", prefix="zr", reuse_loaded=True) +zb = p.irkloader.load_mod_from_path("./zebra_base_data.py", prefix="zb", reuse_loaded=True) +zr = p.irkloader.load_mod_from_path("./zebra_puzzle_rules.py", prefix="zr", reuse_loaded=True) -__URI__ = "erk:/ocse/0.2/zebra02" +__URI__ = "irk:/ocse/0.2/zebra02" keymanager = p.KeyManager(keyseed=1438) p.register_mod(__URI__, keymanager) @@ -165,7 +165,7 @@ """ -key reservoir created with: `pyerk -l zebra01.py ag -nk 100` +key reservoir created with: `pyirk -l zebra01.py ag -nk 100` supposed keys: diff --git a/tests/test_data/zebra_base_data.py b/tests/test_data/zebra_base_data.py index 04b29ba..bb13a38 100644 --- a/tests/test_data/zebra_base_data.py +++ b/tests/test_data/zebra_base_data.py @@ -11,10 +11,10 @@ """ -import pyerk as p +import pyirk as p -__URI__ = "erk:/ocse/0.2/zebra_base_data" +__URI__ = "irk:/ocse/0.2/zebra_base_data" keymanager = p.KeyManager(keyseed=1835) p.register_mod(__URI__, keymanager) @@ -258,7 +258,7 @@ R1__has_label="is functional activity", R2__has_description="specifies that a relation is functional in the context of the zebra puzzle", R8__has_domain_of_argument_1=p.I40["general relation"], - R11__has_range_of_result=bool, + R11__has_range_of_result=p.I53["bool"], R22__is_functional=True, R62__is_relation_property=True, ) @@ -430,7 +430,7 @@ "specifies that the subject (a relation) is the opposite of a relation with R2850__is_functional_activity=True" ), R8__has_domain_of_argument_1=p.I40["general relation"], - R11__has_range_of_result=bool, + R11__has_range_of_result=p.I53["bool"], R22__is_functional=True, R62__is_relation_property=True, ) @@ -482,7 +482,7 @@ def report(display=True, title=""): # all_humans = I7435["human"].get_inv_relations("R4", return_subj=True) # this makes this function paste-able - human = p.ds.get_entity_by_uri("erk:/ocse/0.2/zebra_base_data#I7435") + human = p.ds.get_entity_by_uri("irk:/ocse/0.2/zebra_base_data#I7435") all_humans = human.get_inv_relations("R4", return_subj=True) res = [] diff --git a/tests/test_data/zebra_puzzle_rules.py b/tests/test_data/zebra_puzzle_rules.py index 29d6b63..786710d 100644 --- a/tests/test_data/zebra_puzzle_rules.py +++ b/tests/test_data/zebra_puzzle_rules.py @@ -7,14 +7,14 @@ See https://en.wikipedia.org/wiki/Zebra_Puzzle """ -import pyerk as p +import pyirk as p from ipydex import IPS # for debugging -zb = p.erkloader.load_mod_from_path("./zebra_base_data.py", prefix="zb", reuse_loaded=True) +zb = p.irkloader.load_mod_from_path("./zebra_base_data.py", prefix="zb", reuse_loaded=True) -__URI__ = "erk:/ocse/0.2/zebra_puzzle_rules" +__URI__ = "irk:/ocse/0.2/zebra_puzzle_rules" keymanager = p.KeyManager(keyseed=1629) p.register_mod(__URI__, keymanager) @@ -158,11 +158,11 @@ cm_AND.new_rel(cm.itm1, p.R57["is placeholder"], True) cm_AND.new_condition_func(p.label_compare_method, cm.itm1, cm.itm2) - # case 2: itm1 is not a placholder (no statement) + # case 2: itm1 is not a placeholder (no statement) with cm_OR.AND() as cm_AND: cm_AND.new_condition_func(p.does_not_have_relation, cm.itm1, p.R57["is placeholder"]) - # case 3: itm1 is not a placholder (explicit statement with object `False`) + # case 3: itm1 is not a placeholder (explicit statement with object `False`) cm_OR.new_rel(cm.itm1, p.R57["is placeholder"], False, qualifiers=[p.qff_allows_alt_functional_value(True)]) # TODO: this blocks the second application because only one itm is placeholder -> introduce logical OR @@ -202,14 +202,14 @@ with I725.scope("assertion") as cm: cm.new_rel(cm.itm2, cm.rel2, cm.itm1, qualifiers=[p.qff_has_rule_ptg_mode(5)]) -txt = r"{h1} {rel1} {h2} AND {rel1} R68__is_invsere_of {rel2}." +txt = r"{h1} {rel1} {h2} AND {rel1} R68__is_inverse_of {rel2}." I725.set_relation(p.R69["has explanation text template"], txt) # ############################################################################### I730 = p.create_item( - R1__has_label="rule: deduce negative facts for neighbours", + R1__has_label="rule: deduce negative facts for neighbors", R2__has_description=("deduce some negative facts e.g. which pet a person does not own"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -243,7 +243,7 @@ I740 = p.create_item( R1__has_label="rule: deduce more negative facts from negative facts", - R2__has_description=("deduce e.g. if h1 onws dog and h1 drinks milk and h2 owns zebra then h2 drinks not milk"), + R2__has_description=("deduce e.g. if h1 owns dog and h1 drinks milk and h2 owns zebra then h2 drinks not milk"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -295,7 +295,7 @@ I750 = p.create_item( R1__has_label="rule: every human lives in one house", - R2__has_description=("human should have a house associated, create a new instance if neccessary"), + R2__has_description=("human should have a house associated, create a new instance if necessary"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -324,7 +324,7 @@ I760 = p.create_item( - R1__has_label="rule: deduce impossible house indices of neighbour", + R1__has_label="rule: deduce impossible house indices of neighbor", R2__has_description=("next to house 1 is house 2"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -349,10 +349,10 @@ cm.new_rel(cm.hn1, p.R40["has index"], cm.house_index1) -def exclude_house_numbers_for_neighbour(self, nbr_hn: p.Item, primal_house_index: int) -> p.RuleResult: +def exclude_house_numbers_for_neighbor(self, nbr_hn: p.Item, primal_house_index: int) -> p.RuleResult: """ - Given the index (int) of a (primal) house, inferre which house_number (represented by nbr_hn) are not allowed - for the neighbour + Given the index (int) of a (primal) house, infer which house_number (represented by nbr_hn) are not allowed + for the neighbor """ res = p.RuleResult() @@ -374,13 +374,13 @@ def exclude_house_numbers_for_neighbour(self, nbr_hn: p.Item, primal_house_index with I760.scope("assertion") as cm: - cm.new_consequent_func(exclude_house_numbers_for_neighbour, cm.hn2, cm.house_index1) + cm.new_consequent_func(exclude_house_numbers_for_neighbor, cm.hn2, cm.house_index1) # ############################################################################### I763 = p.create_item( - R1__has_label="rule: deduce impossible house index for left-right neighbours", - R2__has_description=("deduce impossible house indices for right neighbour"), + R1__has_label="rule: deduce impossible house index for left-right neighbors", + R2__has_description=("deduce impossible house indices for right neighbor"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -456,8 +456,8 @@ def create_none_of_tuple(self, hn_item, imp_idcs_tup): # ############################################################################### I780 = p.create_item( - R1__has_label="rule: infere from 'is none of' -> 'is one of'", - R2__has_description=("principle of exclusion, part 1: infere from 'is none of' -> 'is one of'"), + R1__has_label="rule: infer from 'is none of' -> 'is one of'", + R2__has_description=("principle of exclusion, part 1: infer from 'is none of' -> 'is one of'"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -506,8 +506,8 @@ def tuple_difference_factory(self, tuple_item1, tuple_item2): I790 = p.create_item( - R1__has_label="rule: infere from 'is one of' -> 'is same as'", - R2__has_description=("principle of exclusion part 2: infere from 'is one of' -> 'is same as of'"), + R1__has_label="rule: infer from 'is one of' -> 'is same as'", + R2__has_description=("principle of exclusion part 2: infer from 'is one of' -> 'is same as of'"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -531,7 +531,7 @@ def tuple_difference_factory(self, tuple_item1, tuple_item2): I741 = p.create_item( R1__has_label="rule: deduce more negative facts from negative facts", - R2__has_description=("deduce e.g. if h1 onws dog and h1 drinks milk and h2 owns zebra then h2 drinks not milk"), + R2__has_description=("deduce e.g. if h1 owns dog and h1 drinks milk and h2 owns zebra then h2 drinks not milk"), R4__is_instance_of=p.I41["semantic rule"], ) @@ -591,7 +591,7 @@ def tuple_difference_factory(self, tuple_item1, tuple_item2): I792 = p.create_item( R1__has_label="rule: deduce different-from-facts from negative facts", - R2__has_description=("deduce e.g. if h1 not onws dog and h2 onws dog then h2 different from h1 and vice versa."), + R2__has_description=("deduce e.g. if h1 not owns dog and h2 owns dog then h2 different from h1 and vice versa."), R4__is_instance_of=p.I41["semantic rule"], ) @@ -635,7 +635,7 @@ def tuple_difference_factory(self, tuple_item1, tuple_item2): I794 = p.create_item( - R1__has_label="rule: deduce neighbour-facts from house indices", + R1__has_label="rule: deduce neighbor-facts from house indices", R4__is_instance_of=p.I41["semantic rule"], ) @@ -677,7 +677,7 @@ def tuple_difference_factory(self, tuple_item1, tuple_item2): I796 = p.create_item( - R1__has_label="rule: deduce different-from facts for neighbour-pairs", + R1__has_label="rule: deduce different-from facts for neighbor-pairs", R4__is_instance_of=p.I41["semantic rule"], ) @@ -934,7 +934,7 @@ def add_stm_by_exclusion(self, p1, oppo_rel, not_itm1, not_itm2, not_itm3, not_i I830.cheat = [ p.ruleengine.AlgorithmicRuleApplicationWorker.hardcoded_I830, zb, p.raise_contradiction, - "{} has too many `R50__is_differnt_from` statements" + "{} has too many `R50__is_different_from` statements" ] @@ -974,7 +974,7 @@ def add_stm_by_exclusion(self, p1, oppo_rel, not_itm1, not_itm2, not_itm3, not_i cm.new_rel(cm.p5, p.R57["is placeholder"], False) with I830.scope("assertion") as cm: - cm.new_consequent_func(p.raise_contradiction, "{} has too many `R50__is_differnt_from` statements", cm.p0) + cm.new_consequent_func(p.raise_contradiction, "{} has too many `R50__is_different_from` statements", cm.p0) # ############################################################################### @@ -1027,7 +1027,7 @@ def add_stm_by_exclusion(self, p1, oppo_rel, not_itm1, not_itm2, not_itm3, not_i def exclude_houses(self, p1, nbr_hn): - # construct the neighbours of the neighbour + # construct the neighbors of the neighbor left_nbrs = [] right_nbrs = [] diff --git a/tests/test_rulebased_reasoning.py b/tests/test_rulebased_reasoning.py index 9245b21..342be88 100644 --- a/tests/test_rulebased_reasoning.py +++ b/tests/test_rulebased_reasoning.py @@ -7,10 +7,10 @@ # noinspection PyUnresolvedReferences from ipydex import IPS, activate_ips_on_exception, set_trace # noqa -import pyerk as p -import pyerk.io +import pyirk as p +import pyirk.io from addict import Addict as Container -import pyerk.reportgenerator as rgen +import pyirk.reportgenerator as rgen from .settings import ( @@ -24,12 +24,12 @@ TEST_MOD_NAME, # TEST_ACKREP_DATA_FOR_UT_PATH, TEST_BASE_URI, - HouskeeperMixin, + HousekeeperMixin, ) -class Test_01_rulebased_reasoning(HouskeeperMixin, unittest.TestCase): +class Test_01_rulebased_reasoning(HousekeeperMixin, unittest.TestCase): def setUp(self): super().setUp() @@ -41,7 +41,7 @@ def setup_data1(self): def test_c07__zebra_puzzle01(self): """ - Test one special rule I901, with new features from builin_entities + Test one special rule I901, with new features from builtin_entities """ self._apply_and__t_e_s_t__matching_rule("zb__I901") @@ -50,7 +50,7 @@ def _apply_and__t_e_s_t__matching_rule(self, rule_key, nbr_of_new_stms=1): # store relevant data in Container to evaluate further c = Container() - zb = c.zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA01, prefix="zb") + zb = c.zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA01, prefix="zb") c.rule = p.ds.get_entity_by_key_str(rule_key) @@ -69,14 +69,14 @@ def _apply_and__t_e_s_t__matching_rule(self, rule_key, nbr_of_new_stms=1): def test_c08__zebra_puzzle02(self): """ - Test one special rule I902, with new features from builin_entities + Test one special rule I902, with new features from builtin_entities """ self._apply_and__t_e_s_t__matching_rule("zb__I902") def test_c09__zebra_puzzle03(self): """ - Test one special rule I903 with new features from builin_entities + Test one special rule I903 with new features from builtin_entities """ c = self._apply_and__t_e_s_t__matching_rule("zb__I903", nbr_of_new_stms=2) @@ -94,7 +94,7 @@ def test_c10__zebra_puzzle_all_of_stage01(self): """ apply all rules of stage 01 of the zebra puzzle and assess the correctness of the result """ - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA01, prefix="zb") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA01, prefix="zb") self.assertNotEqual(zb.I4037["Englishman"].zb__R8216__drinks, zb.I7509["water"]) _ = p.ruleengine.apply_all_semantic_rules(mod_context_uri=zb.__URI__) @@ -105,7 +105,7 @@ def test_d01__zebra_puzzle_stage02(self): assess correctness of full data """ - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") # test base data self.assertEqual(zp.zb.I4037["Englishman"].zb__R8098__has_house_color.R1.value, "red") @@ -118,14 +118,14 @@ def test_d02__zebra_puzzle_stage02(self): apply rules and assess correctness of the result """ - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") - neighbour = zp.person1.zb__R2353__lives_immediately_right_of - self.assertIsNone(neighbour) + neighbor = zp.person1.zb__R2353__lives_immediately_right_of + self.assertIsNone(neighbor) res = p.ruleengine.apply_semantic_rule(zp.zr.I710, mod_context_uri=zp.__URI__) - # assert that both statemens have been created: + # assert that both statements have been created: # S(person1, p.R47["is same as"], person2) # S(person2, p.R47["is same as"], person1) counter = 0 @@ -145,8 +145,8 @@ def test_d02__zebra_puzzle_stage02(self): with p.uri_context(uri=TEST_BASE_URI): p.replace_and_unlink_entity(zp.person2, zp.person1) - neighbour = zp.person1.zb__R2353__lives_immediately_right_of - self.assertEqual(neighbour, zp.person3) + neighbor = zp.person1.zb__R2353__lives_immediately_right_of + self.assertEqual(neighbor, zp.person3) def test_d03__zebra_puzzle_stage02(self): """ @@ -288,7 +288,7 @@ def test_d04__overwrite_stm_inside_rule_scope(self): premise_stms = I702.scp__premise.get_inv_relations("R20") - # there are two premisie statements: 1.: R4, 2. R38 + # there are two premise statements: 1.: R4, 2. R38 self.assertEqual(len(premise_stms), 2) def test_d04b__zebra_puzzle_stage02(self): @@ -336,10 +336,10 @@ def test_d05__zebra_puzzle_stage02(self): apply rules and assess correctness of the result """ - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") - neighbour_before = zp.person1.zb__R2353__lives_immediately_right_of - self.assertEqual(neighbour_before, None) + neighbor_before = zp.person1.zb__R2353__lives_immediately_right_of + self.assertEqual(neighbor_before, None) res = p.ruleengine.apply_semantic_rule( zp.zr.I710["rule: identify same items via zb__R2850__is_functional_activity"], mod_context_uri=zp.__URI__ @@ -354,8 +354,8 @@ def test_d05__zebra_puzzle_stage02(self): self.assertIn((zp.person9, zp.person5), res.replacements) self.assertIn((zp.person2, zp.person1), res.replacements) - neighbour_after = zp.person1.zb__R2353__lives_immediately_right_of - self.assertEqual(neighbour_after, zp.person3) + neighbor_after = zp.person1.zb__R2353__lives_immediately_right_of + self.assertEqual(neighbor_after, zp.person3) def test_d06__zebra_puzzle_stage02(self): """ @@ -860,9 +860,9 @@ def test_d14__zebra_puzzle_stage02(self): """ match persons which have four negative statements of the same kind (test statement relations) """ - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") with p.uri_context(uri=TEST_BASE_URI): @@ -894,9 +894,9 @@ def test_d14__zebra_puzzle_stage02(self): def test_d15__zebra_puzzle_stage02(self): - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") # this will change soon self.assertNotIn(zb.I5209["red"], zp.person3.zb__R1055__has_not_house_color) @@ -909,9 +909,9 @@ def test_d15__zebra_puzzle_stage02(self): def test_d16__zebra_puzzle_stage02(self): - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") araw = p.ruleengine.AlgorithmicRuleApplicationWorker() func_act_list = p.ds.get_subjects_for_relation(zb.R2850["is functional activity"].uri, filter=True) @@ -925,9 +925,9 @@ def test_d16__zebra_puzzle_stage02(self): def test_d17__zebra_puzzle_stage02(self): - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") # get all non-placeholder etc humans h_list = p.get_instances_of(zb.I7435["human"], filter=p.is_relevant_item) @@ -939,7 +939,7 @@ def test_d17__zebra_puzzle_stage02(self): with p.uri_context(uri=TEST_BASE_URI): - zp.person1.set_mutliple_relations(p.R50["is different from"], ph_list) + zp.person1.set_multiple_relations(p.R50["is different from"], ph_list) # this does nothing because we only have 'meaningless' R50-statements res = p.ruleengine.apply_semantic_rules( @@ -947,7 +947,7 @@ def test_d17__zebra_puzzle_stage02(self): ) self.assertEqual(len(res.new_statements), 0) - zp.person1.set_mutliple_relations(p.R50["is different from"], h_list) + zp.person1.set_multiple_relations(p.R50["is different from"], h_list) with self.assertRaises(p.aux.LogicalContradiction) as err: res = p.ruleengine.apply_semantic_rules( @@ -956,7 +956,7 @@ def test_d17__zebra_puzzle_stage02(self): if res.exception: raise res.exception - msg = ' has too many `R50__is_differnt_from` statements' + msg = ' has too many `R50__is_different_from` statements' self.assertEqual(err.exception.args[0], msg) @unittest.skip("currently too slow") @@ -964,9 +964,9 @@ def test_d18__zebra_puzzle_stage02(self): """ Test HTML report """ - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") fpath = pjoin(TEST_DATA_DIR1, "test_zebra_triples2.nt") with p.uri_context(uri=TEST_BASE_URI): @@ -1003,8 +1003,8 @@ def test_e01__zebra_puzzle_stage02(self): reports = [] result_history = [] - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) # before loading the hint, we can already infer some new statements res = p.ruleengine.apply_semantic_rules( @@ -1018,8 +1018,8 @@ def test_e01__zebra_puzzle_stage02(self): self.assertIn(p.R43["is opposite of"].uri, res.rel_map) self.assertIn(p.R68["is inverse of"].uri, res.rel_map) - # load the hints and perform basic inferrence - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + # load the hints and perform basic inference + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") res_I702 = res = p.ruleengine.apply_semantic_rules( zp.zr.I701["rule: imply parent relation of a subrelation"], zp.zr.I702["rule: add reverse statement for symmetrical relations"], @@ -1027,7 +1027,7 @@ def test_e01__zebra_puzzle_stage02(self): ) result_history.append(res) - # only inferrence until now: 5 R3606["lives next to"]-statements + # only inference until now: 5 R3606["lives next to"]-statements self.assertEqual(len(res.new_statements), 5) self.assertEqual(len(res.rel_map), 1) self.assertIn(zp.zb.R3606["lives next to"].uri, res.rel_map) @@ -1065,7 +1065,7 @@ def test_e01__zebra_puzzle_stage02(self): self.assertIn(zb.R2693["is located immediately right of"].uri, res.rel_map) res_I730 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I730["rule: deduce negative facts for neighbours"], mod_context_uri=TEST_BASE_URI + zp.zr.I730["rule: deduce negative facts for neighbors"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I730")) result_history.append(res) @@ -1097,12 +1097,12 @@ def test_e01__zebra_puzzle_stage02(self): self.assertEqual(new_houses_nbr, 13) self.assertEqual(len(res.new_statements), new_houses_nbr * 2) # including placeholder-statements - # revist the example from above + # revisit the example from above self.assertNotEqual(zb.I4037["Englishman"].zb__R9040__lives_in_numbered_house, None) # apply next rule: res_I760 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I760["rule: deduce impossible house indices of neighbour"], mod_context_uri=TEST_BASE_URI + zp.zr.I760["rule: deduce impossible house indices of neighbor"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I760")) result_history.append(res) @@ -1113,7 +1113,7 @@ def test_e01__zebra_puzzle_stage02(self): # apply next rule: res_I763 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I763["rule: deduce impossible house index for left-right neighbours"], mod_context_uri=TEST_BASE_URI + zp.zr.I763["rule: deduce impossible house index for left-right neighbors"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I763")) result_history.append(res) @@ -1135,7 +1135,7 @@ def test_e01__zebra_puzzle_stage02(self): # apply next rule: res_I780 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I780["rule: infere from 'is none of' -> 'is one of'"], mod_context_uri=TEST_BASE_URI + zp.zr.I780["rule: infer from 'is none of' -> 'is one of'"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I780")) result_history.append(res) @@ -1145,7 +1145,7 @@ def test_e01__zebra_puzzle_stage02(self): # apply next rule: res_I790 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I790["rule: infere from 'is one of' -> 'is same as'"], mod_context_uri=TEST_BASE_URI + zp.zr.I790["rule: infer from 'is one of' -> 'is same as'"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I790")) result_history.append(res) @@ -1178,14 +1178,14 @@ def test_e01__zebra_puzzle_stage02(self): self.assertGreaterEqual(len(res.new_statements), 65) res_I794 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I794["rule: deduce neighbour-facts from house indices"], mod_context_uri=TEST_BASE_URI + zp.zr.I794["rule: deduce neighbor-facts from house indices"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I794")) result_history.append(res) self.assertGreaterEqual(len(res.new_statements), 2) res_I796 = res = p.ruleengine.apply_semantic_rule( - zp.zr.I796["rule: deduce different-from facts for neighbour-pairs"], mod_context_uri=TEST_BASE_URI + zp.zr.I796["rule: deduce different-from facts for neighbor-pairs"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I796")) result_history.append(res) @@ -1218,9 +1218,9 @@ def test_e02__zebra_puzzle_stage02(self): reports = [] result_history = [] - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") fpath = pjoin(TEST_DATA_DIR1, "test_zebra_triples2.nt") with p.uri_context(uri=TEST_BASE_URI): @@ -1304,7 +1304,7 @@ def test_e02__zebra_puzzle_stage02(self): result_history.append(res) res = p.ruleengine.apply_semantic_rule( - zp.zr.I760["rule: deduce impossible house indices of neighbour"], mod_context_uri=TEST_BASE_URI + zp.zr.I760["rule: deduce impossible house indices of neighbor"], mod_context_uri=TEST_BASE_URI ) # contains 2 trivial facts of non-placeholder houses, but on good fact @@ -1323,13 +1323,13 @@ def test_e02__zebra_puzzle_stage02(self): result_history.append(res) res = p.ruleengine.apply_semantic_rule( - zp.zr.I780["rule: infere from 'is none of' -> 'is one of'"], mod_context_uri=TEST_BASE_URI + zp.zr.I780["rule: infer from 'is none of' -> 'is one of'"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I780_(2)")) result_history.append(res) res = p.ruleengine.apply_semantic_rule( - zp.zr.I790["rule: infere from 'is one of' -> 'is same as'"], mod_context_uri=TEST_BASE_URI + zp.zr.I790["rule: infer from 'is one of' -> 'is same as'"], mod_context_uri=TEST_BASE_URI ) reports.append(zb.report(display=False, title="I790_(2)")) result_history.append(res) @@ -1409,9 +1409,9 @@ def test_e02__zebra_puzzle_stage02(self): @unittest.skip("currently too slow") def test_e03__zebra_puzzle_stage02(self): - zb = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") - zr = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) - zp = p.erkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") + zb = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_BASE_DATA, prefix="zb") + zr = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA_RULES, prefix="zr", reuse_loaded=True) + zp = p.irkloader.load_mod_from_path(TEST_DATA_PATH_ZEBRA02, prefix="zp") args = (zp.person8, zb.R2835["lives not in numbered house"], zb.I7582["house 2"], zb.I4735["house 3"], zb.I4785["house 4"], zb.I1383["house 5"]) @@ -1430,7 +1430,7 @@ def test_e03__zebra_puzzle_stage02(self): p.core._unlink_entity(zp.person12.uri, remove_from_mod=True) # not sure were this comes from but it has to go (disconnected artifact) - p.core._unlink_entity("erk:/local/unittest#Ia9473", remove_from_mod=True) + p.core._unlink_entity("irk:/local/unittest#Ia9473", remove_from_mod=True) all_relevant_rules = [ @@ -1440,18 +1440,18 @@ def test_e03__zebra_puzzle_stage02(self): zr.I710["rule: identify same items via zb__R2850__is_functional_activity"], zr.I720["rule: replace (some) same_as-items"], zr.I725["rule: deduce facts from inverse relations"], - zr.I730["rule: deduce negative facts for neighbours"], + zr.I730["rule: deduce negative facts for neighbors"], zr.I740["rule: deduce more negative facts from negative facts"], zr.I741["rule: deduce more negative facts from negative facts"], zr.I750["rule: every human lives in one house"], - zr.I760["rule: deduce impossible house indices of neighbour"], - zr.I763["rule: deduce impossible house index for left-right neighbours"], + zr.I760["rule: deduce impossible house indices of neighbor"], + zr.I763["rule: deduce impossible house index for left-right neighbors"], zr.I770["rule: deduce impossible house_number items from impossible indices"], - # zr.I780["rule: infere from 'is none of' -> 'is one of'"], # not useful for iterating - zr.I790["rule: infere from 'is one of' -> 'is same as'"], + # zr.I780["rule: infer from 'is none of' -> 'is one of'"], # not useful for iterating + zr.I790["rule: infer from 'is one of' -> 'is same as'"], # zr.I792["rule: deduce different-from-facts from negative facts"], # slow - zr.I794["rule: deduce neighbour-facts from house indices"], - zr.I796["rule: deduce different-from facts for neighbour-pairs"], + zr.I794["rule: deduce neighbor-facts from house indices"], + zr.I796["rule: deduce different-from facts for neighbor-pairs"], zr.I798["rule: deduce negative facts from different-from-facts"], zr.I800["rule: mark relations which are opposite of functional activities"], zr.I810["rule: deduce positive fact from 4 negative facts (hardcoded cheat)"], @@ -1478,7 +1478,7 @@ def test_e03__zebra_puzzle_stage02(self): # manually unload internal module: - p.unload_mod(hyre.contex_uri, strict=False) + p.unload_mod(hyre.context_uri, strict=False) # IPS() # WIP diff --git a/tests/test_script.py b/tests/test_script.py index 85fd95e..9d450ee 100644 --- a/tests/test_script.py +++ b/tests/test_script.py @@ -8,20 +8,20 @@ # noinspection PyUnresolvedReferences from ipydex import IPS, activate_ips_on_exception, set_trace # noqa -import pyerk as p +import pyirk as p from .settings import ( TEST_BASE_URI, TEST_DATA_DIR1, - HouskeeperMixin, - TEST_DATA_PATH2, + HousekeeperMixin, + TEST_DATA_DIR_OCSE, ) # noinspection PyPep8Naming -class Test_01_Script(HouskeeperMixin, unittest.TestCase): +class Test_01_Script(HousekeeperMixin, unittest.TestCase): def test_a01__insert_keys(self): srcpath = pjoin(TEST_DATA_DIR1, "tmod2_with_new_items.py") @@ -33,7 +33,7 @@ def test_a01__insert_keys(self): N = len(os.listdir(TEST_DATA_DIR1)) - cmd = f"pyerk --insert-keys-for-placeholders {modpath}" + cmd = f"pyirk --insert-keys-for-placeholders {modpath}" os.system(cmd) # ensure that temporary file is deleted correctly @@ -46,7 +46,7 @@ def test_a01__insert_keys(self): self.assertNotIn("I000", txt) # ensure that the module is loadable - mod = p.erkloader.load_mod_from_path(modpath, prefix="tm1") + mod = p.irkloader.load_mod_from_path(modpath, prefix="tm1") # test I000["key insertion by label"] itm3, = mod.I1000.R72__is_generally_related_to @@ -58,6 +58,20 @@ def test_a01__insert_keys(self): @unittest.skipIf(os.environ.get("CI"), "Skipping visualization test on CI to prevent graphviz-dependency") def test_c01__visualization(self): - cmd = "pyerk -vis I12" + cmd = "pyirk -vis I12" + res = os.system(cmd) + self.assertEqual(res, 0) + + @unittest.skipIf(os.environ.get("CI"), "Skipping visualization test on CI to prevent graphviz-dependency") + def test_c02__visualization_commands(self): + os.chdir(TEST_DATA_DIR_OCSE) + + self.files_to_delete.append("tmp_dot.txt") + self.files_to_delete.append("tmp.svg") + cmd = "pyirk --load-mod control_theory1.py demo -vis __all__" + res = os.system(cmd) + self.assertEqual(res, 0) + + cmd = "pyirk --load-mod control_theory1.py demo -vis ma__I9904" res = os.system(cmd) self.assertEqual(res, 0)