guile-rdf/rdf/entailment/rdfs.scm

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