rewriteOf, sameAs, equals, and differentrewriteOf, sameAs and equals merge two ground names into one
belief-following partition, and why different still holds afterward.How two names come to denote one thing, and why the unique-name assumption survives it.
Nothing in the engine can say two names denote the same individual. Dedup is
syntactic — find-or-create-sentex keys on the canonical sentence — so Obama
and BarackObama are two individuals with two disjoint fact sets, two type sets,
two argument roots, and no way to ever connect them.
That caps the KB at hand-curated single-source data. Every real ingest pipeline produces co-referents on day one, so this is the precondition for an import path, not a refinement of one.
It also makes the most useful common-sense constraint unusable. Without equality,
(functional motherOf) plus (motherOf Tom Mary) and (motherOf Tom MrsSmith) can only
be a clash, even when they are the same woman — and functional roles
(mother, birthplace, age) are exactly where co-reference shows up.
| relation | scope | contract |
|---|---|---|
(rewriteOf P D) | any term | directional: D is deprecated, migrate off it |
(sameAs A B) | individuals, by OWL's reading | OWL: reflexive, symmetric, transitive, substitutive |
(equals A B) | any terms | equality without OWL's individual restriction |
All three feed one equivalence closure. They differ in what they say about the members, not in the classes they produce:
rewriteOf names the representative and marks the loser deprecated. Because it
is about spelling rather than identity, it applies to predicates and types as
well — which is what vocabulary alignment on import needs.sameAs follows OWL, which uses sameAs for individuals, equivalentClass for
classes and equivalentProperty for properties. Both names stay first-class;
neither is deprecated; the representative is an internal detail. No check holds a
sameAs to individuals: core.txt types both arguments thing, so (sameAs dog cat)
is stored and merges two types.equals states an identity between any two terms, with no individuals-only reading.Picking a canonical representative and rewriting to it is how OWL reasoners
implement sameAs anyway, so one mechanism serves all three.
The choice is keyed on content, never on a handle or on arrival order (nmtms.md), since handles are allocated in assertion order.
rewriteOf chain wins. Chains compose: rewriteOf A B and rewriteOf B C make A the representative of all three, and with (rewriteOf A B) and (sameAs B Aardvark) the representative is A, though Aardvark sorts first.solve/content-key.A preference is per supporter, not per edge. One edge may be supported by a sameAs
and a rewriteOf at once, and disbelieving the rewriteOf withdraws the deprecation
while the merge stands on the sameAs.
wff refuses a rewriteOf self-edge, (rewriteOf A A), and an edge closing a cycle over
the active preferences, found by reachability, as it refuses a genl cycle. (sameAs A A) is accepted, since OWL makes sameAs reflexive. The taxonomy's election
(tax/class-rep) stays total, because wff runs above the taxonomy: a cycle falls
back to the smallest preferred term, then to the smallest member, and the closure never
returns nil for a term it holds.
OWL drops UNA. We do not. (different X Y) is provable exactly when the
arguments lie in no shared equivalence class — so distinct symbols denote
distinct individuals until an equality sentex says otherwise.
One carve-out: an unmerged indeterminate term — a skolem constant, or a
believed member of the extensible indeterminate_term category — suspends the
UNA for itself. It stands for some object without determining which, so
neither equals nor different is provable between it and a determinate term
until a rewriteOf or merge identifies it, at which point its representative moves
off it and the exemption lifts (provers.clj, pairwise-distinct?). The category,
what it does to same-class?, and the stratification edge it adds are in
predall.md.
A rule guarded by different is re-checked when either input moves. Both a merge and an
indeterminacy declaration withdraw the firing they licensed, and retracting one brings it
back, so the guard is order-independent though the prover can name no handle for a
justification to carry (predall.md).
This is negation as failure over the equality closure, and it is what keeps
counting meaningful: counting distinct symbols stays correct except for terms
somebody explicitly merged, so count-with-arg and friends do not acquire a
blanket caveat.
different is:
(different A B C) asserts the arguments are pairwise
distinct.(different ?x Y) would enumerate every term in the KB that is not Y, so it is
refused rather than answered explosively.(different A) asserts nothing and (different) is meaningless.Two contract details the closure must settle, because different reads them:
same-class? is reflexive everywhere, and an unmerged symbol is different from
every other unmerged symbol. The prover answers (different A A) false before it
reads the closure.wff refuses a compound argument
to all three relations, except a rewriteOf NAT declaration (nat.md) and a
schematic equals (equational.md) (wff/equality-problems). different,
though, may compare ground compounds: it normalizes each argument by replacing
its symbols with their representatives and compares the results, which is
congruence-consistent and needs no theory.Because it consumes bindings rather than producing them, different is a
deferred literal, the same class as lessThan / greaterThan / evaluate.
sentex/canonicalize-rule keeps that class in the author's order
(sentex/deferred-literal?), because a deferred literal's position is operational
rather than logical. The generators are sorted by sentex/cmp-blind, which never sees a
deferred literal and ignores variable names, so two spellings of one rule that differ in
?x against ?a canonicalize together.
A believed equation is answered by every read, and three rules make that hold.
rewriteOf / sameAs / equals (kb/rewritable-sentex?), so the stated spelling is
the only one there will ever be. res/retired-for? therefore never drops one: the
per-reader filter that hides a displaced spelling (res/without-retired) would
otherwise hide (sameAs A B) from every reader that sees the merge it states, and no
read would return it under any name. An equality relation nested inside a sentence,
(not (sameAs A B)) say, is read through the mention walk, since its arguments move
by a spelling rename and not by an identity merge.different goal
(res/goal-held-as-spelled?). Its arguments are mentions, and the goal rewrite would
turn (rewriteOf P D) into the self-edge (rewriteOf P P), which matches nothing.sameAs and equals goals are answered from the closure by
provers/EqualityProver, scoped to the asking context the way same-class? is. A
ground goal holds when both sides reach one normal form (congruence, then the visible
schematic equations), so the stated form, its symmetric form, the reflexive form and a
form joining two ends of a chain all answer true. A goal binding a symbol on one side
and a variable on the other enumerates that symbol's class, itself included.Three shapes are not answered from the closure, and each is left to the stored
equations. A goal open on both sides asks for every pair over the domain, reflexive
pairs included, so it returns the stated equations only. A goal binding a compound
beside a variable asks for every term that normalizes with it, which is E-unification
("What is not built" below). And rewriteOf is directional: (rewriteOf P D) is
answered by the stored edge, its converse and the self-edge are not, and a chain does
not compose into a stated edge, since the preference it records is per edge.
sentexes-matching is a storage read, so it returns the stated equation as spelled and
nothing else: the symmetric form of an equation stated one way round is not a stored
sentex. prove takes the stored facts as its leaf, so it proves the stated spelling;
the closure's other answers come through ask, or through prove run with a registry
leaf (inference.md).
rewriteOf, sameAs and equals are in the baseline forced-monotonic set,
so on every KB, upper layer or not, an equation premise is never the loser of a nogood whatever
strength it was written at (nmtms.md), and no
placed defeat hides a merge (reference.md, decision 13). A denial of a stored equation premise loses to it
at either strength, and no denial undoes a merge or blocks a rewrite.
Four parts, all reusing machinery that already exists:
Migrate. find-sentexes returns every sentex containing a term some reader
retires, at any nesting depth, in one index lookup. Each gets a rewritten twin
under the representative, derived and justified by [the original sentex, the equality sentex]. Dedup falls out: when the rewritten form already exists,
find-or-create returns that handle and it simply gains a second justification.
One twin per reader whose election differs, placed where that reader lives —
"Scope, context, and re-election" below.
Supersede the original. The stale spelling stays stored but is not believed and does not match. Handles that callers already hold stay valid.
Supersession is TMS state of its own, jtms's :superseded map, datum -> {old-term representative}. exceptWhen's :blocked holds justification ids, and
a directly asserted (bornIn Dep Chicago) is a premise with no justification,
which relabel holds IN unconditionally. :superseded subtracts reported belief
(in?, in-datums) and leaves the label alone
(defenses.md), and why-not
reports the datum :superseded. It is read from the sentex's own context, the
context migration writes it in.
A superseded spelling does not fire a forward rule, and it is the one
unbelieved datum that does not — a defeated default still fires, because a defeat is
a label and the conclusion drawn off it is labelled OUT with it and revives with it
(nmtms.md). Supersession is not a label. It subtracts reported belief
with no relabel behind it, so a conclusion drawn off a retired spelling would stand
believed under a name no read asks after: the same three sentences concluding once
where the merge preceded the fact and twice where it followed. The restatement is on
the chaining agenda beside the retired spelling, so the firing is made once at the
elected name; and when the merge goes away the spelling comes back through settle's
un-merge channel and fires then. chain/process-datum is where that supersession check sits.
A firing made over the spelling before the merge arrived is withdrawn at the
merge, so the store holds the firings the merge-first order holds. With (pp Zz),
(not (pp Aa)) known-true, (pp ?x) => (rr ?x) and (rewriteOf Aa Zz), the firing
over (pp Zz) would otherwise leave (rr Aa) justified by [(rr Zz) (rewriteOf Aa Zz)], a restatement no defeat names, and (rr Aa) believed in the merge-last orders
only. The settle drops the firing (nmtms.md),
and (rr Aa) is not believed in any of the 24 orders.
An argument-type mint (argtypes.md) is drawn over a superseded
spelling, and the mint is spelled at the retired term. special/restate-retired-mint
restates the mint as it is stored, as assert restates a fact naming a retired term,
so the mint is superseded and its twin is believed at the elected name. A merge that
arrives after the mint migrates the mint with the fact, and every order believes the
same membership
(argtype_entail_test/a-mint-over-a-retired-spelling-is-believed-at-the-elected-name-in-every-order).
Rewrite goals. A query naming a non-representative is rewritten before
lookup, since its own sentexes are no longer believed. handle-of and the lookup
levels do not rewrite, since they answer about storage.
Queue the re-check. Steps 2 and 3 are exactly what a closed-world condition —
an exceptWhen query, an unknown, an aggregate's census — cannot see coming. A
retired spelling leaves no fact arriving or departing on its predicate, and a
rewritten goal changes an answer with nothing on the queried predicate moving at
all, so neither the predicate keying nor the firing keying that narrows the taxonomy
triggers can reach it. special/recheck-equality-edge therefore queues every rule
carrying such a condition, both when the closure grows and when it splits, and
chain/settled-bindings rewrites the firing's stored bindings before the condition
is re-evaluated — a justification records what matched when it fired, and a merge
does not go back and edit it. exceptions.md is where that channel
sits beside the other three.
different is exempt from step 3. Rewriting a different goal would map every
argument to its class representative, so a merged pair would compare equal — and
since the whole job of different is to read class membership, every goal would
come out false the moment anything merged. Its arguments are already
rewrite-invariant: the prover consults the closure directly. Exempt it explicitly
rather than relying on the rewrite being a no-op.
A positive equation is exempt from step 3 too, for the reason "Answering an equation" gives: its arguments are mentions and the stored equation is never restated, so the goal meets it only as spelled.
Dropping the equality invalidates the derivations, the dependency-directed sweep collects the twins, and un-superseding revives the originals.
The reconcile in step 2 runs on the write path. An entry reads the displaced
datum's record, the class its terms belong to, the rewrite rules, the genlCx ancestor
set and whether the restatement is stored. The equality relations, the rewrite rules and
genlCx are forced monotonic, so an entry moves only when a sentence is stored or
removed, or an except moves, and never with a settle's belief: a merge rests on
:monotonic evidence alone (reference.md, decisions 6 and 13)
and is undone only by retracting a premise it rests on. Re-examining an entry is a record
fetch, a rewrite through the closure and a store probe for the restatement, and the
standing displaced set is not a small fixed thing: owl:sameAs is what an RDF import
emits in quantity, so a pass over the whole set per write is one probe per standing merge.
So each write reconciles what it moved. A migration hands over the entries it restated,
and migrate-class walks the whole class an edge moved, so a merge arriving is described
by its own output. A sentence leaving the store reconciles at the removal choke point
(special/reconcile-removed-supersession!): a displaced datum leaving drops its entry,
and an equality leaving names the classes it moved term by term
(tax/take-equality-moves!), so an un-merge re-examines the stored sentexes naming a term
of that class and no other entry. A genlCx edge, stored or removed, and a schematic
rewrite rule removed can retire an entry no class move names — a context edge changes
which merges a reader can see, and a rule leaving re-normalizes every sentence it reached
— so each re-examines the whole set while any spelling is superseded, and recover does
too. The settle reconciles only after an except moved (the data it reaches,
special/except-move-region), and after an equality edge moved in belief. A settle
that moved no equality premise calls no reconcile.
A supporter believed by a relabel migrates as one that arrived believed. A firing
held void stores its equality OUT
(taxonomy.md), so its
arrival restates nothing. A forced-monotonic declaration that releases the firing, or a valid
firing of the stored conclusion, makes it believed with no arrival. The partition
reconcile records each supporter it moves from disbelieved to believed
(tax/take-believed-again!), and settle-finish runs the arrival's re-check and
migration over that supporter's class (special/believed-again-sweeps). The KB then
stores the twins the order with the valid firing first stores.
settle-finish then reconciles the taxonomy's caches over the region and the data whose
supersession changed since the last settle (special/take-supersession-moves!), since a
supersession flip moves belief with no relabel behind it.
An equality applies where it is visible. A merge asserted in CxUniverse
applies everywhere; one asserted in a context applies there and below, by the
ordinary genlCx up-closure. That answers "is equality global or scoped" the
same way everything else in the engine answers it, and it is what lets a story
merge two characters without leaking into an unrelated one.
That holds on the question as much as on the answer. kb/rewrite-goal takes the
goal's context and rewrites only by the merges it can see; the class reads
(representative / same-class? / equiv-class / deprecated?) take one too, and
different reads the scoped partition, since the unique-name assumption is what a
context holds until it is told otherwise. Scoping migration alone would be worse
than scoping nothing: the goal would be renamed to a spelling migration correctly
declined to create, and the asking context would lose a fact it still believes, under
either name. An invisible edge can split a class, so the scoped read is its own
election over the visible edges rather than a filter of the global answer
(docs/taxonomy.md).
Which makes the twin's placement the hard half, because a fact and the merge that restates it need not live in the same context, and the party whose spelling is at stake is neither of them — it is whoever reads the fact, which is any context below it, each electing over a different set of visible edges.
Two shapes make the point, both with Leaf under Mid:
(likes Tom Bravo) in Mid, (rewriteOf Alpha Bravo) in Leaf. Mid has been told nothing and keeps its spelling. Leaf sees
both, so Leaf elects Alpha — and a context that can see a fact has to be able to
ask for it. One normal form computed at the fact's context serves Mid and leaves
Leaf asking under two names that both miss.(rewriteOf Bravo Charlie) in Mid and
(rewriteOf Alpha Bravo) in Leaf put Mid and Leaf on different heads of one
rewriteOf chain: Mid elects Bravo, Leaf elects Alpha. One normal form
computed globally serves Leaf and hands Mid a spelling only Leaf's edge
produces — while Mid's own fact, superseded, is retrievable under nothing.So migration runs once per reader whose election differs, and each twin is placed
in the context that elected it. The readers are the fact's context closed under where
it meets the equalities' — tax/meet-closure, the same enumeration a qualitative
network takes over the contexts holding its facts (qcn.md) and for the same
reason: knowledge stated in several contexts is read by whoever inherits some
combination of them, and which combination changes the answer. Two readers electing the
same form share one twin, at the more general of them; a reader that changes nothing
costs one rewrite. The candidate set is the whole class, not the edges incident on
the sentence's own terms — chain composition means an edge touching nothing in the
sentence still moves what its terms rewrite to.
A reader can retire the KB's own representative. (sameAs Pa Qb) and (rewriteOf Qb Pa) elect Qb; a reader that sees the sameAs and not the rewriteOf, because the
rewriteOf is stated in a context it does not inherit or excepted in it, elects Pa. So
a sweep over a class (migrate-class, migrate-under-context-edge, an except moving)
walks the representative's facts too when a reader can retire it (tax/retirable?): a
rewriteOf names it preferred and the class holds a supporter that is not such a claim. A
representative no rewriteOf names preferred is the smallest member, which every reader's
election keeps, so a class merged by sameAs and equals alone walks its other members
only.
The fact's own context is always a reader, and it is the one that supersedes: a reader below it restates the fact for itself and leaves the original believed where it lives, because that context has been told nothing.
Which leaves supersession short by one, and the shortfall is a read filter.
jtms/superseded is per datum — a sentex lives in one context, so one flag answers
for one context — while staleness is per reader. Leaf inheriting Mid's
un-superseded spelling alongside the twin it elected would report one fact twice, under
two names it knows denote one thing, and every count over the answer set would double.
res/without-retired drops the matches whose stored spelling the asking context has
retired, beside the except filter that already removes what a context may not see.
It runs only when the closure is non-empty, so a KB that has merged nothing pays one
set-empty test per query.
genlCx edge is a third arrival orderAn equality applies where it is visible, so the genlCx ancestor set decides what a merge
restates as much as the closure does — and the ancestor set is knowledge that arrives in its own
time. (equals Tom Thomas) in Up, (mammal Tom) in Low, and (genlCx Low Up) are
three sentences that must yield one KB in all six orders, and the edge is the ingredient
whose arrival nothing keyed on: migrate-class covers the merge arriving last and the
assert path's migrate-sentex covers the fact arriving last, while a supersession is
only ever dropped or restated by the reconcile and so cannot write a restatement that
was never made. With the edge last, Low would keep the spelling it stored the fact
under while every read from Low asks after Thomas — a sentex believed and answering
no query, under either name.
special/migrate-under-context-edge closes that, and it is the equality twin of the
genlCx seeding forward chaining takes (inference.md). Both ancestor sets,
because the whole of the new reachability is that a reader in context-down(sub) now
sees context-up(super): a reader, a fact and a merge form a new triple only if the
reader newly reached one of the two, which puts that one in super's ancestor set and the
reader in sub's descendant set — a merge above meeting the facts the widened readers
already saw, and a fact above meeting the merges they already saw. It is enumerated
from the merges, not from the ancestor set: the candidates are the stored sentexes naming a
term one of those merges displaces, which the inverted term index answers in one lookup
per term, so the cost is proportional to the standing merges and to what they reach
rather than to how much ontology the ancestor set holds. A KB that has merged nothing pays one
set-empty test, and each half runs only when the other side holds a merge the reader can
see, so wiring a context under one whose merges it already inherits enumerates nothing.
A derived edge runs the same arm from chain/place-fact-conclusion, so which
spelling a context reads a fact under does not depend on whether the context edge was written
or inferred.
The removal side needs no twin of this: dropping an edge narrows what a reader sees, and
a twin names, for each term its reader's election displaces, every equality on one path
from the term to its representative (tax/scoped-path-supporters), one justification per
choice of supporter along the path, and the genlCx edges its reader sees the original
and each equality through (special/migrate-into). The ordinary dependency-directed
sweep collects one whose merge, link or original the reader can no longer see,
rederive-descended restates it again over a surviving path, and
refresh-supersessions hands the spelling back.
A late rewriteOf can move a representative, and that means re-migration.
(sameAs A B) elects a representative lexicographically; a later (rewriteOf B A)
names B preferred and re-elects it. Everything already migrated to A migrates
again: migrate-class walks the whole class an edge moved, so a re-election
re-migrates with no separate code path.
Congruence — if a = b then any term containing a equals the same term with b
substituted — has no algorithm of its own here. The inverted term index
locates a term at any nesting depth, and migration rewrites it there, so
merging performs congruence closure eagerly over all ground content. No congruence
algorithm is written, because the index already answers the question it would ask.
One position is exempt: a mention. A term named as syntax, rather than one the
sentence refers with, does not fold onto its referent's class — the arguments of a quoting
function (Quote and Quasiquote, fixed by the engine and declared by nothing), and the
proposition a modal_predicate attributes to its agent. (believes Oedipus (marriedTo Oedipus Jocasta)) is not rewritten by a sameAs the asker holds, because an attitude is
opaque and the asker's identities are not the agent's; the agent's own merges do rewrite
it, where the projection reads them. A rewriteOf spelling rename reaches into a
mention either way, since it retires a name rather than merging referents. The rule and
both halves of what it provides are belief.md, "Opacity: the proposition is a
mention"; the exemption is in the congruence walk itself, so migration and query hold it
alike.
functional infers equality instead of throwing(functional P) plus two different symbols for the same first argument, both
facts :monotonic, derives (equals V1 V2), with antecedents [both facts, the functional declaration]. Every other collision is a nogood the settle decides: two
numbers, two strings or a compound, because no merge can make two numbers one thing, and
a pair with a :default fact, because a merge is never defeated and is undone only by
retracting a premise it rests on, so it rests on :monotonic evidence alone
(reference.md, decisions 6 and 13). The settle places the
defeat of the unique :default fact of such a pair, and two :default facts are a
dilemma. A defeasible identity is
written with a predicate that does not merge.
The merge follows each member's class, not only its arrival. A member's class moves
under an unchanged label when a justification of it arrives or leaves: a :monotonic rule
firing over a :default fact, or that firing's premise retracted. At the start of each
settle pass special/class-moved-merges re-asks the collisions of the relabelled handles
(jtms/touched) whose class can have moved, conditional on the marks as the write path is. A
member IN at :monotonic goes back through the write-path derivation, so the merge is the
one the member would have met arriving at that class. A collision justification with a
member IN at :default is dropped, as retracting that member drops it, and an equality
with another justification stands on it. A settle whose own defeats stand when a merge
moves settles again from no defeat (settle/*unmerged-sink*). core/preview keeps every
handle, so it leaves a dropped merge standing.
(functionalInArg P n) is the same machinery with the determined position named
rather than fixed at argument 2 (taxonomy.md). Everything in this section
holds of it unchanged: the same merge-or-nogood rule, the same four arrival directions, the
same justification shape. Two differences matter. The clash the checks hand
back carries the position — with the generalized mark a predicate may be constrained
at more than one position at once, and two slots of one sentex are two different incoming
fillers — so the filler is read at the clash's position (checks/functional-filler),
not as argument 2. And the merge rests
on every declaration constraining that position, both spellings unioned
(checks/functional-declaration-supporters), so a predicate carrying (functional P) and
(functionalInArg P 2) keeps its merge when either one is retracted — the same rule two
(functional P) sentexes in different contexts follow, applied one level up
(defenses.md).
The merge derives equals, not sameAs, because a functional value need not be an
individual.
The fluent lane has its own counterpart. A value carried under initiates never becomes a
bare literal, so this closure never sees it; (functional_at_instant F) states that F has
at most one value for one subject at a single instant, and
vaelii.core/functional-at-instant-violations reports a moment carrying two — a merge for
two symbols, a contradiction otherwise. It reports rather than merges: whether two fluents
overlap at an instant follows from the clipping closure and is read on demand, the shape
specified-violations uses. See time.md.
The merge is a justified derivation: why names the declaration and the two facts, and
retracting any one of them un-merges. One wrong functional declaration merges two
individuals across the KB, and the justification makes that merge inspectable and
reversible.
The mark is read up the predicate hierarchy, since two fatherOf mothers for one
child are two parentOf values and (functional parentOf) is what says a child has one
(taxonomy.md). The slot is probed at the marked predicate — (parentOf a ?v) finds a filler written either way through the matcher's fan, where (fatherOf a ?v)
would miss one written at the general spelling — and the merge then rests on the
subsumption as well, so the genl edges both members descended through join the
declarations of the mark that convicted the pair (via) in the antecedents.
Retracting the edge un-merges, exactly as retracting the declaration does; without them
two names would stay merged on a declaration that no longer reaches either of them.
Four directions, because a declaration reaches the facts already stored exactly as it
reaches the facts that follow, and so does either edge that can bring a slot's fillers
into view.
special/derive-functional-equalities is a fact meeting the declaration — it runs on
every asserted fact and on every derived conclusion — special/equate-existing is the
declaration meeting the facts, sweeping the predicate extents of P's whole genl spec
subtree when (functional P) itself arrives, special/equate-under-edge is the genl
edge meeting both, sweeping the arriving (genl sub super)'s own subtree when a mark
stands at or above super, and
special/equate-under-context-edge is the fourth: a genlCx edge meeting both, over the
context ancestor set rather than the predicate one. It has no subtree of its own to sweep — a
context edge changes no predicate and no fact, only which contexts see one another — so
what it sweeps is the ancestor set the edge widens: every context the readers of sub can
see once the edge integrates, which is the same reachability migrate-under-context-edge
computes for the same trigger. It keeps the stored facts there whose functor a mark
could reach, up to tax/*exposure-instance-budget* candidates (below), and hands each
to special/derive-functional-equalities at its own storage context, which sweeps
every reader below that context (context-down), the joining context the edge connected
among them. Two blind sibling contexts
each holding one of two clashing fillers derive nothing until both of the edges wiring
some context under both of them have landed — visibility is the union of every edge that
grants it, not the effect of the latest one alone. Written the
ordinary way — declaration first — the sweep finds an empty extent and costs one root
read.
This one direction is budgeted, unlike the other three. A genl edge's subtree is
bounded by the vocabulary under the predicate the edge names —
where a genlCx edge names two contexts, and the ancestor set they open can be large even when
the edge itself is small: a context wired under a large store exposes all of it. equate-under-context-edge caps its candidates at
tax/*exposure-instance-budget* (shared with vaelii.impl.settle's own bounded sweeps,
so one dial governs every cross-context sweep in the KB) and, past it, files a
:context-edge-exposure-truncated violation — never silently. Unlike a cut sweep in
settle, whose residual a later settle's resumed sweep re-examines, a merge this cap
prevents has no second chance: nothing re-triggers on a genlCx edge that already
finished landing, so the pairs past the cut stay unmerged for good, this edge.
All three run for an edge nobody asserted. A genlCx edge reaches the store by
three entry points: asserted (assert-entry/assert-one), concluded by a rule
(chain/place-fact-conclusion), or computed by the structural producer off a
contextArgSubrelation declaration (context-nat.md). The settle's
drain runs migrate-under-context-edge, equate-under-context-edge and
antisym-equate-under-context-edge for every genlCx move, whichever entry point stored
the edge (contexts.md), and the producer settles once it
has built an edge. A calendar month→year edge therefore merges two fillers of one
functional slot it makes jointly visible, whether the year's fact was written before or
after the month existed.
Past that cap, and only past it, order independence is a residual rather than a
guarantee. The ancestor set is enumerated in handle order — assertion order — and the budget
takes a prefix of it, so which merges a cut edge derives depends on when the facts
arrived. Sorting the ancestor set to content order would remove that and is refused for a
measured reason: the sort forces the whole extent, which is the cost the cap exists to
refuse, and a context cycle makes the ancestor set the graph (taxonomy.md has the
measurement). So the residual is left, and left named — the violation is filed on
every cut, so no reader has to infer completeness from silence. Below the cap, which is
every KB that has not put more than *exposure-instance-budget* marked facts in one
ancestor set, the sweep is exact and the invariant holds outright.
The four directions hand each pair to one derivation, so the equality names both facts
and the declaration whichever way round it was reached, and retracting any of the direct
antecedents un-merges. Re-deriving is idempotent — same-class? skips a pair the closure already holds and
has-justification? skips an argument it already has — so a slot filled by three values
collapses to one class rather than to the first pair walked. The sweep reads what is
stored rather than what is believed, for entail-existing's reason: an equality
derived off a defeated fact rests on that fact and is defeated with it, where skipping it
would leave the merge missing when the fact revives.
A mark that revives runs its arrival sweep again. A defeat of the declaration or of a
genl edge the merge descended through takes every merge it licensed OUT through the
JTMS, since each names it among its antecedents, and a revival brings them back the same
way. A fact that arrived while the declaration was OUT merged nothing: its own
direction read a taxonomy without the mark, and the declaration's arrival was already
over. So settle hands every datum it revives to special/revived-declaration-sweeps,
which runs equate-existing and antisym-equate-existing for it as its arrival did
(order_independence_test/a-revived-mark-reaches-the-facts-that-arrived-while-it-was-out).
The sweeps read the taxonomy, so the settle reconciles its caches over the revived datums
before them: a datum a relabel inside the settle revives, such as a genl edge whose void
firing a forced-monotonic declaration releases, is otherwise in no cache until settle-finish
(order_independence_test/a-genl-edge-a-released-firing-concludes-merges-what-its-arrival-merges).
A rule-concluded declaration the labeller holds OUT at its arrival, such as one a void
firing concludes, runs neither sweep then. The settle that brings it IN runs them, so the
equality arrives believed and migrates the class
(order_independence_test/a-mark-a-void-firing-concludes-merges-nothing-until-the-release).
An except of the declaration moves which readers see it without a relabel, so a fact
that arrived while the except stood merged nothing either. Every except that arrives,
leaves or changes label queues its target, and the settle runs the same sweeps for it
(special/except-move-sweeps), together with the migration an equality's arrival runs
when the target is an equality (equational.md;
order_independence_test/an-except-that-moves-reaches-what-an-equality-or-a-mark-restates).
A revived genl edge under a functional, functionalInArg or anti_symmetric mark
runs equate-under-edge and antisym-equate-under-edge over one walk of its spec subtree
(special/revived-edge-sweep), offering the walked facts to the candidate index as the
arrival does: to the converse candidates alone under an anti_symmetric mark
(order_independence_test/a-revived-genl-edge-offers-its-subtree-to-the-converse-candidates).
That walk is budgeted where the arrival's is not:
a settle revives an edge far more often than one arrives, and lein perf's
constraint-genl-mark-descent holds a revived edge flat in the subtree past
tax/*exposure-instance-budget*. Below the budget the revival merges what the same
sentences in any other order merge. Past it the walk stops, files one
:genl-edge-revival-truncated violation naming the edge, and the facts past the cut that
arrived while the edge was OUT stay unmerged. The walk reads the subtree's predicates in
content order and each posting in the index's order. An edge under no such mark reads two props-over closures and walks
nothing, which constraint-genl-edge-unmarked holds flat
(order_independence_test/a-revived-genl-edge-merges-up-to-the-budget-and-names-the-cut).
The equality also names the genlCx edges its reader sees each fact and the declaration
through, one path each (checks/visibility-support), in all four directions. Retracting
the edge that made the pair jointly visible un-merges, and a second path draws the merge
again (special/rederive-descended;
order_independence_test/a-merge-seen-through-a-context-edge-given-back-is-the-merge-that-never-came).
The closure is a fourth cached relation in vaelii.impl.taxonomy, beside genl,
genlCx and the predicate metadata, and inherits their :support discipline: an edge
is active only while some sentex asserting it is IN in the network, a scoped read filters
each supporter through the read walk (nmtms.md), and
refresh-beliefs reconciles at the end of every settle.
It is an equivalence, not a partial order, so it is stored as a partition —
member → class, class → members and representative — rather than as up/down
closures. Insertion is a union that re-keys every member of the joined class, so growing
one class to n members costs Θ(n²). Deletion can split a class, which
union-find cannot undo, so a retraction rebuilds the affected class from its
remaining believed edges — the same shape as the ancestor-set-local genl deletion, and
bounded by the class rather than the KB.
closures-style from-scratch recomputation survives as the oracle, and the
incremental result is checked against it after every edit, as
taxonomy.md describes for genl. recover rebuilds the partition from
the stored rewriteOf / sameAs / equals sentexes and recomputes supersession from
it.
genl has genls / specs / genl?; genlCx has context-up / sees?.
Equality gets the same treatment, or an application cannot see what merged:
representative, same-class?, equiv-class, deprecated?. Without
deprecated? in particular, nothing outside representative selection
distinguishes rewriteOf from sameAs at all, and the doc's claim that one
deprecates and the other does not would be unobservable.
All four take an optional context, and all four have to: they read one partition, so a
scoped representative electing Bravo beside an unscoped deprecated? calling
Bravo retired is two answers about one context. The unscoped arity asks about the KB
rather than from a vantage, which is what a ?ctx goal means everywhere else.
why-not gains a reason. A superseded spelling is none of :not-stored,
:defeated or :unsupported — it is :superseded, naming the representative that
displaced it.
Disjointness. A merge can create a clash: (dog Rex) + (cat Fluffy) + merge
makes one individual both. Migration asks each twin what place-conclusion asks a
rule's conclusion (checks/derivation-violation), so the twin is stored, supersedes
its original, and the clash is placed as a nogood and reported in
contradictions or conflicts like any stored clash. A member OUT when the merge
arrives is restated too, with a twin labelled OUT beside it, so which spellings are
stored does not depend on which member the clash had defeated. A nogood placed over
the originals before the merge keeps its placement, since the families detect over
the network's IN label, and the reports leave out its contradicts, which names a
superseded member (nmtms.md). An
inadmissible twin (an argument conviction, a malformed sentence) is dropped, its
original stays believed, and violations reports the drop.
Stratification. different in a rule antecedent is a negative
dependency, so it joins the rule dependency graph beside exceptWhen
(exceptions.md). A rule concluding an equality from a
different antecedent is a cycle through negation and is rejected — otherwise
belief would depend on arrival order. The same refusal covers a rule concluding
indeterminate_term or a genl into it, since the unique-name assumption is
suspended for a member and the guard is withdrawn the same way
(predall.md). The negative edge runs to the three equality relations.
checks/negative-edge-rules reads the re-check index beside the antecedent index, so
a stored different rule keeps a genl edge's stratification walk on, and a cycle a
later taxonomy edge closes beneath it is refused.
A rule concluding one of the three relations merges. A rule-concluded
(sameAs A B) reaches special/integrate-equality-sentex — the same arm the table
runs for an asserted one — so the closure learns the edge, migration restates every
sentex it displaces, the retired spelling stops being believed, and a migration the
integrity checks refuse is filed as a violation. rebuild-taxonomy replays every
stored rewriteOf / sameAs / equals, so a derivation path that skipped the merge
would leave a running KB and its own restart disagreeing about what the KB entails.
It is reached by name rather than by the :derived? flag genl carries, and the
reason is the return value: integrate-transitive discards what an arm hands back,
and here that is the work — the twins, which are seeds this chaining run has to take,
and the violations, which are somebody's to report. It runs after the conclusion's
justification, since migration justifies each twin by the equality edges it believes
and a node nothing supports yet merges nothing.
The migration writes from inside the join that concluded the equality, under two
conditions. The twins go back on the agenda, like every other new datum
place-conclusion returns, because the retired spelling stops matching the moment the
supersession lands, and a rule that had not yet reached the original would otherwise
fire on neither spelling. The arms migration runs re-enter neither assert nor
chain: they add cache entries, justifications and re-check queue items
(special/integrate-twin). derive-functional-equalities concludes a merge from the
same place.
A rule naming a retired spelling is restated. A merge that displaces a symbol a
rule mentions migrates the rule, an individual constant as much as a predicate or a
type (kb/rewritable-sentex?; "Merging predicates and types" has the mechanism). The
twin names the representative and fires on the facts restated under it, and the
original is superseded where the merge is visible
(equality_test/a-rule-naming-a-retired-spelling-fires-under-the-representative-in-every-order).
Symmetric predicates. Argument sorting for a symmetric predicate is done at canonicalization time against the stored symbols. A later merge changes what the sorted order should be, so migration must re-canonicalize rather than substitute textually.
Deferred literals are evaluated in a forward join, and different at the
placement. Being deferred is about ordering, and ordering is not evaluation. A
forward join that matched every antecedent against the index would look a lessThan
(or evaluate) antecedent up as a fact nobody stores, find nothing, and
kill the join; chain/join-antecedent instead sends a deferred antecedent to
provers/solve-goal, the same registry the backward chainers discharge it through.
The two ask at different contexts, though: the forward join asks at the wildcard
'?ctx, which reads the whole equality partition, where a backward search reads the
partition its goal's context sees. So the forward join withholds a different
antecedent (chain/join-order) and decides it at the placement: a firing whose
different fails in the conclusion's context is refused and recorded
(chain/refusal-reason, :naf), and the re-check re-decides it when a merge arrives
or leaves. A reader below the placement that sees a merge joining the two arguments
reads a guard defeat of the conclusion, as an unknown that holds there does
(naf.md). The defeat rests on the supporters of one path joining them at
that reader, or on the membership making an argument an indeterminate term, and on
the genlCx edges the reader sees them through (chain/different-guard-placements),
so retracting one releases the conclusion there
(equality_test/a-merge-below-the-placement-defeats-a-different-guarded-firing-there-in-every-order).
See inference.md for the pair and for what the justification records.
A schematic equation with variables is oriented by the Knuth-Bendix order into a rewrite
rule that normalizes stored terms and goals alike (equational.md). A
compound equality over reifiable NATs, (equals (MotherOf A) (MotherOf B)), needs
nothing more: each side reifies to its constant before it reaches the closure
(nat.md). These are not built:
(equals ?x ?y) by searching rewrites
of a goal on demand. A goal open on both sides returns the stated equations only
("Answering an equation").:non-confluent violation and not completed (equational.md).(rel ?x ?y) = (rel ?y ?x), is refused.sameQuantity, evaluate) answer it, not the closure.differentFrom: different is not assertible
(defenses.md).rewriteOf reaches predicates and types, not only individuals — vocabulary
alignment on import (birthplaceOf ⇒ bornIn, dog ⇒ canine) produces co-referent
predicate and type names on day one. Because a merged term now heads sentences
(functor position) as well as filling arguments, the migration reaches further than
individual merging:
(birthplaceOf Ada London) becomes (bornIn Ada London), and
the predicate extent of birthplaceOf no longer answers a believed query.genl closure moves with a merged type: (genl dog animal) migrates to
(genl canine animal) and isa? / genls / specs answer under the
representative, the retired type's edge dropping as its declaration is superseded.
A twin edge brings the facts under it to the rules above it, as an asserted edge does
(special/minted-seeds), whichever path stored the twin: the merge, a fact or edge
arriving after it, or an except of the merge leaving.disjoint, the predicate metadata (transitive,
symmetric, inverse, functional, …), metatype membership — follow, since each
declaration is a sentex the merge re-canonicalizes.:engines / :defeasible / :effect (re-applied by rules/rewrap-sentex,
since the wrappers ride the record, not the stored sentence), and fires under the
representative while the original is superseded. In both arrival orders, which is
the rule entry point's own arm: migrate-class restates a rule already stored when the merge
arrives, and assert-rule-sentence restates one written afterwards, seeding the twin
beside the rule itself — the spelling the author wrote in stops firing the moment the
supersession lands.exceptWhen names the rule it guards, an except names the sentex it
hides, a target_following_predicate reply names the claim it hangs on — all shaped
(P … (sentexHandle H) …). Migrating H to a new handle H′ would strand every one of
them, so the twin fires unguarded, becomes visible, or names a claim no longer
believed. migrate-handle-metas re-points each of H's believed metas onto the twin
(an exceptWhen's query rewritten and realigned too; an except or reply's terms
rewritten and its handle retargeted), derived and justified by [the meta, the equality], so the twin ends properly qualified and retracting the merge collects the
meta twins and revives the originals. This runs in both arrival orders: for a meta
that predates the merge, migrate-into carries it as it migrates the target; for a
meta asserted after the merge — when migration already ran and never saw it —
migrate-meta-onto-twins replays the same carry onto the live twin at the meta's
entry point (the exceptWhen entry point and the ordinary fact entry point alike), idempotent for the metas
already carried. A NAF (unknown …) antecedent lives in the rule sentence, so it
rewrites with the rule and re-posts through the twin's own index-rule-sentex. A
predicate merged only in an exception's query (the rule itself not migrated —
penguin in bird ⇒ flies exceptWhen penguin) is handled by the ordinary
meta-sentex migration, which rewrites the query onto the representative so it keeps
blocking the migrated facts.rewriteOf is the spelling relation, so it is the one that carries alignment across
predicates and types, and
wff enforces that a rewriteOf's two sides are the same role —
predicate-with-predicate, type-with-type, individual-with-individual — since a
cross-role merge is meaningless and a likely import bug. A bare lowercase word is
:either and accepts any non-individual partner; a namespaced symbol is :opaque
and accepts every partner, because the roles are conventions over names a person chose
and a reified NAT constant (nat.md) is one the engine minted — what it
denotes is settled by its materialized result types, so retiring one into the real term
its function's corresponding predicate names is exactly the move this check must not
refuse.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |