rete-graph 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rete_graph-0.2.0 → rete_graph-0.2.2}/Cargo.toml +1 -1
- {rete_graph-0.2.0 → rete_graph-0.2.2}/PKG-INFO +6 -1
- {rete_graph-0.2.0/clients/python → rete_graph-0.2.2}/README.md +5 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/Cargo.lock +1 -1
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/Cargo.toml +1 -1
- {rete_graph-0.2.0 → rete_graph-0.2.2/clients/python}/README.md +5 -0
- rete_graph-0.2.2/clients/python/examples/jupyterlite-build-tutorial.ipynb +315 -0
- rete_graph-0.2.2/clients/python/examples/jupyterlite-demo.ipynb +209 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/src/lib.rs +34 -1
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/tests/test_rete_graph.py +37 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/bgp.rs +152 -2
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/block_cache.rs +4 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/dict.rs +9 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/dictionary.rs +56 -0
- rete_graph-0.2.2/crates/rete-core/src/extbuild.rs +1680 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/file.rs +18 -2
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/index.rs +47 -1
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/lib.rs +2 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/reader.rs +17 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql/eval.rs +275 -17
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/text_index.rs +28 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/sparql_integration.rs +140 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/pyproject.toml +1 -1
- {rete_graph-0.2.0 → rete_graph-0.2.2}/python/rete_graph/__init__.py +16 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/.gitignore +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/examples/tutorial.ipynb +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/src/readers.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/clients/python/tests/conftest.py +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/Cargo.toml +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/LICENSE +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/README.md +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/examples/coherence_bench.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/examples/parallel_bench.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/examples/qbench.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/geo.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/header.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/ingest.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/meta.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/parallel.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/pyramid.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/reach.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/reason.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/results.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/row.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/schema_pyramid.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/service.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/shacl.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql/aggregate.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql/expr.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql/lower.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql/path.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql/ql.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/sparql.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/terms.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/tiling.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/triples.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/src/varint.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/community_eval.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/compatibility_v1.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/fixtures/v1/minimal.rete +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/fixtures/v1/source.nq +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/properties.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/public_api.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/ranged.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/robustness.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/roundtrip.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/service_federation.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/shacl_core.rs +0 -0
- {rete_graph-0.2.0 → rete_graph-0.2.2}/crates/rete-core/tests/sparql_nondeterministic.rs +0 -0
|
@@ -14,7 +14,7 @@ members = [
|
|
|
14
14
|
# release gates (--locked checks, feature matrix, coverage floors) stay
|
|
15
15
|
# untouched by binding-only changes. Future clients (R, Go) follow the same
|
|
16
16
|
# pattern — clients/ consumes crates/, never the other way around.
|
|
17
|
-
exclude = ["fuzz", "clients/python"]
|
|
17
|
+
exclude = ["fuzz", "clients/python", "clients/r/src/rust"]
|
|
18
18
|
|
|
19
19
|
[workspace.package]
|
|
20
20
|
version = "1.0.0-rc.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rete-graph
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -43,6 +43,11 @@ CLI and the [browser playground](https://caviri.github.io/rete/playground.html).
|
|
|
43
43
|
uv pip install rete-graph # or: pip install rete-graph
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
Wheels cover Linux (x86_64/aarch64), macOS, Windows — and, from 0.2.0,
|
|
47
|
+
**browser Python**: in JupyterLite or marimo's WASM playground,
|
|
48
|
+
`%pip install rete-graph` resolves a Pyodide (PyEmscripten) wheel and remote
|
|
49
|
+
graphs are queried over sync-XHR range requests, no server anywhere.
|
|
50
|
+
|
|
46
51
|
A runnable tour with captured outputs lives in
|
|
47
52
|
[`examples/tutorial.ipynb`](examples/tutorial.ipynb).
|
|
48
53
|
|
|
@@ -15,6 +15,11 @@ CLI and the [browser playground](https://caviri.github.io/rete/playground.html).
|
|
|
15
15
|
uv pip install rete-graph # or: pip install rete-graph
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
Wheels cover Linux (x86_64/aarch64), macOS, Windows — and, from 0.2.0,
|
|
19
|
+
**browser Python**: in JupyterLite or marimo's WASM playground,
|
|
20
|
+
`%pip install rete-graph` resolves a Pyodide (PyEmscripten) wheel and remote
|
|
21
|
+
graphs are queried over sync-XHR range requests, no server anywhere.
|
|
22
|
+
|
|
18
23
|
A runnable tour with captured outputs lives in
|
|
19
24
|
[`examples/tutorial.ipynb`](examples/tutorial.ipynb).
|
|
20
25
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
name = "rete-python"
|
|
6
6
|
# Keep in lockstep with pyproject.toml's [project] version — __version__
|
|
7
7
|
# comes from here (CARGO_PKG_VERSION), the wheel metadata from pyproject.
|
|
8
|
-
version = "0.2.
|
|
8
|
+
version = "0.2.1"
|
|
9
9
|
edition = "2021"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
11
|
repository = "https://github.com/caviri/rete"
|
|
@@ -15,6 +15,11 @@ CLI and the [browser playground](https://caviri.github.io/rete/playground.html).
|
|
|
15
15
|
uv pip install rete-graph # or: pip install rete-graph
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
Wheels cover Linux (x86_64/aarch64), macOS, Windows — and, from 0.2.0,
|
|
19
|
+
**browser Python**: in JupyterLite or marimo's WASM playground,
|
|
20
|
+
`%pip install rete-graph` resolves a Pyodide (PyEmscripten) wheel and remote
|
|
21
|
+
graphs are queried over sync-XHR range requests, no server anywhere.
|
|
22
|
+
|
|
18
23
|
A runnable tour with captured outputs lives in
|
|
19
24
|
[`examples/tutorial.ipynb`](examples/tutorial.ipynb).
|
|
20
25
|
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "intro",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Anatomy of a `.rete` — build one from an rdflib graph\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"This notebook builds a **knowledge graph with a small ontology** in\n",
|
|
11
|
+
"[rdflib](https://rdflib.readthedocs.io), turns it into a **`.rete` file**,\n",
|
|
12
|
+
"and then dissects every part of that file: the **dictionary**, the **triple\n",
|
|
13
|
+
"indexes**, the **pyramid**, the **Dataset Card**, and the **embedded example\n",
|
|
14
|
+
"queries**. Everything runs in your browser tab (Pyodide + JupyterLite — no\n",
|
|
15
|
+
"server anywhere).\n",
|
|
16
|
+
"\n",
|
|
17
|
+
"Project: <https://github.com/caviri/rete> · docs:\n",
|
|
18
|
+
"[Python API](https://caviri.github.io/rete/python.html) ·\n",
|
|
19
|
+
"[format spec](https://caviri.github.io/rete/SPEC.html)"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"cell_type": "code",
|
|
24
|
+
"execution_count": null,
|
|
25
|
+
"id": "install",
|
|
26
|
+
"metadata": {},
|
|
27
|
+
"outputs": [],
|
|
28
|
+
"source": [
|
|
29
|
+
"%pip install rete-graph rdflib pandas\n",
|
|
30
|
+
"\n",
|
|
31
|
+
"import pandas as pd\n",
|
|
32
|
+
"import rdflib\n",
|
|
33
|
+
"import rete_graph as rete\n",
|
|
34
|
+
"print(f\"rete-graph {rete.__version__} · rdflib {rdflib.__version__}\")"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"cell_type": "markdown",
|
|
39
|
+
"id": "kg-md",
|
|
40
|
+
"metadata": {},
|
|
41
|
+
"source": [
|
|
42
|
+
"## 1. A small KG + ontology, in rdflib\n",
|
|
43
|
+
"\n",
|
|
44
|
+
"Eight bodies of the solar system, and — this is the part that makes the rest\n",
|
|
45
|
+
"of the notebook interesting — a tiny **ontology** on top: `Planet` and\n",
|
|
46
|
+
"`Moon` are both `rdfs:subClassOf` **`CelestialBody`**, and the `orbits`\n",
|
|
47
|
+
"property declares its domain and range. No instance is ever typed as a\n",
|
|
48
|
+
"`CelestialBody` directly; later, the reasoner will *infer* that they all are."
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"cell_type": "code",
|
|
53
|
+
"execution_count": null,
|
|
54
|
+
"id": "kg",
|
|
55
|
+
"metadata": {},
|
|
56
|
+
"outputs": [],
|
|
57
|
+
"source": [
|
|
58
|
+
"from rdflib import RDF, RDFS, XSD, Literal, Namespace\n",
|
|
59
|
+
"\n",
|
|
60
|
+
"SOL = Namespace(\"https://example.org/solar/\")\n",
|
|
61
|
+
"kg = rdflib.Graph()\n",
|
|
62
|
+
"\n",
|
|
63
|
+
"# --- the ontology (TBox) ---\n",
|
|
64
|
+
"for cls in (\"CelestialBody\", \"Planet\", \"Moon\"):\n",
|
|
65
|
+
" kg.add((SOL[cls], RDF.type, RDFS.Class))\n",
|
|
66
|
+
"kg.add((SOL.Planet, RDFS.subClassOf, SOL.CelestialBody))\n",
|
|
67
|
+
"kg.add((SOL.Moon, RDFS.subClassOf, SOL.CelestialBody))\n",
|
|
68
|
+
"kg.add((SOL.orbits, RDFS.domain, SOL.CelestialBody))\n",
|
|
69
|
+
"kg.add((SOL.orbits, RDFS.range, SOL.CelestialBody))\n",
|
|
70
|
+
"\n",
|
|
71
|
+
"# --- the data (ABox) ---\n",
|
|
72
|
+
"bodies = {\n",
|
|
73
|
+
" \"Sun\": (None, None, 695_700),\n",
|
|
74
|
+
" \"Mercury\": (\"Planet\", \"Sun\", 2_440),\n",
|
|
75
|
+
" \"Venus\": (\"Planet\", \"Sun\", 6_052),\n",
|
|
76
|
+
" \"Earth\": (\"Planet\", \"Sun\", 6_371),\n",
|
|
77
|
+
" \"Moon\": (\"Moon\", \"Earth\", 1_737),\n",
|
|
78
|
+
" \"Mars\": (\"Planet\", \"Sun\", 3_390),\n",
|
|
79
|
+
" \"Phobos\": (\"Moon\", \"Mars\", 11),\n",
|
|
80
|
+
" \"Deimos\": (\"Moon\", \"Mars\", 6),\n",
|
|
81
|
+
"}\n",
|
|
82
|
+
"for name, (cls, parent, radius) in bodies.items():\n",
|
|
83
|
+
" node = SOL[name]\n",
|
|
84
|
+
" kg.add((node, RDFS.label, Literal(name, lang=\"en\")))\n",
|
|
85
|
+
" kg.add((node, SOL.radius_km, Literal(radius, datatype=XSD.integer)))\n",
|
|
86
|
+
" if cls:\n",
|
|
87
|
+
" kg.add((node, RDF.type, SOL[cls]))\n",
|
|
88
|
+
" if parent:\n",
|
|
89
|
+
" kg.add((node, SOL.orbits, SOL[parent]))\n",
|
|
90
|
+
"\n",
|
|
91
|
+
"print(f\"{len(kg)} triples in the rdflib graph — a taste, as Turtle:\\n\")\n",
|
|
92
|
+
"print(\"\\n\".join(kg.serialize(format=\"turtle\").splitlines()[8:16]))"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"cell_type": "markdown",
|
|
97
|
+
"id": "build-md",
|
|
98
|
+
"metadata": {},
|
|
99
|
+
"source": [
|
|
100
|
+
"## 2. From rdflib to `.rete` — what the Builder assembles\n",
|
|
101
|
+
"\n",
|
|
102
|
+
"`rete.Builder()` accepts the rdflib graph directly (anything with a\n",
|
|
103
|
+
"`.serialize()` method works). Configuration is **lazy** — each call just\n",
|
|
104
|
+
"records intent — and `run()` assembles the actual file, which is built from\n",
|
|
105
|
+
"four kinds of sections:\n",
|
|
106
|
+
"\n",
|
|
107
|
+
"| Part | What it is | Why it exists |\n",
|
|
108
|
+
"|---|---|---|\n",
|
|
109
|
+
"| **Dictionary** | every distinct term (IRI, literal, bnode) stored **once**, sorted, in role-split sections | queries compare small integer ids, not strings; sorted order → prefix lookups & compression |\n",
|
|
110
|
+
"| **Triple indexes** | the id-triples in **three permutations** (SPO / POS / OSP) with zone maps | any triple pattern — `(s ? ?)`, `(? p ?)`, `(? ? o)` — becomes a *range scan*, which is also what makes lazy HTTP-range querying possible |\n",
|
|
111
|
+
"| **Pyramid** | a leveled *summary* of the graph — communities of nodes, rolled up level by level, plus the label index | \"zoom out\" views, the schema profile, and label autocomplete, all readable without touching the big indexes |\n",
|
|
112
|
+
"| **Dataset Card** | a JSON self-description in the metadata section | the file documents itself: title, license, counts, and runnable starter queries |\n",
|
|
113
|
+
"\n",
|
|
114
|
+
"For the pyramid we choose `algo=\"types\"`: **one community per `rdf:type`\n",
|
|
115
|
+
"class** — for an ontology-shaped graph like ours, the summary *is* the class\n",
|
|
116
|
+
"structure. (The default, `louvain`, clusters by connectivity instead.)"
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"cell_type": "code",
|
|
121
|
+
"execution_count": null,
|
|
122
|
+
"id": "build",
|
|
123
|
+
"metadata": {},
|
|
124
|
+
"outputs": [],
|
|
125
|
+
"source": "builder = (\n rete.Builder()\n .add(kg) # the rdflib graph, directly\n .card(\n title=\"Solar system micro-KG\",\n description=\"Eight bodies + a tiny ontology (Planet/Moon ⊑ CelestialBody), \"\n \"authored in rdflib inside a browser tab.\",\n license=\"CC0-1.0\",\n created=\"2026-07-17\",\n )\n .example(\n \"SELECT ?body ?parent WHERE { ?body <https://example.org/solar/orbits> ?parent }\",\n title=\"Who orbits whom?\",\n question=\"Which body orbits which other body?\",\n )\n .example(\n \"SELECT DISTINCT ?b WHERE { ?b a <https://example.org/solar/CelestialBody> }\",\n title=\"All celestial bodies (needs reasoning!)\",\n question=\"Which things are celestial bodies — including inferred ones?\",\n )\n .pyramid(algo=\"types\") # communities = classes\n .text_index() # opt-in full-text index\n)\n\ndata = builder.run()\nprint(f\"{len(data):,} bytes\")\nbuilder.stats"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"cell_type": "markdown",
|
|
129
|
+
"id": "dict-md",
|
|
130
|
+
"metadata": {},
|
|
131
|
+
"source": [
|
|
132
|
+
"## 3. The dictionary — terms are stored once\n",
|
|
133
|
+
"\n",
|
|
134
|
+
"Look at `stats` above: the file holds more **statements** than **terms**.\n",
|
|
135
|
+
"Every IRI like `sol:Sun` appears in many triples but is written once; each\n",
|
|
136
|
+
"triple is just three small integers pointing into the dictionary. That's the\n",
|
|
137
|
+
"same trick as HDT and column stores — and because the dictionary is sorted\n",
|
|
138
|
+
"and chunked, a remote client can fetch *only the chunks a query touches* and\n",
|
|
139
|
+
"run **prefix searches** against the label index without a full scan:"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"cell_type": "code",
|
|
144
|
+
"execution_count": null,
|
|
145
|
+
"id": "dict",
|
|
146
|
+
"metadata": {},
|
|
147
|
+
"outputs": [],
|
|
148
|
+
"source": [
|
|
149
|
+
"g = builder.graph()\n",
|
|
150
|
+
"print(f\"statements: {g.quads} · distinct terms: {g.terms}\")\n",
|
|
151
|
+
"g.prefix_search(\"M\") # label index: everything labelled M… (Moon, Mars, Mercury)"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"cell_type": "markdown",
|
|
156
|
+
"id": "pyramid-md",
|
|
157
|
+
"metadata": {},
|
|
158
|
+
"source": [
|
|
159
|
+
"## 4. The pyramid — the graph, summarized by class\n",
|
|
160
|
+
"\n",
|
|
161
|
+
"`pyramidLevels` in the build stats came from the `types` algorithm: level 0\n",
|
|
162
|
+
"groups every node under its `rdf:type` class, and the **schema profile** is\n",
|
|
163
|
+
"exactly that quotient graph — which classes exist, and how they relate\n",
|
|
164
|
+
"through predicates. A client reads this in a couple of small range requests\n",
|
|
165
|
+
"*before* deciding what to query — it's the \"map\" you see first in the\n",
|
|
166
|
+
"[playground](https://caviri.github.io/rete/playground.html):"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"cell_type": "code",
|
|
171
|
+
"execution_count": null,
|
|
172
|
+
"id": "pyramid",
|
|
173
|
+
"metadata": {},
|
|
174
|
+
"outputs": [],
|
|
175
|
+
"source": [
|
|
176
|
+
"s = g.schema()\n",
|
|
177
|
+
"display(pd.DataFrame(s[\"classes\"], columns=[\"class\", \"instances\"]))\n",
|
|
178
|
+
"pd.DataFrame(s[\"relations\"], columns=[\"subject class\", \"predicate\", \"object class\", \"count\"])"
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"cell_type": "markdown",
|
|
183
|
+
"id": "card-md",
|
|
184
|
+
"metadata": {},
|
|
185
|
+
"source": [
|
|
186
|
+
"## 5. The Dataset Card — the file documents itself\n",
|
|
187
|
+
"\n",
|
|
188
|
+
"The card travels **inside** the file's metadata section. We wrote the curated\n",
|
|
189
|
+
"fields (`title`, `description`, `license`, `created`); the counts and\n",
|
|
190
|
+
"`format_version` were stamped automatically at build time. Every client reads\n",
|
|
191
|
+
"the same card — this notebook, the CLI (`rete card solar.rete`), the\n",
|
|
192
|
+
"playground's catalog view:"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"cell_type": "code",
|
|
197
|
+
"execution_count": null,
|
|
198
|
+
"id": "card",
|
|
199
|
+
"metadata": {},
|
|
200
|
+
"outputs": [],
|
|
201
|
+
"source": [
|
|
202
|
+
"card = g.card()\n",
|
|
203
|
+
"{k: card[k] for k in (\"title\", \"description\", \"license\", \"created\",\n",
|
|
204
|
+
" \"triple_count\", \"term_count\", \"format_version\")}"
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"cell_type": "markdown",
|
|
209
|
+
"id": "examples-md",
|
|
210
|
+
"metadata": {},
|
|
211
|
+
"source": [
|
|
212
|
+
"## 6. Embedded example queries — a dataset that ships its own questions\n",
|
|
213
|
+
"\n",
|
|
214
|
+
"The two `.example()` calls landed in the card's starter-query library. Anyone\n",
|
|
215
|
+
"who opens this file — today or in ten years, in any client — can list them\n",
|
|
216
|
+
"and run them as-is:"
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"cell_type": "code",
|
|
221
|
+
"execution_count": null,
|
|
222
|
+
"id": "examples",
|
|
223
|
+
"metadata": {},
|
|
224
|
+
"outputs": [],
|
|
225
|
+
"source": [
|
|
226
|
+
"examples = g.examples()\n",
|
|
227
|
+
"display(pd.DataFrame(examples)[[\"title\", \"question\"]])\n",
|
|
228
|
+
"g.query_df(examples[0][\"sparql\"]) # \"Who orbits whom?\", straight from the file"
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"cell_type": "markdown",
|
|
233
|
+
"id": "reason-md",
|
|
234
|
+
"metadata": {},
|
|
235
|
+
"source": [
|
|
236
|
+
"## 7. The ontology at work — reasoning by query rewriting\n",
|
|
237
|
+
"\n",
|
|
238
|
+
"Remember: nothing is *asserted* to be a `CelestialBody`. A plain query finds\n",
|
|
239
|
+
"nothing — but with `reason=True` the engine rewrites the query through the\n",
|
|
240
|
+
"ontology (`Planet ⊑ CelestialBody`, `Moon ⊑ CelestialBody`, plus the\n",
|
|
241
|
+
"`orbits` domain/range), computing OWL 2 QL entailment **without\n",
|
|
242
|
+
"materializing a single new triple**. That's why it works over lazy remote\n",
|
|
243
|
+
"files too:"
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"cell_type": "code",
|
|
248
|
+
"execution_count": null,
|
|
249
|
+
"id": "reason",
|
|
250
|
+
"metadata": {},
|
|
251
|
+
"outputs": [],
|
|
252
|
+
"source": [
|
|
253
|
+
"q = examples[1][\"sparql\"] # \"All celestial bodies\" from the card\n",
|
|
254
|
+
"print(\"asserted only:\", len(g.query(q)), \"rows\")\n",
|
|
255
|
+
"print(\"with OWL 2 QL reasoning:\")\n",
|
|
256
|
+
"g.query_df(q, reason=True) # planets, moons — and the Sun, via orbits' range"
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"cell_type": "markdown",
|
|
261
|
+
"id": "export-md",
|
|
262
|
+
"metadata": {},
|
|
263
|
+
"source": [
|
|
264
|
+
"## 8. Export — one immutable file, queryable anywhere\n",
|
|
265
|
+
"\n",
|
|
266
|
+
"`export()` writes the finished file (here into the notebook's in-browser\n",
|
|
267
|
+
"filesystem — check the file browser on the left; you can download it). Host\n",
|
|
268
|
+
"it on anything that serves HTTP `Range` and it is queryable in place from\n",
|
|
269
|
+
"Python, JavaScript, the CLI, and the playground — carrying its card, its\n",
|
|
270
|
+
"examples, and its ontology with it. For datasets beyond a few million\n",
|
|
271
|
+
"triples, graduate to the streaming\n",
|
|
272
|
+
"[`rete build` CLI](https://caviri.github.io/rete/cli.html)."
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"cell_type": "code",
|
|
277
|
+
"execution_count": null,
|
|
278
|
+
"id": "export",
|
|
279
|
+
"metadata": {},
|
|
280
|
+
"outputs": [],
|
|
281
|
+
"source": "path = builder.export(\"solar.rete\")\nreopened = rete.open(path)\nprint(path, \"·\", reopened.quads, \"triples · content hash\", reopened.content_hash())"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"cell_type": "markdown",
|
|
285
|
+
"id": "outro",
|
|
286
|
+
"metadata": {},
|
|
287
|
+
"source": [
|
|
288
|
+
"---\n",
|
|
289
|
+
"\n",
|
|
290
|
+
"**Where next:** the [guided tour notebook](rete-graph.ipynb) (remote graphs,\n",
|
|
291
|
+
"lazy range reads) · [Python build tutorial](https://caviri.github.io/rete/python-build-tutorial.html) ·\n",
|
|
292
|
+
"[Dataset Cards](https://caviri.github.io/rete/dataset-cards.html) ·\n",
|
|
293
|
+
"[semantic zoom / pyramid](https://caviri.github.io/rete/semantic-zoom.html) ·\n",
|
|
294
|
+
"[reasoning](https://caviri.github.io/rete/reasoning.html)\n",
|
|
295
|
+
"\n",
|
|
296
|
+
"Source & issues: **<https://github.com/caviri/rete>**\n",
|
|
297
|
+
"\n",
|
|
298
|
+
"© 2026 Carlos Vivar Ríos — released under the\n",
|
|
299
|
+
"[Apache License 2.0](https://github.com/caviri/rete/blob/main/LICENSE)."
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
"metadata": {
|
|
304
|
+
"kernelspec": {
|
|
305
|
+
"display_name": "Python (Pyodide)",
|
|
306
|
+
"language": "python",
|
|
307
|
+
"name": "python"
|
|
308
|
+
},
|
|
309
|
+
"language_info": {
|
|
310
|
+
"name": "python"
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
"nbformat": 4,
|
|
314
|
+
"nbformat_minor": 5
|
|
315
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "intro",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# rete-graph, live in your browser\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"**Everything in this notebook runs in your browser tab.** The Python you are\n",
|
|
11
|
+
"using is [Pyodide](https://pyodide.org) (CPython compiled to WebAssembly),\n",
|
|
12
|
+
"the notebook UI is [JupyterLite](https://jupyterlite.readthedocs.io) — and\n",
|
|
13
|
+
"the package under test is [`rete-graph`](https://pypi.org/project/rete-graph/),\n",
|
|
14
|
+
"whose PyEmscripten wheel installs straight from PyPI.\n",
|
|
15
|
+
"\n",
|
|
16
|
+
"A `.rete` file is a single, immutable, **range-queryable RDF graph**: it sits\n",
|
|
17
|
+
"on plain object storage, and the client fetches only the byte ranges a SPARQL\n",
|
|
18
|
+
"query touches. No database server anywhere — not even outside this tab.\n",
|
|
19
|
+
"\n",
|
|
20
|
+
"Run the cells top to bottom (`Shift+Enter`). The first one downloads the\n",
|
|
21
|
+
"wheel (~2 MB) and pandas, so give it a moment.\n",
|
|
22
|
+
"\n",
|
|
23
|
+
"Project: <https://github.com/caviri/rete> · docs:\n",
|
|
24
|
+
"[Python API](https://caviri.github.io/rete/python.html)"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"cell_type": "code",
|
|
29
|
+
"execution_count": null,
|
|
30
|
+
"id": "install",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"outputs": [],
|
|
33
|
+
"source": "%pip install rete-graph pandas\n\nimport sys\nimport rete_graph as rete\nprint(f\"rete-graph {rete.__version__} running on platform: {sys.platform!r}\")"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"cell_type": "markdown",
|
|
37
|
+
"id": "open-md",
|
|
38
|
+
"metadata": {},
|
|
39
|
+
"source": [
|
|
40
|
+
"## Open a remote graph — lazily\n",
|
|
41
|
+
"\n",
|
|
42
|
+
"The [BOE legal graph](https://caviri.github.io/rete/playground.html) (Spanish\n",
|
|
43
|
+
"state bulletin): **447,128 triples, 6.9 MB**, hosted on Cloudflare R2. Opening\n",
|
|
44
|
+
"it fetches only the header, dictionary directory, and index directories —\n",
|
|
45
|
+
"the kernel runs in a web worker, so the client's synchronous `XMLHttpRequest`\n",
|
|
46
|
+
"range reads are allowed."
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"cell_type": "code",
|
|
51
|
+
"execution_count": null,
|
|
52
|
+
"id": "open",
|
|
53
|
+
"metadata": {},
|
|
54
|
+
"outputs": [],
|
|
55
|
+
"source": [
|
|
56
|
+
"g = rete.open(\"https://data.graphplaza.com/boe/boe.rete\")\n",
|
|
57
|
+
"s = g.stats()\n",
|
|
58
|
+
"print(f\"{g.quads:,} triples available; opened after fetching just \"\n",
|
|
59
|
+
" f\"{s['bytes']:,} of {s['fileLength']:,} bytes in {s['requests']} range requests\")"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"cell_type": "markdown",
|
|
64
|
+
"id": "query-md",
|
|
65
|
+
"metadata": {},
|
|
66
|
+
"source": [
|
|
67
|
+
"## SPARQL → pandas\n",
|
|
68
|
+
"\n",
|
|
69
|
+
"`query_df()` runs a SPARQL SELECT and hands back a **pandas DataFrame**, so\n",
|
|
70
|
+
"results render as proper tables right here. This query lists laws that cite\n",
|
|
71
|
+
"the most other laws:"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"cell_type": "code",
|
|
76
|
+
"execution_count": null,
|
|
77
|
+
"id": "query",
|
|
78
|
+
"metadata": {},
|
|
79
|
+
"outputs": [],
|
|
80
|
+
"source": [
|
|
81
|
+
"g.query_df(\"\"\"\n",
|
|
82
|
+
" SELECT ?law (COUNT(?cited) AS ?cites) WHERE {\n",
|
|
83
|
+
" ?law <http://data.europa.eu/eli/ontology#cites> ?cited\n",
|
|
84
|
+
" }\n",
|
|
85
|
+
" GROUP BY ?law ORDER BY DESC(?cites) LIMIT 10\n",
|
|
86
|
+
"\"\"\")"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"cell_type": "markdown",
|
|
91
|
+
"id": "examples-md",
|
|
92
|
+
"metadata": {},
|
|
93
|
+
"source": [
|
|
94
|
+
"## The dataset documents itself\n",
|
|
95
|
+
"\n",
|
|
96
|
+
"A `.rete` file can embed its own **Dataset Card** with a library of starter\n",
|
|
97
|
+
"queries. `examples()` reads them with one small ranged request — the table\n",
|
|
98
|
+
"below comes from *inside the file*, and every `sparql` entry is runnable:"
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"cell_type": "code",
|
|
103
|
+
"execution_count": null,
|
|
104
|
+
"id": "examples",
|
|
105
|
+
"metadata": {},
|
|
106
|
+
"outputs": [],
|
|
107
|
+
"source": [
|
|
108
|
+
"import pandas as pd\n",
|
|
109
|
+
"\n",
|
|
110
|
+
"examples = g.examples()\n",
|
|
111
|
+
"pd.DataFrame(examples)[[\"tier\", \"title\", \"question\"]]"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"cell_type": "code",
|
|
116
|
+
"execution_count": null,
|
|
117
|
+
"id": "run-example",
|
|
118
|
+
"metadata": {},
|
|
119
|
+
"outputs": [],
|
|
120
|
+
"source": [
|
|
121
|
+
"# ... so let's run the first one, exactly as shipped in the file:\n",
|
|
122
|
+
"print(examples[0][\"title\"], \"—\", examples[0][\"question\"])\n",
|
|
123
|
+
"g.query_df(examples[0][\"sparql\"])"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"cell_type": "markdown",
|
|
128
|
+
"id": "schema-md",
|
|
129
|
+
"metadata": {},
|
|
130
|
+
"source": [
|
|
131
|
+
"## What's in the graph?\n",
|
|
132
|
+
"\n",
|
|
133
|
+
"The schema profile — which classes exist and how often — as another table:"
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"cell_type": "code",
|
|
138
|
+
"execution_count": null,
|
|
139
|
+
"id": "schema",
|
|
140
|
+
"metadata": {},
|
|
141
|
+
"outputs": [],
|
|
142
|
+
"source": [
|
|
143
|
+
"pd.DataFrame(g.schema()[\"classes\"], columns=[\"class\", \"instances\"])"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"cell_type": "markdown",
|
|
148
|
+
"id": "build-md",
|
|
149
|
+
"metadata": {},
|
|
150
|
+
"source": [
|
|
151
|
+
"## Build a graph of your own — in the browser\n",
|
|
152
|
+
"\n",
|
|
153
|
+
"The builder works here too: RDF in, an immutable `.rete` image out, with a\n",
|
|
154
|
+
"Dataset Card embedded. (For big datasets, use the\n",
|
|
155
|
+
"[`rete build` CLI](https://caviri.github.io/rete/cli.html).)"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"cell_type": "code",
|
|
160
|
+
"execution_count": null,
|
|
161
|
+
"id": "build",
|
|
162
|
+
"metadata": {},
|
|
163
|
+
"outputs": [],
|
|
164
|
+
"source": [
|
|
165
|
+
"mine = (\n",
|
|
166
|
+
" rete.Builder()\n",
|
|
167
|
+
" .add('<urn:tab> <urn:runs> <urn:sparql> .\\n<urn:tab> <urn:needs> <urn:no_server> .')\n",
|
|
168
|
+
" .card(title=\"Built inside a browser tab\", license=\"CC0-1.0\")\n",
|
|
169
|
+
" .example(\"SELECT ?s ?p ?o WHERE { ?s ?p ?o }\", title=\"Everything\")\n",
|
|
170
|
+
" .graph()\n",
|
|
171
|
+
")\n",
|
|
172
|
+
"print(mine.card()[\"title\"], \"·\", mine.quads, \"triples\")\n",
|
|
173
|
+
"mine.query_df(mine.examples()[0][\"sparql\"])"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"cell_type": "markdown",
|
|
178
|
+
"id": "outro",
|
|
179
|
+
"metadata": {},
|
|
180
|
+
"source": [
|
|
181
|
+
"---\n",
|
|
182
|
+
"\n",
|
|
183
|
+
"**Where next:** the [playground](https://caviri.github.io/rete/playground.html)\n",
|
|
184
|
+
"(40+ datasets, no code) · [Python API](https://caviri.github.io/rete/python.html) ·\n",
|
|
185
|
+
"[build tutorial](https://caviri.github.io/rete/python-build-tutorial.html) ·\n",
|
|
186
|
+
"[JavaScript client](https://caviri.github.io/rete/javascript.html)\n",
|
|
187
|
+
"\n",
|
|
188
|
+
"Source & issues: **<https://github.com/caviri/rete>**\n",
|
|
189
|
+
"\n",
|
|
190
|
+
"© 2026 Carlos Vivar Ríos — released under the\n",
|
|
191
|
+
"[Apache License 2.0](https://github.com/caviri/rete/blob/main/LICENSE).\n",
|
|
192
|
+
"Datasets keep their own licenses (BOE: © Agencia Estatal Boletín Oficial del\n",
|
|
193
|
+
"Estado, [reuse conditions](https://www.boe.es/datosabiertos/))."
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
"metadata": {
|
|
198
|
+
"kernelspec": {
|
|
199
|
+
"display_name": "Python (Pyodide)",
|
|
200
|
+
"language": "python",
|
|
201
|
+
"name": "python"
|
|
202
|
+
},
|
|
203
|
+
"language_info": {
|
|
204
|
+
"name": "python"
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"nbformat": 4,
|
|
208
|
+
"nbformat_minor": 5
|
|
209
|
+
}
|
|
@@ -18,7 +18,8 @@ use pyo3::prelude::*;
|
|
|
18
18
|
use pyo3::types::PyBytes;
|
|
19
19
|
use rete_core::{
|
|
20
20
|
eval_query, eval_query_reasoned, results_envelope_json, schema_classes, schema_summary,
|
|
21
|
-
BlockCacheReader, CountingReader, RangeReader, Rete,
|
|
21
|
+
validate_shacl, BlockCacheReader, CountingReader, DataGraph, RangeReader, Rete, ReteGraph,
|
|
22
|
+
ShaclShapes, DEFAULT_BLOCK,
|
|
22
23
|
};
|
|
23
24
|
#[cfg(not(target_os = "emscripten"))]
|
|
24
25
|
use rete_core::{parse_sparql_json_results, Binding, ServiceClient};
|
|
@@ -130,6 +131,38 @@ impl Graph {
|
|
|
130
131
|
|
|
131
132
|
#[pymethods]
|
|
132
133
|
impl Graph {
|
|
134
|
+
/// Validate the graph against SHACL Core shapes written in Turtle.
|
|
135
|
+
/// Lazy-aware: over the default graph only the shapes' targets are
|
|
136
|
+
/// fetched (the index is consulted in place); a named `graph` view
|
|
137
|
+
/// materializes that graph first. `format` is "json" (default) or "ttl".
|
|
138
|
+
#[pyo3(signature = (shapes_turtle, *, graph=None, format="json"))]
|
|
139
|
+
fn shacl(
|
|
140
|
+
&self,
|
|
141
|
+
py: Python<'_>,
|
|
142
|
+
shapes_turtle: &str,
|
|
143
|
+
graph: Option<&str>,
|
|
144
|
+
format: &str,
|
|
145
|
+
) -> PyResult<String> {
|
|
146
|
+
let out = py
|
|
147
|
+
.allow_threads(|| -> Result<String, String> {
|
|
148
|
+
let shapes =
|
|
149
|
+
ShaclShapes::parse_turtle(shapes_turtle).map_err(|e| e.to_string())?;
|
|
150
|
+
let report = match graph {
|
|
151
|
+
None => validate_shacl(&ReteGraph::new(&self.rete), &shapes),
|
|
152
|
+
Some(g) => {
|
|
153
|
+
validate_shacl(&DataGraph::from_rete(&self.rete, Some(g)), &shapes)
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
Ok(match format {
|
|
157
|
+
"ttl" => report.to_turtle(),
|
|
158
|
+
_ => report.to_json(),
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
.map_err(PyRuntimeError::new_err)?;
|
|
162
|
+
self.incomplete_guard()?;
|
|
163
|
+
Ok(out)
|
|
164
|
+
}
|
|
165
|
+
|
|
133
166
|
/// Run a SPARQL query (SELECT / ASK / CONSTRUCT / DESCRIBE). Returns the
|
|
134
167
|
/// JSON envelope `{"kind": "select", "vars": [...], "rows": [...]}` etc.;
|
|
135
168
|
/// the Python wrapper parses it into values. `reason=True` turns on OWL 2
|