You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jena.apache.org by Justin Dowdy <th...@gmail.com> on 2026/02/05 23:16:54 UTC

Generic rule reasoner and noValue()

Hi all,

Using Generic rule reasoner (in FORWARD mode), say I have a rule with this
in the head:
(?reportA ex:supercededBy ?reportB)

Then another rule with this in the body:
noValue(?reportA ex:supercededBy ?reportB)

Shouldn't there be an implicit rule fire ordering since we can see that the
second rule depends on the first rule firing until completion.

And a related question...

Say I have triples like:
:thingA :hasValue 8 .
:thingA :hasValue 4 .
:thingA :hasValue 9 .
...

Using FORWARD mode, is it possible to write a set of rules that do the
equivalent of:
```
CONSTRUCT
  {
    ?thing :hasMaxValue ?max_value .
  }
WHERE
  { SELECT  ?thing (MAX(?value) AS ?max_value)
    WHERE
      { ?thing  :hasValue  ?value }
    GROUP BY ?thing
  }
```

Re: Generic rule reasoner and noValue()

Posted by Andy Seaborne <an...@apache.org>.
On 09/02/2026 10:35, Lorenz Buehmann wrote:
> Yes, I read about it on the jena-dev mailing list—it sounds very useful.
> 
> Ultimately, we’re hoping for an efficient implementation, but starting 
> with a simple and correct one makes sense so other execution engines can 
> be compared against it. Looking forward to seeing this become a feature 
> in Jena.
> 
> Cheers,
> Lorenz

Hi Lorenz,

What's the use case you have in mind?

SHACL rules does not prescribe implementation approach but the two basic 
operations infer() (create a graph of all possible new triples) and 
query(s,p,o), do a Graph.find-like operation, are best served in 
different ways.

     Andy


Re: Re: Generic rule reasoner and noValue()

Posted by Lorenz Buehmann <bu...@informatik.uni-leipzig.de>.
Yes, I read about it on the jena-dev mailing list—it sounds very useful.

Ultimately, we’re hoping for an efficient implementation, but starting 
with a simple and correct one makes sense so other execution engines can 
be compared against it. Looking forward to seeing this become a feature 
in Jena.

Cheers,
Lorenz

On 2/8/26 22:20, Andy Seaborne wrote:
>>> As far as I know, there is nothing for
>>>
>>> - guaranteed rule ordering
>>> - stratification
>>> - dependency analysis
>>> - truth maintenance
>
> The Data Shapes WG (the SHACL WG) has work going on to specify a rules 
> system for RDF.
>
> It's taking forward the work in SHACL-AF [1], the SHACL Advanced 
> Features note, which includes the great text "The latter is left to 
> future work" (repeated application of rules).
>
> So this work is motivated by user interest and is seeing the AF idea 
> through.
>
> The *in-complete* *work-in-progress* editors working draft is:
>
>     https://www.w3.org/TR/shacl12-rules/
>
> This is datalog with stratified negation as failure.
>
> There are two concrete syntaxes: a SPARQL-style syntax and an RDF 
> encoding [2]. They are equivalent in that there is nothing you can 
> express in one which is not available in the other. They both parse to 
> an abstract form, which is then evaluated to defined the outcome.
>
>
> I have an implementation in-progress. There are command line tools to 
> parser/print a rule set and to infer triples.
>
> The goal at the moment is correct functionality, with a modular 
> architecture for a range of execution strategies. Scale and 
> performance and not goals at the moment.
>
> There is one execution engine currently, and it is simple (i.e. it can 
> be understood quite easily), but does generate the right answers that 
> can then be used to test against future engines.
>
> This is not a replacement for the rule system in jena-core. Ideally, 
> some or all of that technology can be integrated but that's some way 
> off yet.
>
>     Andy
>
> SHACL-AF Rules
> [1] https://w3c.github.io/shacl/shacl-af/index.html#rules
> Not implemented by jena-shacl.
>
-- 
Lorenz Bühmann
Research Associate/Scientific Developer

Email buehmann@infai.org

Institute for Applied Informatics e.V. (InfAI) | Goerdelerring 9 | 04109 Leipzig | Germany


Re: Generic rule reasoner and noValue()

Posted by Andy Seaborne <an...@apache.org>.
>> As far as I know, there is nothing for
>>
>> - guaranteed rule ordering
>> - stratification
>> - dependency analysis
>> - truth maintenance

The Data Shapes WG (the SHACL WG) has work going on to specify a rules 
system for RDF.

It's taking forward the work in SHACL-AF [1], the SHACL Advanced 
Features note, which includes the great text "The latter is left to 
future work" (repeated application of rules).

So this work is motivated by user interest and is seeing the AF idea 
through.

The *in-complete* *work-in-progress* editors working draft is:

     https://www.w3.org/TR/shacl12-rules/

This is datalog with stratified negation as failure.

