Skip to content

A rule cannot join two entities #818

Description

@WaylandYang

A rule can only speak about one entity. Its conditions read predicates of a subject, and its conclusion lands on that same subject: Condition { group, predicate, op, operand } and Conclusion::{Typing, Computed, Attribute} in crates/utopia-reason/src/rules.rs. There is no variable and no join, so the shape a knowledge graph exists for cannot be written down:

parent(X, Y) AND parent(Y, Z)  =>  grandparent(X, Z)
supplies(X, Y) AND supplies(Y, Z)  =>  upstream_of(X, Z)
owns(X, Y, share > 50) AND owns(Y, Z, share > 50)  =>  controls(X, Z)

What we have instead

Predicate axioms (derive.rs): transitive, symmetric, asymmetric, irreflexive, functional, inverse-functional, inverse_of, sub_property_of. These do chain across edges, bounded by MAX_DEPTH 12 premises and MAX_DERIVED_PER_PREDICATE 20,000. They are seven built-ins; a person cannot add an eighth.

Business rules: groups of conditions, AND within a group and OR across groups [0029], concluding a class, a constant, or an arithmetic expression over the subject's own readings [0032]. Every condition and the conclusion are bound to one subject.

The gap is not recursion. Transitivity already recurses. The gap is a second entity in the rule body.

Time is the part that is already further along

RuleHit carries from/to; each combination of premise readings produces its own interval, so two revenues and three costs make six conclusions with six intervals; a derivation's validity is the intersection of its premises' intervals; the temporal engine closes and contradicts on top of that. So this is not a request for temporal reasoning from scratch. The facts and the derivations are already temporal. What is missing is a rule language able to express a join, at which point the interval intersection already implemented becomes the temporal join.

Why it matters here

paths_between walks up to three hops at query time, once, for one pair of entities the asker already named. That is the only way today to answer "how are these two connected", and it cannot land a fact, cannot be reviewed, cannot be contradicted, and has to be asked again next time. A joined rule turns the same question into a derived fact with a proof and a validity interval, which is the thing the rest of this system is built to handle.

Proposed first cut, for discussion before any code

One extra entity, no more:

  • A rule body may name a second subject reached by one relation from the first (X --p--> Y), and conditions may be attached to either.
  • The conclusion may be a relation between the two, not only an attribute of the first.
  • Validity of the conclusion is the intersection of every premise interval, which RuleHit already computes for the single-subject case.
  • Depth stays bounded by the existing constants; no fixpoint, no negation, no aggregation in this cut.

That is enough for grandparent, upstream-of and control chains, and it leaves the harder questions (stratified negation, aggregation, full fixpoint evaluation, recursion through user-defined predicates) out of the first decision.

Opening this as an issue rather than a branch, per the project's rule that a fix waits for an ADR when it touches the data model. If this is worth doing, the design record comes first.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions