rdf.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 rdf) |
19 | #:use-module (ice-9 match) |
20 | #:use-module (rdf rdf) |
21 | #:use-module ((rdf entailment d) #:prefix d:) |
22 | #:use-module (srfi srfi-1) |
23 | #:export (consistent-graph? |
24 | entails?)) |
25 | |
26 | (define (rdf-iri name) |
27 | (string-append "http://www.w3.org/1999/02/22-rdf-syntax-ns#" name)) |
28 | |
29 | (define (consistent-graph? graph vocabulary) |
30 | (define (alist-set lst key val) |
31 | (match lst |
32 | (() (list (cons key val))) |
33 | (((k . v) lst ...) |
34 | (if (equal? k key) |
35 | (cons (cons key val) lst) |
36 | (cons (cons k v) (alist-set lst key val)))))) |
37 | |
38 | (define (compatible? types) |
39 | (match types |
40 | (() #t) |
41 | ((_) #t) |
42 | ((a b ...) |
43 | (and (null? (filter |
44 | (lambda (t) |
45 | ((rdf-vocabulary-compatible? vocabulary) a t)) |
46 | b)) |
47 | (compatible? b))))) |
48 | |
49 | (define (compatible-types? graph) |
50 | (let loop ((graph graph) (type-mappings '())) |
51 | (if (null? graph) |
52 | (null? |
53 | (filter |
54 | (lambda (t) |
55 | (not (compatible? (cdr t)))) |
56 | type-mappings)) |
57 | (let* ((t (car graph))) |
58 | (if (equal? (rdf-triple-predicate t) (rdf-iri "type")) |
59 | (loop |
60 | (cdr graph) |
61 | (alist-set type-mappings (rdf-triple-subject t) |
62 | (cons (rdf-triple-object t) |
63 | (or |
64 | (assoc-ref type-mappings |
65 | (rdf-triple-subject t)) |
66 | '()))))))))) |
67 | |
68 | (and (d:consistent-graph? graph vocabulary) |
69 | (compatible-types? (recognize graph vocabulary)))) |
70 | |
71 | ;; G entails E if E has an instance (where blank nodes are replaced by literals |
72 | ;; or IRIs) that is a subgraph of G. |
73 | ;; |
74 | ;; We re-use similar procedures to verifying isomorphism of graphs, but this time |
75 | ;; blank nodes can also map to literals and IRIs. |
76 | |
77 | ;; We follow appendix A and use a subgraph comparison (like the simple:entails? |
78 | ;; procedure) after augmenting the graph with additional true triples. |
79 | |
80 | (define rdf-axioms |
81 | (list |
82 | (make-rdf-triple (rdf-iri "type") (rdf-iri "type") (rdf-iri "Property")) |
83 | (make-rdf-triple (rdf-iri "subject") (rdf-iri "type") (rdf-iri "Property")) |
84 | (make-rdf-triple (rdf-iri "predicate") (rdf-iri "type") (rdf-iri "Property")) |
85 | (make-rdf-triple (rdf-iri "object") (rdf-iri "type") (rdf-iri "Property")) |
86 | (make-rdf-triple (rdf-iri "first") (rdf-iri "type") (rdf-iri "Property")) |
87 | (make-rdf-triple (rdf-iri "rest") (rdf-iri "type") (rdf-iri "Property")) |
88 | (make-rdf-triple (rdf-iri "value") (rdf-iri "type") (rdf-iri "Property")) |
89 | (make-rdf-triple (rdf-iri "nil") (rdf-iri "type") (rdf-iri "List")))) |
90 | |
91 | (define (rdf-axioms-container container) |
92 | (list |
93 | (make-rdf-triple |
94 | container (rdf-iri "type") (rdf-iri "Property")))) |
95 | |
96 | (define (rdf-container-property? p) |
97 | (define rdf-container-property-base (rdf-iri "_")) |
98 | (and (string? p) |
99 | (> (string-length p) (string-length rdf-container-property-base)) |
100 | (equal? (substring p 0 (string-length rdf-container-property-base)) |
101 | rdf-container-property-base) |
102 | (string->number |
103 | (substring p (string-length rdf-container-property-base))))) |
104 | |
105 | (define (rdf-container-properties g) |
106 | (let loop ((answer '()) (g g)) |
107 | (match g |
108 | (() (if (null? answer) (list (rdf-iri "_1")) answer)) |
109 | ((($ rdf-triple subject predicate object) g ...) |
110 | (let* ((answer (if (and (rdf-container-property? subject) |
111 | (not (member subject answer))) |
112 | (cons subject answer) |
113 | answer)) |
114 | (answer (if (and (rdf-container-property? predicate) |
115 | (not (member predicate answer))) |
116 | (cons predicate answer) |
117 | answer)) |
118 | (answer (if (and (rdf-container-property? object) |
119 | (not (member object answer))) |
120 | (cons object answer) |
121 | answer))) |
122 | (loop answer g)))))) |
123 | |
124 | (define (augment g) |
125 | (let* ((g (append rdf-axioms g)) |
126 | (g (append |
127 | (append-map rdf-axioms-container (rdf-container-properties g)) |
128 | g))) |
129 | (let loop ((g g)) |
130 | (let ((augment-set |
131 | (let loop2 ((g2 g) (augment-set '())) |
132 | (match g2 |
133 | (() augment-set) |
134 | ((($ rdf-triple subject predicate object) g2 ...) |
135 | (let ((type-triple |
136 | (if (and (rdf-literal? object) |
137 | (rdf-datatype? (rdf-literal-type object))) |
138 | (make-rdf-triple object (rdf-iri "type") |
139 | (rdf-literal-type object)) |
140 | #f)) |
141 | (property-triple |
142 | (make-rdf-triple predicate (rdf-iri "type") |
143 | (rdf-iri "Property")))) |
144 | (loop2 |
145 | g2 |
146 | (append |
147 | (if (or (not type-triple) (member type-triple g) |
148 | (member type-triple augment-set)) |
149 | '() |
150 | (list type-triple)) |
151 | (if (or (member property-triple g) |
152 | (member type-triple augment-set)) |
153 | '() |
154 | (list property-triple)) |
155 | augment-set)))))))) |
156 | (if (null? augment-set) |
157 | g |
158 | (loop (append augment-set g))))))) |
159 | |
160 | (define (equiv? n1 n2) |
161 | (match (list n1 n2) |
162 | ((($ rdf-literal l1 ($ rdf-datatype _ _ _ _ lexical->value1 _) lang1) |
163 | ($ rdf-literal l2 ($ rdf-datatype _ _ _ _ lexical->value2 _) lang2)) |
164 | (and (equal? lang1 lang2) |
165 | (equal? (lexical->value1 l1)) |
166 | (equal? (lexical->value2 l2)))) |
167 | (_ (equal? n1 n2)))) |
168 | |
169 | (define (validate-mapping mapping g1 g2) |
170 | (match g1 |
171 | ('() #t) |
172 | ((t1 g1 ...) |
173 | (and (not (null? (filter |
174 | (lambda (t2) |
175 | (let ((s1 (rdf-triple-subject t1)) |
176 | (s2 (rdf-triple-subject t2)) |
177 | (p1 (rdf-triple-predicate t1)) |
178 | (p2 (rdf-triple-predicate t2)) |
179 | (o1 (rdf-triple-object t1)) |
180 | (o2 (rdf-triple-object t2))) |
181 | (and |
182 | (if (blank-node? s1) |
183 | (equiv? (assoc-ref mapping s1) s2) |
184 | (equiv? s1 s2)) |
185 | (equal? p1 p2) |
186 | (if (blank-node? o1) |
187 | (equiv? (assoc-ref mapping o1) o2) |
188 | (equiv? o1 o2))))) |
189 | g2))) |
190 | (validate-mapping mapping g1 g2))))) |
191 | |
192 | (define (entails? g e vocabulary) |
193 | "Return true if g entails e" |
194 | (let ((g (recognize g vocabulary))) |
195 | (or (not (consistent-graph? g vocabulary)) |
196 | (d:entails? (augment g) e vocabulary)))) |
197 |