There are two concrete syntaxes: a SPARQL-style syntax and an RDF 
encoding [2]. They are equivalent in that there is nothing you can 
express in one which is not available in the other. They both parse to 
an abstract form, which is then evaluated to defined the outcome.


I have an implementation in-progress. There are command line tools to 
parser/print a rule set and to infer triples.

The goal at the moment is correct functionality, with a modular 
architecture for a range of execution strategies. Scale and performance 
and not goals at the moment.

There is one execution engine currently, and it is simple (i.e. it can 
be understood quite easily), but does generate the right answers that 
can then be used to test against future engines.

This is not a replacement for the rule system in jena-core. Ideally, 
some or all of that technology can be integrated but that's some way off 
yet.

     Andy

SHACL-AF Rules
[1] https://w3c.github.io/shacl/shacl-af/index.html#rules
Not implemented by jena-shacl.


Re: Generic rule reasoner and noValue()

Posted by Justin Dowdy <th...@gmail.com>.
I slept on it and I have something that works.

What is needed to make some rule ordering happen (for the FORWARD mode) is
a triple that indicates a rule has fired.
e.g.

I now have a rule with this in the head:
(?reportA ex:supercededBy ?reportB) (ex:SUPERCEDED_RULE ex:fired ex:true)

Then another rule with this in the body:
(ex:SUPERCEDED_RULE_FIRED ex:fired ex:true)
noValue(?reportA ex:supercededBy ?reportB)

That will ensure that is first rule runs before the second rule.

I believe this was hinted at in the docs for the `hide(p)` primitive:
```
...
This is useful to enable non-monotonic forward rules to define flag
predicates which are only used for inference control and do not "pollute"
the inference results.
```

Using this approach you can also do things like find the `max()` by having
a rule do pairwise comparisons and asserting a triple like (X lessThan Y)
and then looking for the absence of (X lessThan) then you know it was the
max.

I am mostly doing this because I want explanations of inferred triples.
One drawback to using noValue(), however, is that there isn't anything to
print in the derivation trace when noValue() is satisfied.

On Fri, Feb 6, 2026 at 4:23 AM Lorenz Buehmann <
buehmann@informatik.uni-leipzig.de> wrote:

> Hi Justin,
>
> I'm not the developer of the rule engine, might I will try do answer
> your question with my limited knowledge:
>
> > Using Generic rule reasoner (in FORWARD mode), say I have a rule with
> this
> > in the head:
> > (?reportA ex:supercededBy ?reportB)
> >
> > Then another rule with this in the body:
> > noValue(?reportA ex:supercededBy ?reportB)
> >
> > Shouldn't there be an implicit rule fire ordering since we can see that
> the
> > second rule depends on the first rule firing until completion.
>
> As far as I know, there is nothing for
>
> - guaranteed rule ordering
> - stratification
> - dependency analysis
> - truth maintenance
>
> Under the hood, the forward GenericRuleReasoner is implemented using a
> RETE network. What RETE does not imply rule ordering, stratification, or
> safe negation. It just makes matching fast.
>
> Your specific example
>
> Rule A (producer):
>
> ... -> (?reportA ex:supercededBy ?reportB)
>
>
> Rule B (consumer):
>
> noValue(?reportA ex:supercededBy ?reportB) ... -> ...
>
>
> Your expectation is basically:
>
> “Rule B should wait until Rule A has fully fired.”
>
> That expectation assumes stratified negation (like in Datalog or SQL),
> but Jena does not do that.
>
>
> So, why do I think that Jena can’t safely do implicit ordering?
>
> Because the builtIn noValue is non-monotonic.
>
> At the moment Rule B fires:
>
> - The triple might not exist yet
> - But it might be inferred later
> - Jena has no way to know that in advance
>
> So from the engine’s perspective:
>
> "Absence of a triple right now is not stable information."
>
> That’s why:
>
> - noValue is evaluated against the current graph snapshot
> - Results may become logically invalid later
> - Jena will not retract them
>
> This is probably one reason why the documentation quietly treats noValue
> as dangerous but useful.
>
> > And a related question...
> >
> > Say I have triples like:
> > :thingA :hasValue 8 .
> > :thingA :hasValue 4 .
> > :thingA :hasValue 9 .
> > ...
> >
> > Using FORWARD mode, is it possible to write a set of rules that do the
> > equivalent of:
> > ```
> > CONSTRUCT
> >    {
> >      ?thing :hasMaxValue ?max_value .
> >    }
> > WHERE
> >    { SELECT  ?thing (MAX(?value) AS ?max_value)
> >      WHERE
> >        { ?thing  :hasValue  ?value }
> >      GROUP BY ?thing
> >    }
> > ```
> >
> No, I don't think this is possible. The Generic Rule Reasoner cannot
> express aggregation semantics like MAX in a sound way.
>
> Why aggregation doesn’t work in Jena forward rules, is basically because
> those:
>
> - Fire on local pattern matches
> - Have no global view
> - Cannot “see all values” for a thing at once
> - Cannot retract previously inferred triples
>
>
> For example when we have
>
> :thingA :hasValue 8 .
> :thingA :hasValue 4 .
> :thingA :hasValue 9 .
>
> A forward rule might see:
>
> - 8 first -> infer hasMaxValue 8
> - then 9 later -> infer hasMaxValue 9
>
> but cannot remove the 8
>
> So we would end up with
>
> :thingA :hasMaxValue 8 .
> :thingA :hasMaxValue 9 .
>
>
> I know that other rule languages might have extensions for aggregates,
> but we could maybe better think of Jena FORWARD rules as:
>
> “RDFS with custom Horn clauses — not SQL, not Datalog with negation, not
> a rule engine with agenda control.”
>
>
>
> Hope this answers your questions to some extent, although that wouldn't
> solve your problem. A combination of rules + SPARQL CONSTRUCT (INSERTs)
> might solve your use-case
>
>
> Cheers,
>
> Lorenz
>
> --
> Lorenz Bühmann
> Research Associate/Scientific Developer
>
> Emailbuehmann@infai.org
>
> Institute for Applied Informatics e.V. (InfAI) | Goerdelerring 9 | 04109
> Leipzig | Germany
>

