1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2009-2022, University of Amsterdam 7 CWI, Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(rdf_compare, 38 [ rdf_equal_graphs/3 % +Graph1, +Graph2, -Substitutions 39 ]). 40:- if(exists_source(library(semweb/rdf_db))). 41:- use_module(library(semweb/rdf_db),[lang_equal/2,rdf_is_bnode/1]). 42:- endif. 43:- autoload(library(apply),[partition/4,maplist/3]). 44:- use_module(library(debug),[debug/3]). 45:- autoload(library(lists),[select/3]). 46 47/** <module> Compare RDF graphs 48 49This library provides predicates that compare RDF graphs. The current 50version only provides one predicate: rdf_equal_graphs/3 verifies that 51two graphs are identical after proper labeling of the blank nodes. 52 53Future versions of this library may contain more advanced operations, 54such as diffing two graphs. 55*/ 56 57 58%! rdf_equal_graphs(+GraphA, +GraphB, -Substition) is semidet. 59% 60% True if GraphA and GraphB are the same under Substition. 61% Substition is a list of BNodeA = BNodeB, where BNodeA is a blank 62% node that appears in GraphA and BNodeB is a blank node that 63% appears in GraphB. 64% 65% @param GraphA is a list of rdf(S,P,O) terms 66% @param GraphB is a list of rdf(S,P,O) terms 67% @param Substition is a list if NodeA = NodeB terms. 68% @tbd The current implementation is rather naive. After 69% dealing with the subgraphs that contain no bnodes, 70% it performs a fully non-deterministic substitution. 71 72rdf_equal_graphs(A, B, Substitutions) :- 73 sort(A, SA), 74 sort(B, SB), 75 partition(contains_bnodes, SA, VA, GA), 76 partition(contains_bnodes, SB, VB, GB), 77 ( GA == GB 78 -> true 79 ; maplist(compare_triple, GA, GB) 80 ), 81 compare_list(VA, VB, [], Substitutions), 82 !. 83 84contains_bnodes(rdf(S,P,O)) :- 85 ( node_id(S) 86 ; node_id(P) 87 ; node_id(O) 88 ), 89 !. 90 91compare_list([], [], S, S). 92compare_list([H1|T1], In2, S0, S) :- 93 select(H2, In2, T2), 94 compare_triple(H1, H2, S0, S1), 95 compare_list(T1, T2, S1, S). 96 97compare_triple(T1, T2) :- 98 compare_triple(T1,T2,[],[]). 99 100compare_triple(rdf(Subj1,P1,O1), rdf(Subj2, P2, O2), S0, S) :- 101 compare_field(Subj1, Subj2, S0, S1), 102 compare_field(P1, P2, S1, S2), 103 compare_field(O1, O2, S2, S). 104 105compare_field(X, X, S, S) :- !. 106compare_field(literal(X), xml(X), S, S) :- !. % TBD 107compare_field(literal(lang(L1,X)), literal(lang(L2,X)), S, S) :- 108 !, 109 lang_equal(L1, L2). 110compare_field(X, Id, S, S) :- 111 memberchk(X=Id, S), 112 !. 113compare_field(X, Y, S, [X=Y|S]) :- 114 \+ memberchk(X=_, S), 115 node_id(X), 116 node_id(Y), 117 debug(rdf_compare, 'Assume ~w = ~w~n', [X, Y]). 118 119node_id(node(_)) :- !. 120node_id(X) :- 121 rdf_is_bnode(X). 122 123:- if(\+current_predicate(rdf_is_bnode/1)). 124rdf_is_bnode(Node) :- 125 atom(Node), 126 sub_atom(Node, 0, _, _, '_:'). 127:- endif. 128:- if(\+current_predicate(lang_equal/2)). 129lang_equal(X, Y) :- 130 downcase_atom(X, L), 131 downcase_atom(Y, L). 132:- endif.