What you are building
In Prometheux, an ontology is not just documentation — it is a runnable model that defines entities, relationships, and the logic used to query and process data. You write that logic in Vadalog, a declarative language. For this first project, we will answer a classic question about company ownership:A company X controls a company Y if:We will model this with the conglomerate Yum! Brands and its holdings (KFC, Pizza Hut, and others), then let Prometheux compute every company Yum! controls, directly or indirectly.
- X directly owns more than 50% of Y; or
- X controls a set of companies that jointly — possibly together with X itself — own more than 50% of Y.
Declare what you want, not how to get it
Unlike traditional data processing, you do not write Vadalog as imperative steps (“first fetch this, then join that”). Instead, you declare what the output should be and let the engine do the work. For example, deciding whether a user is “engaged” (10+ events in the past 30 days) looks like this declaratively:Step 1 — Start with facts
Facts are ground truths that will eventually come from your data sources. For now, write them by hand so you can run the program immediately.owns fact reads: an owner holds a fraction of shares in a brand.
Step 2 — Model the first business rule
Rule 1 says a conglomerate controls a brand when it directly owns more than 50% of it. Think of relationships between entities as verbs —owns, controls.
controls(...), before <-) declares intent; the body (after <-)
is the implementation detail.
Step 3 — Add indirect ownership
Real ownership is rarely direct. Introduce an intermediary: Yum! owns 70% of FastFoodsGroup, which owns 45% of McDonalds, and Yum! also owns 10% of McDonalds directly.has_shares, and
a summed total, has_total_shares.
When aggregating across recursion, use the recursion-specific
aggregators (for example
msum), which keep
the running aggregation correct across each recursive call.Step 4 — The complete ontology
Putting it together, generalised over any conglomerate, and exposing the result with an@output annotation:
- The two
has_sharesrules must share the same arity. We force the first to four arguments with an anonymous variable_, so Prometheux treats them as two branches of one predicate rather than two separate predicates. controlsis recursively defined only at the level ofhas_shares, not as its own base case. Keeping the output to a single atom and recursing one level deeper avoids subtle bugs where indirect relationships more than one hop away go missing.
Step 5 — Run it and read the output
Running the program produces every control relationship the rules can derive:Why recursion matters
Because Rule 2 builds oncontrols — the very thing we are computing — the model
naturally follows ownership chains of any depth. Extend the data so Yum! and
McDonalds are several hops apart:
Connect real data
The facts above were handwritten so you could run the program right away. To bind a predicate to a real source instead, replace facts with bindings. For example, the same logic backed by PostgreSQL tables:Where to go next
- Concepts and lineage — knowledge graphs, ontologies, reasoning, and chase graphs
- Thinking in Vadalog — the full version of this example, including recursion gotchas and SQL comparisons
- Vadalog overview — language reference
- Annotations —
@output,@qbind, and more - Aggregations — recursion-aware aggregators
- Recursion — modelling recursive logic
- Examples — more end-to-end programs