Re: Generic rule reasoner and noValue()

Posted by Lorenz Buehmann <bu...@informatik.uni-leipzig.de>.
Hi Justin,

I'm not the developer of the rule engine, might I will try do answer 
your question with my limited knowledge:

> Using Generic rule reasoner (in FORWARD mode), say I have a rule with this
> in the head:
> (?reportA ex:supercededBy ?reportB)
>
> Then another rule with this in the body:
> noValue(?reportA ex:supercededBy ?reportB)
>
> Shouldn't there be an implicit rule fire ordering since we can see that the
> second rule depends on the first rule firing until completion.

As far as I know, there is nothing for

- guaranteed rule ordering
- stratification
- dependency analysis
- truth maintenance

Under the hood, the forward GenericRuleReasoner is implemented using a 
RETE network. What RETE does not imply rule ordering, stratification, or 
safe negation. It just makes matching fast.

Your specific example

Rule A (producer):

... -> (?reportA ex:supercededBy ?reportB)


Rule B (consumer):

noValue(?reportA ex:supercededBy ?reportB) ... -> ...


Your expectation is basically:

“Rule B should wait until Rule A has fully fired.”

That expectation assumes stratified negation (like in Datalog or SQL), 
but Jena does not do that.


So, why do I think that Jena can’t safely do implicit ordering?

Because the builtIn noValue is non-monotonic.

At the moment Rule B fires:

- The triple might not exist yet
- But it might be inferred later
- Jena has no way to know that in advance

So from the engine’s perspective:

"Absence of a triple right now is not stable information."

That’s why:

- noValue is evaluated against the current graph snapshot
- Results may become logically invalid later
- Jena will not retract them

This is probably one reason why the documentation quietly treats noValue 
as dangerous but useful.

> And a related question...
>
> Say I have triples like:
> :thingA :hasValue 8 .
> :thingA :hasValue 4 .
> :thingA :hasValue 9 .
> ...
>
> Using FORWARD mode, is it possible to write a set of rules that do the
> equivalent of:
> ```
> CONSTRUCT
>    {
>      ?thing :hasMaxValue ?max_value .
>    }
> WHERE
>    { SELECT  ?thing (MAX(?value) AS ?max_value)
>      WHERE
>        { ?thing  :hasValue  ?value }
>      GROUP BY ?thing
>    }
> ```
>
No, I don't think this is possible. The Generic Rule Reasoner cannot 
express aggregation semantics like MAX in a sound way.

Why aggregation doesn’t work in Jena forward rules, is basically because 
those:

- Fire on local pattern matches
- Have no global view
- Cannot “see all values” for a thing at once
- Cannot retract previously inferred triples


For example when we have

:thingA :hasValue 8 .
:thingA :hasValue 4 .
:thingA :hasValue 9 .

A forward rule might see:

- 8 first -> infer hasMaxValue 8
- then 9 later -> infer hasMaxValue 9

but cannot remove the 8

So we would end up with

:thingA :hasMaxValue 8 .
:thingA :hasMaxValue 9 .


I know that other rule languages might have extensions for aggregates, 
but we could maybe better think of Jena FORWARD rules as:

“RDFS with custom Horn clauses — not SQL, not Datalog with negation, not 
a rule engine with agenda control.”



Hope this answers your questions to some extent, although that wouldn't 
solve your problem. A combination of rules + SPARQL CONSTRUCT (INSERTs) 
might solve your use-case


Cheers,

Lorenz

-- 
Lorenz Bühmann
Research Associate/Scientific Developer

Emailbuehmann@infai.org

Institute for Applied Informatics e.V. (InfAI) | Goerdelerring 9 | 04109 Leipzig | Germany