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 | (and (valid-subclasses? (recognize graph vocabulary)) |
46 | (rdf:consistent-graph? graph vocabulary))) |
47 | |
48 | ;; G entails E if E has an instance (where blank nodes are replaced by literals |
49 | ;; or IRIs) that is a subgraph of G. |
50 | ;; |
51 | ;; We re-use similar procedures to verifying isomorphism of graphs, but this time |
52 | ;; blank nodes can also map to literals and IRIs. |
53 | |
54 | ;; We follow appendix A and use a subgraph comparison (like the simple:entails? |
55 | ;; procedure) after augmenting the graph with additional true triples. |
56 | |
57 | (define rdfs-axioms |
58 | (list |
59 | (make-rdf-triple (rdf-iri "type") (rdfs-iri "domain") (rdfs-iri "Resource")) |
60 | (make-rdf-triple (rdfs-iri "domain") (rdfs-iri "domain") (rdf-iri "Property")) |
61 | (make-rdf-triple (rdfs-iri "range") (rdfs-iri "domain") (rdf-iri "Property")) |
62 | (make-rdf-triple (rdfs-iri "subPropertyOf") (rdfs-iri "domain") |
63 | (rdf-iri "Property")) |
64 | (make-rdf-triple (rdfs-iri "subClassOf") (rdfs-iri "domain") (rdfs-iri "Class")) |
65 | (make-rdf-triple (rdf-iri "subject") (rdfs-iri "domain") (rdf-iri "Statement")) |
66 | (make-rdf-triple (rdf-iri "predicate") (rdfs-iri "domain") (rdf-iri "Statement")) |
67 | (make-rdf-triple (rdf-iri "object") (rdfs-iri "domain") (rdf-iri "Statement")) |
68 | (make-rdf-triple (rdfs-iri "member") (rdfs-iri "domain") (rdfs-iri "Resource")) |
69 | (make-rdf-triple (rdf-iri "first") (rdfs-iri "domain") (rdf-iri "List")) |
70 | (make-rdf-triple (rdf-iri "rest") (rdfs-iri "domain") (rdf-iri "List")) |
71 | (make-rdf-triple (rdfs-iri "seeAlso") (rdfs-iri "domain") (rdfs-iri "Resource")) |
72 | (make-rdf-triple (rdfs-iri "isDefinedBy") (rdfs-iri "domain") |
73 | (rdfs-iri "Resource")) |
74 | (make-rdf-triple (rdfs-iri "comment") (rdfs-iri "domain") (rdfs-iri "Resource")) |
75 | (make-rdf-triple (rdfs-iri "label") (rdfs-iri "domain") (rdfs-iri "Resource")) |
76 | (make-rdf-triple (rdf-iri "value") (rdfs-iri "domain") (rdfs-iri "Resource")) |
77 | |
78 | (make-rdf-triple (rdf-iri "type") (rdfs-iri "range") (rdfs-iri "Class")) |
79 | (make-rdf-triple (rdfs-iri "domain") (rdfs-iri "range") (rdfs-iri "Class")) |
80 | (make-rdf-triple (rdfs-iri "range") (rdfs-iri "range") (rdfs-iri "Class")) |
81 | (make-rdf-triple (rdfs-iri "subPropertyOf") (rdfs-iri "range") |
82 | (rdf-iri "Property")) |
83 | (make-rdf-triple (rdfs-iri "subClassOf") (rdfs-iri "range") (rdfs-iri "Class")) |
84 | (make-rdf-triple (rdf-iri "subject") (rdfs-iri "range") (rdfs-iri "Resource")) |
85 | (make-rdf-triple (rdf-iri "predicate") (rdfs-iri "range") (rdfs-iri "Resource")) |
86 | (make-rdf-triple (rdf-iri "object") (rdfs-iri "range") (rdfs-iri "Resource")) |
87 | (make-rdf-triple (rdfs-iri "member") (rdfs-iri "range") (rdfs-iri "Resource")) |
88 | (make-rdf-triple (rdf-iri "first") (rdfs-iri "range") (rdfs-iri "Resource")) |
89 | (make-rdf-triple (rdf-iri "rest") (rdfs-iri "range") (rdf-iri "List")) |
90 | (make-rdf-triple (rdfs-iri "seeAlso") (rdfs-iri "range") (rdfs-iri "Resource")) |
91 | (make-rdf-triple (rdfs-iri "isDefinedBy") (rdfs-iri "range") |
92 | (rdfs-iri "Resource")) |
93 | (make-rdf-triple (rdfs-iri "comment") (rdfs-iri "range") (rdfs-iri "Literal")) |
94 | (make-rdf-triple (rdfs-iri "label") (rdfs-iri "range") (rdfs-iri "Literal")) |
95 | (make-rdf-triple (rdf-iri "value") (rdfs-iri "range") (rdfs-iri "Resource")) |
96 | |
97 | (make-rdf-triple (rdf-iri "Alt") (rdfs-iri "subClassOf") (rdfs-iri "Container")) |
98 | (make-rdf-triple (rdf-iri "Bag") (rdfs-iri "subClassOf") (rdfs-iri "Container")) |
99 | (make-rdf-triple (rdf-iri "Seq") (rdfs-iri "subClassOf") (rdfs-iri "Container")) |
100 | (make-rdf-triple (rdfs-iri "ContainerMembershipProperty") |
101 | (rdfs-iri "subClassOf") (rdf-iri "Property")) |
102 | |
103 | (make-rdf-triple (rdfs-iri "isDefinedBy") (rdfs-iri "subPropertyOf") |
104 | (rdfs-iri "seeAlso")) |
105 | |
106 | (make-rdf-triple (rdfs-iri "Datatype") (rdfs-iri "subClassOf") |
107 | (rdfs-iri "Class")))) |
108 | |
109 | (define (rdfs-axioms-container container) |
110 | (list |
111 | (make-rdf-triple |
112 | container (rdf-iri "type") (rdfs-iri "ContainerMembershipProperty")) |
113 | (make-rdf-triple |
114 | container (rdfs-iri "domain") (rdfs-iri "Resource")) |
115 | (make-rdf-triple |
116 | container (rdfs-iri "range") (rdfs-iri "Resource")))) |
117 | |
118 | (define (rdfs-axioms-types d) |
119 | `(,@(if (and (member xsd:integer d) (member xsd:decimal d)) |
120 | (list (make-rdf-triple xsd:integer (rdfs-iri "subClassOf") xsd:decimal)) |
121 | '()))) |
122 | |
123 | (define (rdf-container-property? p) |
124 | (define rdf-container-property-base (rdf-iri "_")) |
125 | (and (string? p) |
126 | (> (string-length p) (string-length rdf-container-property-base)) |
127 | (equal? (substring p 0 (string-length rdf-container-property-base)) |
128 | rdf-container-property-base) |
129 | (string->number |
130 | (substring p (string-length rdf-container-property-base))))) |
131 | |
132 | (define (rdf-container-properties g) |
133 | (let loop ((answer '()) (g g)) |
134 | (match g |
135 | (() (if (null? answer) (list (rdf-iri "_1")) answer)) |
136 | ((($ rdf-triple subject predicate object) g ...) |
137 | (let* ((answer (if (and (rdf-container-property? subject) |
138 | (not (member subject answer))) |
139 | (cons subject answer) |
140 | answer)) |
141 | (answer (if (and (rdf-container-property? predicate) |
142 | (not (member predicate answer))) |
143 | (cons predicate answer) |
144 | answer)) |
145 | (answer (if (and (rdf-container-property? object) |
146 | (not (member object answer))) |
147 | (cons object answer) |
148 | answer))) |
149 | (loop answer g)))))) |
150 | |
151 | (define (is-iri? node iri) |
152 | (or (and (string? node) (equal? node iri)) |
153 | (and (rdf-datatype? node) (member iri (rdf-datatype-iris node))))) |
154 | |
155 | (define (get-entailments graph subclasses subprops ranges domains types) |
156 | (let ((type-adds |
157 | ;; rdfs 6 8 10 12 and 13 |
158 | (filter |
159 | (lambda (a) a) |
160 | (map |
161 | (match-lambda |
162 | (($ rdf-triple s _ o) |
163 | (cond |
164 | ((is-iri? o (rdf-iri "Property")) |
165 | (make-rdf-triple s (rdfs-iri "subPropertyOf") s)) |
166 | ((is-iri? o (rdfs-iri "Class")) |
167 | (make-rdf-triple s (rdfs-iri "subClassOf") (rdfs-iri "Resource"))) |
168 | ((is-iri? o (rdfs-iri "ContainerMembershipProperty")) |
169 | (make-rdf-triple s (rdfs-iri "subPropertyof") |
170 | (rdfs-iri "member"))) |
171 | ((is-iri? o (rdfs-iri "Datatype")) |
172 | (make-rdf-triple s (rdfs-iri "subClassOf") |
173 | (rdfs-iri "Literal"))) |
174 | (else #f)))) |
175 | types)))) |
176 | (append |
177 | type-adds |
178 | (append-map |
179 | (match-lambda |
180 | (($ rdf-triple s p o) |
181 | `(;; grdf1 |
182 | ,@(if (and (rdf-literal? o) |
183 | (rdf-datatype? (rdf-literal-type o))) |
184 | (list (make-rdf-triple o (rdf-iri "type") (rdf-literal-type o))) |
185 | '()) |
186 | ;; rdf2 |
187 | ,(make-rdf-triple p (rdf-iri "type") (rdf-iri "Property")) |
188 | ;; rdfs2 |
189 | ,@(append-map |
190 | (match-lambda |
191 | (($ rdf-triple subject predicate object) |
192 | (if (equal? subject p) |
193 | (list (make-rdf-triple s (rdf-iri "type") object)) |
194 | '()))) |
195 | domains) |
196 | ;; rdfs3 |
197 | ,@(append-map |
198 | (match-lambda |
199 | (($ rdf-triple subject predicate object) |
200 | (if (equal? subject p) |
201 | (list (make-rdf-triple o (rdf-iri "type") object)) |
202 | '()))) |
203 | ranges) |
204 | ;; rdfs4a |
205 | ,(make-rdf-triple s (rdf-iri "type") (rdfs-iri "Resource")) |
206 | ;; rdfs4b |
207 | ,(make-rdf-triple o (rdf-iri "type") (rdfs-iri "Resource")) |
208 | ;; rdfs5 |
209 | ,@(if (is-iri? p (rdfs-iri "subPropertyOf")) |
210 | (let ((candidates (filter |
211 | (match-lambda |
212 | (($ rdf-triple _ _ object) |
213 | (equal? object s))) |
214 | subprops))) |
215 | (map |
216 | (match-lambda |
217 | (($ rdf-triple subject _ _) |
218 | (make-rdf-triple subject (rdfs-iri "subPropertyOf") |
219 | o))) |
220 | candidates)) |
221 | '()) |
222 | ;; rdfs7 |
223 | ,@(map |
224 | (match-lambda |
225 | (($ rdf-triple _ _ object) |
226 | (make-rdf-triple s object o))) |
227 | (filter |
228 | (match-lambda |
229 | (($ rdf-triple subject _ _) |
230 | (equal? subject p))) |
231 | subprops)) |
232 | ;; rdfs9 |
233 | ,@(if (is-iri? p (rdf-iri "type")) |
234 | (let ((candidates (filter |
235 | (match-lambda |
236 | (($ rdf-triple subject _ _) |
237 | (equal? subject o))) |
238 | subprops))) |
239 | (map |
240 | (match-lambda |
241 | (($ rdf-triple _ _ object) |
242 | (make-rdf-triple s (rdf-iri "type") object))) |
243 | candidates)) |
244 | '()) |
245 | ;; rdfs11 |
246 | ,@(if (is-iri? p (rdfs-iri "subClassOf")) |
247 | (let ((candidates (filter |
248 | (match-lambda |
249 | (($ rdf-triple _ _ object) |
250 | (equal? object s))) |
251 | subclasses))) |
252 | (map |
253 | (match-lambda |
254 | (($ rdf-triple subject _ _) |
255 | (make-rdf-triple subject (rdfs-iri "subClassOf") |
256 | o))) |
257 | candidates)) |
258 | '())))) |
259 | graph)))) |
260 | |
261 | (define (augment g d) |
262 | (let* ((g (append rdfs-axioms g)) |
263 | (g (append (rdfs-axioms-types d) g)) |
264 | (g (append |
265 | ;; rdfs1 |
266 | (map |
267 | (lambda (t) |
268 | (make-rdf-triple t (rdf-iri "type") (rdfs-iri "Datatype"))) |
269 | d))) |
270 | (g (append |
271 | (append-map rdfs-axioms-container (rdf-container-properties g)) |
272 | g))) |
273 | (let loop ((graph '()) (subclasses '()) (subprops '()) (ranges '()) |
274 | (domains '()) (types '())) |
275 | (let inner-loop ((graph graph) (subclasses subclasses) (subprops subprops) |
276 | (ranges ranges) (domains domains) |
277 | (types types) (added? #f) |
278 | (augment-set |
279 | (if (null? graph) |
280 | g |
281 | (pk 'entailments |
282 | (get-entailments |
283 | graph subclasses subprops ranges |
284 | domains types))))) |
285 | (match augment-set |
286 | (() (if added? |
287 | (loop graph subclasses subprops ranges domains types) |
288 | graph)) |
289 | ((t augment-set ...) |
290 | (if (member t graph) |
291 | (inner-loop graph subclasses subprops ranges domains types |
292 | added? augment-set) |
293 | (let ((p (rdf-triple-predicate t))) |
294 | (cond |
295 | ((is-iri? p "subClassOf") |
296 | (inner-loop (cons t graph) (cons t subclasses) subprops |
297 | ranges domains types #t augment-set)) |
298 | ((is-iri? p "subPropertyOf") |
299 | (inner-loop (cons t graph) subclasses (cons t subprops) |
300 | ranges domains types #t augment-set)) |
301 | ((is-iri? p "range") |
302 | (inner-loop (cons t graph) subclasses subprops |
303 | (cons t ranges) domains types #t augment-set)) |
304 | ((is-iri? p "domain") |
305 | (inner-loop (cons t graph) subclasses subprops |
306 | ranges (cons t domains) types #t augment-set)) |
307 | ((is-iri? p "type") |
308 | (inner-loop (cons t graph) subclasses subprops |
309 | ranges domains (cons t types) #t augment-set)) |
310 | (else |
311 | (inner-loop (cons t graph) subclasses subprops |
312 | ranges domains types #t augment-set))))))))))) |
313 | |
314 | (define (entails? g e vocabulary) |
315 | "Return true if g entails e recognizing d" |
316 | (let* ((g (recognize g vocabulary))) |
317 | (or (not (consistent-graph? g vocabulary)) |
318 | (d:entails? (augment g (rdf-vocabulary-datatypes vocabulary)) |
319 | e vocabulary)))) |
320 |