Let A = {x1,x2,…,xn}. We want to calculate the difference between xi and xj, where xj is min{x ∈ A : x > xi }. It is possible to translate this kind of expression into a Jena rule?
-
2You can do math with Jena rules. You'll need to explain how you are representing the set of numbers, and where you want to store the result. That said, this would probably be *much* easier if you use SPARQL. You could use a single query to get all the results that you want, and you could even use a construct query to build the results in RDF, which could be added back to your model. – Joshua Taylor Oct 14 '14 at 12:07
1 Answers
Yes you can do this in both Jena Rules and in Jena ARQ (the query processing engine for SPARQL).
The Setup
Let us assume that you have defined a structured representation of your data. For Xi, you have an object that corresponds to something like this (N3):
[ a :Entry
; :hasEntryValue 4.3
]
Then, for your set, A, you can define its values as an RDF Collection such as rdf:List. The following N3 woudl describe the set A={4.3, 4.2}, for example.
[ a :Set
; dct:identifier "A"
; entries ( [ a :Entry
; :hasEntryValue 4.3
][ a :Entry
; :hasEntryValue 4.2
] )
] .
In Jena Rules
In Jena rules, you could write something along the lines of:
[calculate: ( ?set rdf:type :Set ),
( ?set rdfs:member ?x0 ),
nextSmallest( ?set ?x0 ?x1 ),
makeSkolem( ?calculated ?x0 ?set),
difference( ?difference ?x0 ?x1)
-> (?calculated :hasValue ?difference),
(?calculated :derivedFrom ?x0),
(?calculated :derivedFrom ?x1)
]
This would require you to construct a Jena Builtin for the nextSmallest
portion of the query. The above rule would, for a given value, ?x0
, within a particular set ,?set
, attempt to identify the nextSmallest
element, ?x1
, and (if that succeeds) construct a new node, ?calculated
and associate the ?difference
with it by means of some domain-specific :hasValue
property. As a style note, I also introduced a :derivedFrom
property in order to trace where that node came from so it wouldn't be useless as a calculation result.
In ARQ / SPARQL
The first question is whether or not you want the results to be in your graph with your data (as in the rule-based approach) or if you simply want the results. If you simply want the results, you have a choice as to whether or not the results are in a graph form. The simplest approach would be to simply SELECT
the results:
SELECT ?set ?x0Val (BIND(?x1Val-?x0Val) AS ?difference) WHERE {
?set rdf:type :Set .
?set rdfs:member ?x0 .
?set rdfs:member ?x1 .
?x0 :hasEntryValue ?x0Val .
?x1 :hasEntryValue ?x1Val .
FILTER( ?x0Val < ?x1Val )
FILTER NOT EXISTS {
?set rdfs:member ?otherEntry .
?otherEntry :hasEntryValue ?otherVal .
FILTER( ?x0Val < ?otherVal && ?otherVal < ?x1Val ) .
}
}
The above query was written off the top of my head so it may need some tweaks. The gist of it is that you retrieve any two members of the collection, and orient them such that ?x0
is the smaller of the two. You then throw out the bound pairs if there exists any pair between them. The query engine will consider the cartesian product of the set when selecting bindings for ?x0
and ?x1
including when sameTerm(?x0, ?x1)
, so using FILTER( ?x0Val < ?x1Val )
provides a quick way to discard trivially poor bindings.