rdfs.scm
1 | ;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu> |
2 | ;;;; |
3 | ;;;; This library is free software; you can redistribute it and/or |
4 | ;;;; modify it under the terms of the GNU Lesser General Public |
5 | ;;;; License as published by the Free Software Foundation; either |
6 | ;;;; version 3 of the License, or (at your option) any later version. |
7 | ;;;; |
8 | ;;;; This library is distributed in the hope that it will be useful, |
9 | ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
11 | ;;;; Lesser General Public License for more details. |
12 | ;;;; |
13 | ;;;; You should have received a copy of the GNU Lesser General Public |
14 | ;;;; License along with this library; if not, write to the Free Software |
15 | ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
16 | ;;;; |
17 | |
18 | (define-module (rdf entailment rdfs) |
19 | #:use-module (ice-9 match) |
20 | #:use-module (rdf rdf) |
21 | #:use-module ((rdf entailment d) #:prefix d:) |
22 | #:use-module ((rdf entailment rdf) #:prefix rdf:) |
23 | #:use-module ((rdf xsd) #:prefix xsd:) |
24 | #:use-module (srfi srfi-1) |
25 | #:export (consistent-graph? |
26 | entails?)) |
27 | |
28 | (define (rdf-iri name) |
29 | (string-append "http://www.w3.org/1999/02/22-rdf-syntax-ns#" name)) |
30 | |
31 | (define (rdfs-iri name) |
32 | (string-append "http://www.w3.org/2000/01/rdf-schema#" name)) |
33 | |
34 | (define (consistent-graph? graph vocabulary) |
35 | (define (valid-subclasses? graph) |
36 | (match graph |
37 | (() #t) |
38 | ((($ rdf-triple (? rdf-datatype? s) p (? rdf-datatype? o)) graph ...) |
39 | (if (is-iri? p (rdfs-iri "subClassOf")) |
40 | (and ((rdf-vocabulary-order vocabulary) s o) |
41 | (valid-subclasses? graph)) |
42 | (valid-subclasses? graph))) |
43 | ((_ graph ...) |
44 | (valid-subclasses? graph)))) |
45 | (let ((graph (augment (recognize graph vocabulary) vocabulary))) |
46 | (and (valid-subclasses? graph) |
47 | (rdf:consistent-graph? graph vocabulary)))) |
48 | |
49 | ;; G entails E if E has an instance (where blank nodes are replaced by literals |
50 | ;; or IRIs) that is a subgraph of G. |
51 | ;; |
52 | ;; We re-use similar procedures to verifying isomorphism of graphs, but this time |
53 | ;; blank nodes can also map to literals and IRIs. |
54 | |
55 | ;; We follow appendix A and use a subgraph comparison (like the simple:entails? |
56 | ;; procedure) after augmenting the graph with additional true triples. |
57 | |
58 | (define rdfs-axioms |
59 | (list |
60 | (make-rdf-triple (rdf-iri "type") (rdfs-iri "domain") (rdfs-iri "Resource")) |
61 | (make-rdf-triple (rdfs-iri "domain") (rdfs-iri "domain") (rdf-iri "Property")) |
62 | (make-rdf-triple (rdfs-iri "range") (rdfs-iri "domain") (rdf-iri "Property")) |
63 | (make-rdf-triple (rdfs-iri "subPropertyOf") (rdfs-iri "domain") |
64 | (rdf-iri "Property")) |
65 | (make-rdf-triple (rdfs-iri "subClassOf") (rdfs-iri "domain") (rdfs-iri "Class")) |
66 | (make-rdf-triple (rdf-iri "subject") (rdfs-iri "domain") (rdf-iri "Statement")) |
67 | (make-rdf-triple (rdf-iri "predicate") (rdfs-iri "domain") (rdf-iri "Statement")) |
68 | (make-rdf-triple (rdf-iri "object") (rdfs-iri "domain") (rdf-iri "Statement")) |
69 | (make-rdf-triple (rdfs-iri "member") (rdfs-iri "domain") (rdfs-iri "Resource")) |
70 | (make-rdf-triple (rdf-iri "first") (rdfs-iri "domain") (rdf-iri "List")) |
71 | (make-rdf-triple (rdf-iri "rest") (rdfs-iri "domain") (rdf-iri "List")) |
72 | (make-rdf-triple (rdfs-iri "seeAlso") (rdfs-iri "domain") (rdfs-iri "Resource")) |
73 | (make-rdf-triple (rdfs-iri "isDefinedBy") (rdfs-iri "domain") |
74 | (rdfs-iri "Resource")) |
75 | (make-rdf-triple (rdfs-iri "comment") (rdfs-iri "domain") (rdfs-iri "Resource")) |
76 | (make-rdf-triple (rdfs-iri "label") (rdfs-iri "domain") (rdfs-iri "Resource")) |
77 | (make-rdf-triple (rdf-iri "value") (rdfs-iri "domain") (rdfs-iri "Resource")) |
78 | |
79 | (make-rdf-triple (rdf-iri "type") (rdfs-iri "range") (rdfs-iri "Class")) |
80 | (make-rdf-triple (rdfs-iri "domain") (rdfs-iri "range") (rdfs-iri "Class")) |
81 | (make-rdf-triple (rdfs-iri "range") (rdfs-iri "range") (rdfs-iri "Class")) |
82 | (make-rdf-triple (rdfs-iri "subPropertyOf") (rdfs-iri "range") |
83 | (rdf-iri "Property")) |
84 | (make-rdf-triple (rdfs-iri "subClassOf") (rdfs-iri "range") (rdfs-iri "Class")) |
85 | (make-rdf-triple (rdf-iri "subject") (rdfs-iri "range") (rdfs-iri "Resource")) |
86 | (make-rdf-triple (rdf-iri "predicate") (rdfs-iri "range") (rdfs-iri "Resource")) |
87 | (make-rdf-triple (rdf-iri "object") (rdfs-iri "range") (rdfs-iri "Resource")) |
88 | (make-rdf-triple (rdfs-iri "member") (rdfs-iri "range") (rdfs-iri "Resource")) |
89 | (make-rdf-triple (rdf-iri "first") (rdfs-iri "range") (rdfs-iri "Resource")) |
90 | (make-rdf-triple (rdf-iri "rest") (rdfs-iri "range") (rdf-iri "List")) |
91 | (make-rdf-triple (rdfs-iri "seeAlso") (rdfs-iri "range") (rdfs-iri "Resource")) |
92 | (make-rdf-triple (rdfs-iri "isDefinedBy") (rdfs-iri "range") |
93 | (rdfs-iri "Resource")) |
94 | (make-rdf-triple (rdfs-iri "comment") (rdfs-iri "range") (rdfs-iri "Literal")) |
95 | (make-rdf-triple (rdfs-iri "label") (rdfs-iri "range") (rdfs-iri "Literal")) |
96 | (make-rdf-triple (rdf-iri "value") (rdfs-iri "range") (rdfs-iri "Resource")) |
97 | |
98 | (make-rdf-triple (rdf-iri "Alt") (rdfs-iri "subClassOf") (rdfs-iri "Container")) |
99 | (make-rdf-triple (rdf-iri "Bag") (rdfs-iri "subClassOf") (rdfs-iri "Container")) |
100 | (make-rdf-triple (rdf-iri "Seq") (rdfs-iri "subClassOf") (rdfs-iri "Container")) |
101 | (make-rdf-triple (rdfs-iri "ContainerMembershipProperty") |
102 | (rdfs-iri "subClassOf") (rdf-iri "Property")) |
103 | |
104 | (make-rdf-triple (rdfs-iri "isDefinedBy") (rdfs-iri "subPropertyOf") |
105 | (rdfs-iri "seeAlso")) |
106 | |
107 | (make-rdf-triple (rdfs-iri "Datatype") (rdfs-iri "subClassOf") |
108 | (rdfs-iri "Class")))) |
109 | |
110 | (define (rdfs-axioms-container container) |
111 | (list |
112 | (make-rdf-triple |
113 | container (rdf-iri "type") (rdfs-iri "ContainerMembershipProperty")) |
114 | (make-rdf-triple |
115 | container (rdfs-iri "domain") (rdfs-iri "Resource")) |
116 | (make-rdf-triple |
117 | container (rdfs-iri "range") (rdfs-iri "Resource")))) |
118 | |
119 | (define (rdfs-axioms-types d) |
120 | `(,@(if (and (member xsd:integer d) (member xsd:decimal d)) |
121 | (list (make-rdf-triple xsd:integer (rdfs-iri "subClassOf") xsd:decimal)) |
122 | '()))) |
123 | |
124 | (define (rdf-container-property? p) |
125 | (define rdf-container-property-base (rdf-iri "_")) |
126 | (and (string? p) |
127 | (> (string-length p) (string-length rdf-container-property-base)) |
128 | (equal? (substring p 0 (string-length rdf-container-property-base)) |
129 | rdf-container-property-base) |
130 | (string->number |
131 | (substring p (string-length rdf-container-property-base))))) |
132 | |
133 | (define (rdf-container-properties g) |
134 | (let loop ((answer '()) (g g)) |
135 | (match g |
136 | (() (if (null? answer) (list (rdf-iri "_1")) answer)) |
137 | ((($ rdf-triple subject predicate object) g ...) |
138 | (let* ((answer (if (and (rdf-container-property? subject) |
139 | (not (member subject answer))) |
140 | (cons subject answer) |
141 | answer)) |
142 | (answer (if (and (rdf-container-property? predicate) |
143 | (not (member predicate answer))) |
144 | (cons predicate answer) |
145 | answer)) |
146 | (answer (if (and (rdf-container-property? object) |
147 | (not (member object answer))) |
148 | (cons object answer) |
149 | answer))) |
150 | (loop answer g)))))) |
151 | |
152 | (define (is-iri? node iri) |
153 | (or (and (string? node) (equal? node iri)) |
154 | (and (rdf-datatype? node) (member iri (rdf-datatype-iris node))))) |
155 | |
156 | (define (get-entailments graph subclasses subprops ranges domains types) |
157 | (let ((type-adds |
158 | ;; rdfs 6 8 10 12 and 13 |
159 | (filter |
160 | (lambda (a) a) |
161 | (map |
162 | (match-lambda |
163 | (($ rdf-triple s _ o) |
164 | (cond |
165 | ((is-iri? o (rdf-iri "Property")) |
166 | (make-rdf-triple s (rdfs-iri "subPropertyOf") s)) |
167 | ((is-iri? o (rdfs-iri "Class")) |
168 | (make-rdf-triple s (rdfs-iri "subClassOf") (rdfs-iri "Resource"))) |
169 | ((is-iri? o (rdfs-iri "ContainerMembershipProperty")) |
170 | (make-rdf-triple s (rdfs-iri "subPropertyOf") |
171 | (rdfs-iri "member"))) |
172 | ((is-iri? o (rdfs-iri "Datatype")) |
173 | (make-rdf-triple s (rdfs-iri "subClassOf") |
174 | (rdfs-iri "Literal"))) |
175 | (else #f)))) |
176 | types)))) |
177 | (append |
178 | type-adds |
179 | (append-map |
180 | (match-lambda |
181 | (($ rdf-triple s p o) |
182 | `(;; grdf1 |
183 | ,@(if (and (rdf-literal? o) |
184 | (rdf-datatype? (rdf-literal-type o))) |
185 | (list (make-rdf-triple o (rdf-iri "type") (rdf-literal-type o))) |
186 | '()) |
187 | ;; rdf2 |
188 | ,(make-rdf-triple p (rdf-iri "type") (rdf-iri "Property")) |
189 | ;; rdfs2 |
190 | ,@(append-map |
191 | (match-lambda |
192 | (($ rdf-triple subject predicate object) |
193 | (if (equal? subject p) |
194 | (list (make-rdf-triple s (rdf-iri "type") object)) |
195 | '()))) |
196 | domains) |
197 | ;; rdfs3 |
198 | ,@(append-map |
199 | (match-lambda |
200 | (($ rdf-triple subject predicate object) |
201 | (if (equal? subject p) |
202 | (list (make-rdf-triple o (rdf-iri "type") object)) |
203 | '()))) |
204 | ranges) |
205 | ;; rdfs4a |
206 | ,(make-rdf-triple s (rdf-iri "type") (rdfs-iri "Resource")) |
207 | ;; rdfs4b |
208 | ,(make-rdf-triple o (rdf-iri "type") (rdfs-iri "Resource")) |
209 | ;; rdfs5 |
210 | ,@(if (is-iri? p (rdfs-iri "subPropertyOf")) |
211 | (let ((candidates (filter |
212 | (match-lambda |
213 | (($ rdf-triple _ _ object) |
214 | (equal? object s))) |
215 | subprops))) |
216 | (map |
217 | (match-lambda |
218 | (($ rdf-triple subject _ _) |
219 | (make-rdf-triple subject (rdfs-iri "subPropertyOf") |
220 | o))) |
221 | candidates)) |
222 | '()) |
223 | ;; rdfs7 |
224 | ,@(map |
225 | (match-lambda |
226 | (($ rdf-triple _ _ object) |
227 | (make-rdf-triple s object o))) |
228 | (filter |
229 | (match-lambda |
230 | (($ rdf-triple subject _ _) |
231 | (equal? subject p))) |
232 | subprops)) |
233 | ;; rdfs9 |
234 | ,@(if (is-iri? p (rdf-iri "type")) |
235 | (let ((candidates (filter |
236 | (match-lambda |
237 | (($ rdf-triple subject _ _) |
238 | (equal? subject o))) |
239 | subprops))) |
240 | (map |
241 | (match-lambda |
242 | (($ rdf-triple _ _ object) |
243 | (make-rdf-triple s (rdf-iri "type") object))) |
244 | candidates)) |
245 | '()) |
246 | ;; rdfs11 |
247 | ,@(if (is-iri? p (rdfs-iri "subClassOf")) |
248 | (let ((candidates (filter |
249 | (match-lambda |
250 | (($ rdf-triple _ _ object) |
251 | (equal? object s))) |
252 | subclasses))) |
253 | (map |
254 | (match-lambda |
255 | (($ rdf-triple subject _ _) |
256 | (make-rdf-triple subject (rdfs-iri "subClassOf") |
257 | o))) |
258 | candidates)) |
259 | '())))) |
260 | graph)))) |
261 | |
262 | (define (augment g voc) |
263 | (let* ((g (append rdfs-axioms g)) |
264 | (g (append (rdfs-axioms-types (rdf-vocabulary-datatypes voc)) g)) |
265 | (g (append |
266 | ;; rdfs1 |
267 | (map |
268 | (lambda (t) |
269 | (make-rdf-triple t (rdf-iri "type") (rdfs-iri "Datatype"))) |
270 | (rdf-vocabulary-datatypes voc)) |
271 | g)) |
272 | (g (append |
273 | (append-map rdfs-axioms-container (rdf-container-properties g)) |
274 | g))) |
275 | (let loop ((graph '()) (subclasses '()) (subprops '()) (ranges '()) |
276 | (domains '()) (types '())) |
277 | (let inner-loop ((graph graph) (subclasses subclasses) (subprops subprops) |
278 | (ranges ranges) (domains domains) |
279 | (types types) (added? #f) |
280 | (augment-set |
281 | (if (null? graph) |
282 | g |
283 | (recognize |
284 | (get-entailments |
285 | graph subclasses subprops ranges |
286 | domains types) |
287 | voc)))) |
288 | (match augment-set |
289 | (() (if added? |
290 | (loop graph subclasses subprops ranges domains types) |
291 | graph)) |
292 | ((t augment-set ...) |
293 | (if (member t graph) |
294 | (inner-loop graph subclasses subprops ranges domains types |
295 | added? augment-set) |
296 | (let ((p (rdf-triple-predicate t))) |
297 | (cond |
298 | ((is-iri? p (rdfs-iri "subClassOf")) |
299 | (inner-loop (cons t graph) (cons t subclasses) subprops |
300 | ranges domains types #t augment-set)) |
301 | ((is-iri? p (rdfs-iri "subPropertyOf")) |
302 | (inner-loop (cons t graph) subclasses (cons t subprops) |
303 | ranges domains types #t augment-set)) |
304 | ((is-iri? p (rdfs-iri "range")) |
305 | (inner-loop (cons t graph) subclasses subprops |
306 | (cons t ranges) domains types #t augment-set)) |
307 | ((is-iri? p (rdfs-iri "domain")) |
308 | (inner-loop (cons t graph) subclasses subprops |
309 | ranges (cons t domains) types #t augment-set)) |
310 | ((is-iri? p (rdf-iri "type")) |
311 | (inner-loop (cons t graph) subclasses subprops |
312 | ranges domains (cons t types) #t augment-set)) |
313 | (else |
314 | (inner-loop (cons t graph) subclasses subprops |
315 | ranges domains types #t augment-set))))))))))) |
316 | |
317 | (define (entails? g e vocabulary) |
318 | "Return true if g entails e recognizing d" |
319 | (let* ((g (recognize g vocabulary)) |
320 | (g (augment g vocabulary)) |
321 | (e (recognize e vocabulary))) |
322 | (or (not (consistent-graph? g vocabulary)) |
323 | (d:entails? g e vocabulary)))) |
324 |