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