Quick Start Guide¶
Installation¶
Choose the method that matches your operating system:
๐ง Linux (Arch Linux)¶
zelph is available in the AUR:
paru -S zelph
๐ง Linux (Debian / Ubuntu)¶
Download the latest .deb package for your architecture from Releases and install it:
sudo apt install ./zelph_*_amd64.deb
๐ง Linux (Other Distributions)¶
Download the latest zelph-linux-x64.zip (for arm64: zelph-linux-arm64.zip) from Releases, extract it, and run the binary directly.
Alternatively, see Building zelph to compile from source.
๐ macOS (via Homebrew)¶
brew tap acrion/zelph
brew install zelph
๐ช Windows (via Chocolatey)¶
choco install zelph
Basic Usage¶
Once installed, you can run zelph in interactive mode simply by typing zelph in your terminal.
(If you downloaded a binary manually without installing, run ./zelph from the extraction directory).
A file of the same lines runs the same way, either as an argument or on standard input:
zelph my-facts.zph # runs as if the lines had been typed
zelph < my-facts.zph # the same, read from a pipe
Both are a session, so statements are echoed, derivations are printed and
inference runs after every line. A #!/usr/bin/env zelph script therefore
behaves the way it looks, and it may be called anything โ report, not
report.zph. Loading a file with .import from inside a session is the other
thing: that makes it a quiet module, see Scripts and Modules.
zelph leaves with a non-zero status when something failed, so a script can be
used in a pipeline or a Makefile.
Let's try a basic example:
Berlin "is capital of" Germany
Germany "is located in" Europe
(X "is capital of" Y, Y "is located in" Z) => (X "is located in" Z)
After entering these statements, zelph will automatically infer that Berlin is located in Europe:
(Berlin "is located in" Europe) โ {(Germany "is located in" Europe) (Berlin "is capital of" Germany)}
Note that none of the items used in the above statements are predefined, i.e. all are made known to zelph by these statements. In section Semantic Network Structure you'll find details about the core concepts, including syntactic details.
A statement may span several lines. zelph reads lines until it has a
complete statement โ a subject, a predicate and at least one object โ so a
line that stops short of that waits for the rest. Two forms are easy to type
by accident: a p (the object forgotten) and (a p b) on its own, which is a
term, i.e. a statement prefix, and is exactly how the renderer prints a
nested fact and how .explain takes its argument. Both wait, and the next
statement line is appended to them:
zelph> (a p b)
zelph> c q d
zelph> S c O
Answer: (a p b) c q
Answer: (a p b) c d
The query requests the predicate c since that is precisely what the two lines
built: the term (a p b) became the subject of a statement having the
predicate c and objects q and d.
At end of input an unfinished statement is reported (Input ends inside an
unfinished statement: (a p b)), and a .-command typed while one is pending
says which one it is.
Two values need whitespace between them. A line the parser cannot read at
all is refused rather than buffered, and the most frequent cause is a value
written against an opening parenthesis โ the f(x) of ordinary mathematical
notation:
zelph> a b x(c d e)
Error in line "a b x(c d e)": Syntax error: Could not parse statement. "x(" is a
value glued to a "(": the grammar separates two values by whitespace, so write
"x (" if a group was meant. Function notation such as "f(x)" exists only inside
a notation island -- see ".import math-syntax".
Two Statement Prefixes¶
Besides the dot-commands, two prefixes modify how a statement is read. They are not commands and take no arguments โ they attach to the statement itself.
? โ ask for a result. Most standard-library modules expose their
answer under =, which normally means asserting the request, letting the
fixpoint run, and querying the result separately. ? does all three in one
line, and keeps the inference pass quiet:
zelph> .import decimal-arithmetic
zelph> ? &12 * &34
Answer: (&12 * &34) = &408
It is repeatable: once the result fact exists, asking again answers from the graph without re-deriving anything.
: โ the self-fact prefix. Many requests are facts whose subject and
object are the same node, (T simplify T). The prefix spells that once:
:simplify T is (T simplify T), in input and in output. See
The Self-Fact Prefix.
The two combine, which is the usual way to drive the mathematical modules:
zelph> .import math
zelph> <x> ~ polyring
zelph> ? :topoly $( (x+1)^2 )
Answer: (:topoly ((x + &1) ^ &2)) = (x poly <(pos zint &1) (pos zint &2) (pos zint &1)>)
When a request has no answer, nothing is printed. Throughout the standard library that is deliberate: partiality is expressed by absence, never by a default value.
The Standard Library¶
zelph ships with a standard library of scripts. When a script given to .import is not found at the given path, zelph searches the standard library โ there, the .zph extension is optional:
.import math # the whole mathematics stack in one import
.import sparql # SPARQL query interface
.import wikidata-classes # Wikidata class hierarchy: culprits, chains, reports
.import decimal-arithmetic # rule-based arithmetic, base 10 (+ - * / mod cmp ^)
.import binary-arithmetic # the same, base 2 (full-adder/subtractor axioms)
.import binary-nand-arithmetic # the same, derived from a single NAND axiom
.import primes # primality by trial division
.import nn # neural network helpers
The three arithmetic modules are interchangeable: each claims the module ID
arithmetic via .provides, so anything built on top of arithmetic uses
whichever you imported first. See
Mathematics for the modules stacked above them.
Examples โ including every script referenced throughout this documentation โ live in the examples/ subdirectory and are addressed with their subpath:
.import examples/english
.import examples/neural/nn-wikidata-demo
Search order: $ZELPH_STDLIB (if set) โ stdlib/ next to the zelph executable โ ../share/zelph relative to the executable (e.g. /usr/share/zelph) โ /usr/local/share/zelph and /usr/share/zelph on Unix-like systems.
All installation methods on this page install the standard library automatically. The portable release archives contain it as a stdlib/ directory next to the binary โ keep the two together (or point ZELPH_STDLIB at the directory) if you relocate the binary; otherwise .import <name> cannot fall back to the library.
Note: some import/export examples read data files (e.g. taxonomy.json) from the current working directory; run those from within their examples directory or copy the data files first.
Loading and Saving Network State¶
zelph allows you to save the current network state to a binary file and load it later:
.save network.bin # Save the current network
.load network.bin # Load a previously saved network
The .load command is general-purpose:
- If the file ends with
.bin, it loads the serialized network directly (fast). - If the file ends with
.jsonor.json.bz2(a Wikidata dump), it imports the data and automatically creates a.bincache file for future loads.
Data Cleanup Commands¶
zelph provides powerful commands for targeted data removal:
-
.prune-facts <pattern>โ Removes only the matching facts (statement nodes).
Useful for deleting specific properties without affecting the entities themselves. A pattern without variables removes exactly the one fact it names; a pattern that matches nothing changes nothing. -
.prune-nodes <pattern>โ Removes matching facts and the nodes bound to the pattern's variable.
Requirements: exactly one variable (subject or a single object), fixed relation. Two variables are rejected โ the variable names what gets deleted, so there can only be one.
Warning: a deleted node takes everything it is a part of with it โ every fact naming it, every fact naming one of those, and every rule one of them is a condition or a conclusion of โ including facts and rules unrelated to the pattern, plus its names. Use with caution! -
.prune-nodes <variable> (<conditions>)โ The same, selected by a conjunction whose named variable says which bindings die.
Any number of variables and predicates is allowed there; the other conditions are the filter that selected the victims, and their own facts survive. With a transitive path condition this replaces a hand-written list of subclasses:
.prune-nodes A (A P31 C, C P279โ Q6999)removes every instance of a class at or below Q6999.
Both commands remove claims. A statement that exists only as a rule's own condition or consequence is graph structure rather than data โ queries do not answer it and .explain calls it a rule pattern โ so the prune commands leave it alone and say so. Use .node to get its ID and .remove if you really mean to delete that structure.
.cleanupโ Removes all isolated nodes and cleans name mappings. The engine's core nodes (!,nil,conjunction,negation) are exempt, since they carry no edges until something uses them.
Example:
.lang wikidata
A P31 Q8054 # Query all proteins
.prune-facts A P31 Q8054 # Remove only "instance of protein" statements
.prune-nodes A P31 Q8054 # Remove statements AND all protein nodes (with all their properties!)
.cleanup # Clean up any remaining isolated nodes
Full Command Reference¶
Type .help inside the interactive session for a complete overview, or .help <command> for details on a specific command.
Session¶
.help [command]โ Show this help or detailed help for a specific command.quitโ Exit REPL (quits zelph).licensesโ Show third-party libraries and licenses
Scripts, Loading & Saving¶
.import <script> [args...]โ Load and execute a zelph (.zph, optional) or Janet (.janet) script; falls back to the standard library.provides <id> [id2 ...]โ Claim module IDs in the import registry.load <file>โ Load a saved network (.bin) or import Wikidata JSON dump (creates .bin cache).load-partial <file|manifest> [...]โ Load selected chunks as a read-only partial view (see.help .load-partial).save <file.bin>โ Save the current network to a binary file.save-predicates <file.bin> <predicate> [...]โ Save only the facts of the given predicates (a slice; see Publishing a Predicate Slice).stat-file <file.bin>โ Show serialized-file chunk statistics without loading the network.index-file <file.bin> <json>โ Emit a JSON byte-offset index for a serialized .bin file
Languages & Names¶
.lang [code]โ Show or set current language (e.g. en, de, wikidata).name <node|id> <new_name>โ Set name in current language.name <node|id> <lang> <new_name>โ Set name in specific language.delname <node|id> [lang]โ Delete name in current language (or specified language)
Giving a node a name that another node already holds in that language merges the two, with a warning naming both โ that is how one states, after the fact, that a node written by hand and an imported entity are the same thing. A node is the hash of what it is built from, so everything built on the node that disappears is re-created under the id its new components give it, and folds into an equal fact where the graph already holds one. Core nodes are never the ones that disappear, and a variable and a non-variable cannot be merged at all.
Exploring the Network¶
.statโ Show network statistics (nodes, RAM usage, name entries, languages, rules).explain [<fact>] [depth]โ Reconstruct why a fact holds (proof tree; no arg: last output, 0 = unlimited depth); alias:.why.list <count>โ List first N existing nodes (internal map order, with details).clist <count>โ List first N nodes named in current language (sorted by ID if feasible).node [<name|id|fact>]โ Show detailed node information; defaults to last output node.out <name|id|fact> [count]โ List details of outgoing connected nodes (default 20).in <name|id|fact> [count]โ List details of incoming connected nodes (default 20).mermaid [<name|id|fact>] [depth] [max_neighbours]โ Generate a Mermaid HTML graph; defaults to last output node.list-predicate-usage [max]โ Show predicate usage statistics (top N most frequent predicates).list-predicate-value-usage <name|id|fact> [max]โ Show object/value usage statistics for a specific predicate (top N most frequent values)
Inference & Rules¶
.runโ Run full inference (from Janet:(zelph/run)).run-onceโ Run a single inference pass (from Janet:(zelph/run-once)).run-deltaโ Run inference seeded only by the facts added since the last run; costs time in the size of the addition rather than of the graph (from Janet:(zelph/run-delta), see Reasoning incrementally).run-export <file>โ Run inference and write what that run derives to a JSON Lines file (see Exporting Derivations).auto-runโ Toggle automatic execution of .run after each input; takes no argument (default: on). Auto-run is tied to processing an input line, so a program that only calls the Janet API has to run the engine itself with(zelph/run)..deductions [all|focus|quiet|off]โ Set the deduction printing mode (default: quiet).list-rulesโ List all defined inference rules.remove-rulesโ Remove all inference rules
Editing & Removing¶
.remove <name|id>โ Remove a node and everything it is a part of (destructive).prune-facts <pattern>โ Remove all facts matching the query pattern (only statements).prune-nodes <pattern>โ Remove matching facts AND all involved subject/object nodes.prune-nodes <var> (<conditions>)โ ... selected by a conjunction, deleting what<var>binds.cleanupโ Remove isolated nodes and clean name mappings (core nodes exempt).newโ Clear the complete network and re-initialize the core nodes
Clusters¶
.cluster [name]โ Show clusters, or activate one ('default' = no cluster).cluster-drop <name>โ Remove a cluster INCLUDING all nodes created in it (rollback).cluster-merge <from> <to>โ Move a cluster's membership into another ('default' = keep nodes, forget cluster)
Wikidata¶
.wikidata-constraints <json> <dir>โ Export property constraints as zelph scripts to a directory.wikidata-qualifiers <json> [P1 P2 ...]โ Import statement qualifiers from a Wikidata dump (all, or only listed qualifier properties).export-wikidata <json> <id1> [id2 ...]โ Extract exact JSON lines for Q-IDs (no import)
Engine Behaviour¶
.parallelโ Toggle parallel processing (default: on).anchors [on|off]โ Show or set anchor-based candidate lookups in unification (default: on).semi-naive [on|off|check]โ Show or set the fixpoint evaluation strategy (default: on).fact-stores [on|off]โ Show or disable the fact-path acceleration stores (memory vs. speed).contradiction-records [on|off]โ Show or disable writing each contradiction into the graph (memory vs. repeated reports)
Logging & Profiling¶
.log <max-depth>โ Enable detailed reasoning logging up to given recursion depth (0 = off, -1 = counters only).log-janetโ Toggle logging of Janet function calls (inputs/outputs).prof [reset]โ Dump reasoning profiler counters (requires .log -1 or .log N); 'reset' starts a fresh window
What's Next?¶
- Mathematics โ proving polynomial identities, symbolic differentiation, and a stack built from a single logic gate upwards
- Explore the Core Concepts to understand how zelph represents knowledge
- Learn about Rules and Inference to leverage zelph's reasoning capabilities
- Check out the Example Script for a comprehensive demonstration