rdf-isomorphic 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +1 -0
- data/README +25 -18
- data/VERSION +1 -1
- data/lib/rdf/isomorphic.rb +140 -102
- metadata +58 -23
- data/README.md +0 -70
data/AUTHORS
CHANGED
data/README
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RDF Isomorphism
|
2
2
|
|
3
|
-
|
4
|
-
includes RDF::Repository, RDF::Graph, query results, and more.
|
3
|
+
This is an RDF.rb plugin for RDF Isomorphism functionality for RDF::Enumerables.
|
4
|
+
That includes RDF::Repository, RDF::Graph, query results, and more.
|
5
5
|
|
6
6
|
For more information about RDF.rb, see <http://rdf.rubyforge.org>
|
7
7
|
|
@@ -9,42 +9,46 @@ For more information about RDF.rb, see <http://rdf.rubyforge.org>
|
|
9
9
|
|
10
10
|
require 'rdf/isomorphic'
|
11
11
|
require 'rdf/ntriples'
|
12
|
+
|
13
|
+
|
12
14
|
a = RDF::Repository.load './tests/isomorphic/test1/test1-1.nt'
|
13
15
|
a.first
|
14
|
-
|
16
|
+
# < RDF::Statement:0xd344c4(<http://example.org/a> <http://example.org/prop> <_:abc> .) >
|
15
17
|
|
16
18
|
b = RDF::Repository.load './tests/isomorphic/test1/test1-2.nt'
|
17
19
|
b.first
|
18
|
-
|
20
|
+
# < RDF::Statement:0xd3801a(<http://example.org/a> <http://example.org/prop> <_:testing> .) >
|
19
21
|
|
20
22
|
a.isomorphic_with? b
|
21
|
-
|
23
|
+
# true
|
24
|
+
|
22
25
|
a.bijection_to b
|
23
|
-
|
26
|
+
# { #<RDF::Node:0xd345a0(_:abc)>=>#<RDF::Node:0xd38574(_:testing)> }
|
27
|
+
|
24
28
|
|
25
29
|
## Algorithm
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
<http://
|
31
|
+
The algorithm used here is very similar to the one described by Jeremy Carroll
|
32
|
+
in <http://www.hpl.hp.com/techreports/2001/HPL-2001-293.pdf>. See
|
33
|
+
<http://blog.datagraph.org/2010/03/rdf-isomorphism>.
|
30
34
|
|
31
35
|
Generally speaking, the Carroll algorithm is a very good fit for RDF graphs. It
|
32
36
|
is a specialization of the naive factorial-time test for graph isomorphism,
|
33
|
-
|
37
|
+
wherein non-anonymous RDF data lets us eliminate vast quantities of options well
|
34
38
|
before we try them. Pathological cases, such as graphs which only contain
|
35
|
-
anonymous resources,
|
39
|
+
anonymous resources, may experience poor performance.
|
36
40
|
|
37
41
|
### Equality
|
38
42
|
|
39
|
-
Although it was considered to provide `==` to mean isomorphic, RDF isomorphism
|
40
|
-
factorial-complexity problem and it seemed better to perhaps
|
41
|
-
a commonly used method for that. But it's really useful for
|
42
|
-
libraries. Try this:
|
43
|
+
Although it was considered to provide `==` to mean isomorphic, RDF isomorphism
|
44
|
+
can sometimes be a factorial-complexity problem and it seemed better to perhaps
|
45
|
+
not overwrite such a commonly used method for that. But it's really useful for
|
46
|
+
specs in RDF libraries. Try this in your tests:
|
43
47
|
|
44
48
|
require 'rdf/isomorphic'
|
45
49
|
module RDF
|
46
50
|
module Isomorphic
|
47
|
-
alias_method :==, :isomorphic_with
|
51
|
+
alias_method :==, :isomorphic_with?
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
@@ -57,7 +61,8 @@ libraries. Try this:
|
|
57
61
|
end
|
58
62
|
|
59
63
|
### Information
|
60
|
-
* Author: Ben Lavender <blavender@gmail.com>
|
64
|
+
* Author: Ben Lavender <blavender@gmail.com> - <http://bhuga.net>
|
65
|
+
* Author: Arto Bendiken <arto.bendiken@gmail.com> - <http://ar.to>
|
61
66
|
* Source: <http://github.com/bhuga/RDF-Isomorphic>
|
62
67
|
* Issues: <http://github.com/bhuga/RDF-Isomorphic/issues>
|
63
68
|
|
@@ -67,4 +72,6 @@ libraries. Try this:
|
|
67
72
|
|
68
73
|
### "License"
|
69
74
|
|
70
|
-
rdf-isomorphic is free and unemcumbered software in the public domain. For
|
75
|
+
rdf-isomorphic is free and unemcumbered software in the public domain. For
|
76
|
+
more information, see the accompanying UNLICENSE file or <http://unlicense.org>
|
77
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.1
|
data/lib/rdf/isomorphic.rb
CHANGED
@@ -16,39 +16,41 @@ module RDF
|
|
16
16
|
# @return [Boolean]
|
17
17
|
# @example
|
18
18
|
# repository_a.isomorphic_with repository_b #=> true
|
19
|
-
def isomorphic_with(other)
|
19
|
+
def isomorphic_with?(other)
|
20
20
|
!(bijection_to(other).nil?)
|
21
21
|
end
|
22
22
|
|
23
|
-
alias_method :isomorphic?, :isomorphic_with
|
24
|
-
alias_method :isomorphic_with?, :isomorphic_with
|
23
|
+
alias_method :isomorphic?, :isomorphic_with?
|
25
24
|
|
26
25
|
|
27
26
|
# Returns a hash of RDF::Nodes => RDF::Nodes representing an isomorphic
|
28
|
-
# bijection of this RDF::Enumerable's
|
29
|
-
# cannot be found.
|
27
|
+
# bijection of this RDF::Enumerable's to another RDF::Enumerable's blank
|
28
|
+
# nodes, or nil if a bijection cannot be found.
|
30
29
|
# @example
|
31
30
|
# repository_a.bijection_to repository_b
|
32
31
|
# @param other [RDF::Enumerable]
|
33
32
|
# @return [Hash, nil]
|
34
33
|
def bijection_to(other)
|
35
|
-
|
36
|
-
each_statement do |
|
37
|
-
|
38
|
-
named_statements_match = other.has_statement?(statement)
|
39
|
-
end
|
40
|
-
break unless named_statements_match
|
34
|
+
|
35
|
+
grounded_stmts_match = each_statement.all? do | stmt |
|
36
|
+
stmt.has_blank_nodes? || other.has_statement?(stmt)
|
41
37
|
end
|
42
38
|
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
if grounded_stmts_match
|
40
|
+
# blank_stmts and other_blank_stmts are just a performance
|
41
|
+
# consideration--we could just as well pass in self and other. But we
|
42
|
+
# will be iterating over this list quite a bit during the algorithm, so
|
43
|
+
# we break it down to the parts we're interested in.
|
46
44
|
blank_stmts = find_all { |statement| statement.has_blank_nodes? }
|
47
45
|
other_blank_stmts = other.find_all { |statement| statement.has_blank_nodes? }
|
48
|
-
|
49
|
-
|
46
|
+
|
47
|
+
nodes = RDF::Isomorphic.blank_nodes_in(blank_stmts)
|
48
|
+
other_nodes = RDF::Isomorphic.blank_nodes_in(other_blank_stmts)
|
50
49
|
build_bijection_to blank_stmts, nodes, other_blank_stmts, other_nodes
|
50
|
+
else
|
51
|
+
nil
|
51
52
|
end
|
53
|
+
|
52
54
|
end
|
53
55
|
|
54
56
|
private
|
@@ -60,90 +62,76 @@ module RDF
|
|
60
62
|
# relevant pseudocode.
|
61
63
|
#
|
62
64
|
# Many more comments are in the method itself.
|
65
|
+
#
|
66
|
+
# @param [RDF::Enumerable] anon_stmts
|
67
|
+
# @param [Array] nodes
|
68
|
+
# @param [RDF::Enumerable] other_anon_stmts
|
69
|
+
# @param [Array] other_nodes
|
70
|
+
# @param [Hash] these_grounded_hashes
|
71
|
+
# @param [Hash] other_grounded_hashes
|
72
|
+
# @return [nil,Hash]
|
63
73
|
# @private
|
64
|
-
def build_bijection_to(anon_stmts, nodes, other_anon_stmts, other_nodes,
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
# potential_hashes and hashes, is a node which has no ungrounded
|
76
|
-
# anonymous neighbors in a relevant statement.
|
77
|
-
potential_hashes = {}
|
78
|
-
[ [anon_stmts,nodes], [other_anon_stmts,other_nodes] ].each do | tuple |
|
79
|
-
hash_needed = true
|
80
|
-
while hash_needed
|
81
|
-
hash_needed = false
|
82
|
-
tuple.last.each do | node |
|
83
|
-
unless hashes.member? node
|
84
|
-
grounded, hash = node_hash_for(node, tuple.first, hashes) unless hashes.member? node
|
85
|
-
if grounded
|
86
|
-
hash_needed = true
|
87
|
-
hashes[node] = hash
|
88
|
-
end
|
89
|
-
potential_hashes[node] = hash
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
74
|
+
def build_bijection_to(anon_stmts, nodes, other_anon_stmts, other_nodes, these_grounded_hashes = {}, other_grounded_hashes = {})
|
75
|
+
|
76
|
+
|
77
|
+
# Create a hash signature of every node, based on the signature of
|
78
|
+
# statements it exists in.
|
79
|
+
# We also save hashes of nodes that cannot be reliably known; we will use
|
80
|
+
# that information to eliminate possible recursion combinations.
|
81
|
+
#
|
82
|
+
# Any mappings given in the method parameters are considered grounded.
|
83
|
+
these_hashes, these_ungrounded_hashes = RDF::Isomorphic.hash_nodes(anon_stmts, nodes, these_grounded_hashes)
|
84
|
+
other_hashes, other_ungrounded_hashes = RDF::Isomorphic.hash_nodes(other_anon_stmts, other_nodes, other_grounded_hashes)
|
94
85
|
|
95
|
-
# see variables above
|
96
|
-
bijection = {}
|
97
|
-
bijection_hashes = hashes.dup
|
98
86
|
|
99
|
-
#
|
100
|
-
|
87
|
+
# Using the created hashes, map nodes to other_nodes
|
88
|
+
bijection = {}
|
101
89
|
nodes.each do | node |
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
other_nodes.any? do | item | k.eql?(item) end
|
90
|
+
other_node, other_hash = other_hashes.find do | other_node, other_hash |
|
91
|
+
# we need to use eql?, as coincedentally-named bnode identifiers are == in rdf.rb
|
92
|
+
these_hashes[node].eql? other_hash
|
106
93
|
end
|
107
|
-
next unless
|
108
|
-
|
109
|
-
|
110
|
-
|
94
|
+
next unless other_node
|
95
|
+
bijection[node] = other_node
|
96
|
+
|
97
|
+
# we need to delete , as we don't want two nodes with the same hash
|
98
|
+
# to be mapped to the same other_node.
|
99
|
+
other_hashes.delete other_node
|
111
100
|
end
|
112
101
|
|
113
|
-
#
|
102
|
+
# bijection is now a mapping of nodes to other_nodes. If all are
|
103
|
+
# accounted for on both sides, we have a bijection.
|
114
104
|
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
bijection
|
119
|
-
# So we've got unhashed nodes that can't be definitively grounded. Make
|
120
|
-
# a tentative bijection between two with identical ungrounded signatures
|
121
|
-
# in the graph and recurse.
|
122
|
-
else
|
105
|
+
# If not, we will speculatively mark pairs with matching ungrounded
|
106
|
+
# hashes as bijected and recurse.
|
107
|
+
unless (bijection.keys.sort == nodes.sort) && (bijection.values.sort == other_nodes.sort)
|
123
108
|
bijection = nil
|
124
|
-
nodes.
|
109
|
+
nodes.any? do | node |
|
110
|
+
|
125
111
|
# We don't replace grounded nodes' hashes
|
126
|
-
next if
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
112
|
+
next if these_hashes.member? node
|
113
|
+
other_nodes.any? do | other_node |
|
114
|
+
|
115
|
+
# We don't replace grounded other_nodes' hashes
|
116
|
+
next if other_hashes.member? other_node
|
117
|
+
|
118
|
+
# The ungrounded signature must match for this to potentially work
|
119
|
+
next unless these_ungrounded_hashes[node] == other_ungrounded_hashes[other_node]
|
120
|
+
|
133
121
|
hash = Digest::SHA1.hexdigest(node.to_s)
|
134
|
-
|
135
|
-
bijection = build_bijection_to(anon_stmts, nodes, other_anon_stmts, other_nodes, hashes.merge(test_hashes))
|
122
|
+
bijection = build_bijection_to(anon_stmts, nodes, other_anon_stmts, other_nodes, these_hashes.merge( node => hash), other_hashes.merge(other_node => hash))
|
136
123
|
end
|
137
|
-
|
124
|
+
bijection
|
138
125
|
end
|
139
|
-
bijection
|
140
126
|
end
|
127
|
+
|
128
|
+
bijection
|
141
129
|
end
|
142
130
|
|
131
|
+
# Blank nodes appearing in given list of statements
|
143
132
|
# @private
|
144
133
|
# @return [RDF::Node]
|
145
|
-
|
146
|
-
def blank_nodes_in(blank_stmt_list)
|
134
|
+
def self.blank_nodes_in(blank_stmt_list)
|
147
135
|
nodes = []
|
148
136
|
blank_stmt_list.each do | statement |
|
149
137
|
nodes << statement.object if statement.object.anonymous?
|
@@ -151,7 +139,46 @@ module RDF
|
|
151
139
|
end
|
152
140
|
nodes.uniq
|
153
141
|
end
|
154
|
-
|
142
|
+
|
143
|
+
# Given a set of statements, create a mapping of node => SHA1 for a given
|
144
|
+
# set of blank nodes. grounded_hashes is a mapping of node => SHA1 pairs
|
145
|
+
# that we will take as a given, and use those to make more specific
|
146
|
+
# signatures of other nodes.
|
147
|
+
#
|
148
|
+
# Returns a tuple of hashes: one of grounded hashes, and one of all
|
149
|
+
# hashes. grounded hashes are based on non-blank nodes and grounded blank
|
150
|
+
# nodes, and can be used to determine if a node's signature matches
|
151
|
+
# another.
|
152
|
+
#
|
153
|
+
# @param [Array] statements
|
154
|
+
# @param [Array] nodes
|
155
|
+
# @param [Hash] grounded_hashes
|
156
|
+
# @private
|
157
|
+
# @return [Hash, Hash]
|
158
|
+
def self.hash_nodes(statements, nodes, grounded_hashes)
|
159
|
+
hashes = grounded_hashes.dup
|
160
|
+
ungrounded_hashes = {}
|
161
|
+
hash_needed = true
|
162
|
+
|
163
|
+
# We may have to go over the list multiple times. If a node is marked as
|
164
|
+
# grounded, other nodes can then use it to decide their own state of
|
165
|
+
# grounded.
|
166
|
+
while hash_needed
|
167
|
+
hash_needed = false
|
168
|
+
nodes.each do | node |
|
169
|
+
unless hashes.member? node
|
170
|
+
grounded, hash = node_hash_for(node, statements, hashes)
|
171
|
+
if grounded
|
172
|
+
hash_needed = true
|
173
|
+
hashes[node] = hash
|
174
|
+
end
|
175
|
+
ungrounded_hashes[node] = hash
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
[hashes,ungrounded_hashes]
|
180
|
+
end
|
181
|
+
|
155
182
|
# Generate a hash for a node based on the signature of the statements it
|
156
183
|
# appears in. Signatures consist of grounded elements in statements
|
157
184
|
# associated with a node, that is, anything but an ungrounded anonymous
|
@@ -166,53 +193,64 @@ module RDF
|
|
166
193
|
# for the hash
|
167
194
|
# @private
|
168
195
|
# @return [Boolean, String]
|
169
|
-
def node_hash_for(node,statements,hashes)
|
196
|
+
def self.node_hash_for(node,statements,hashes)
|
170
197
|
statement_signatures = []
|
171
198
|
grounded = true
|
172
199
|
statements.each do | statement |
|
173
200
|
if (statement.object == node) || (statement.subject == node)
|
174
|
-
statement_signatures << hash_string_for(statement,hashes)
|
201
|
+
statement_signatures << hash_string_for(statement,hashes,node)
|
175
202
|
[statement.subject, statement.object].each do | resource |
|
176
|
-
grounded = false unless grounded(resource, hashes)
|
203
|
+
grounded = false unless grounded(resource, hashes) || resource == node
|
177
204
|
end
|
178
205
|
end
|
179
206
|
end
|
207
|
+
# Note that we sort the signatures--without a canonical ordering,
|
208
|
+
# we might get different hashes for equivalent nodes.
|
180
209
|
[grounded,Digest::SHA1.hexdigest(statement_signatures.sort.to_s)]
|
181
210
|
end
|
182
211
|
|
183
|
-
# Provide a string signature for the given statement
|
212
|
+
# Provide a string signature for the given statement, collecting
|
213
|
+
# string signatures for grounded node elements.
|
214
|
+
# return [String]
|
184
215
|
# @private
|
185
|
-
def hash_string_for(statement,hashes)
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
216
|
+
def self.hash_string_for(statement,hashes,node)
|
217
|
+
string = ""
|
218
|
+
string << string_for_node(statement.subject,hashes,node)
|
219
|
+
string << statement.predicate.to_s
|
220
|
+
string << string_for_node(statement.object,hashes,node)
|
221
|
+
string
|
191
222
|
end
|
192
223
|
|
193
224
|
# Returns true if a given node is grounded
|
225
|
+
# A node is groundd if it is not a blank node or it is included
|
226
|
+
# in the given mapping of grounded nodes.
|
227
|
+
# @return [Boolean]
|
194
228
|
# @private
|
195
|
-
def grounded(node, hashes)
|
229
|
+
def self.grounded(node, hashes)
|
196
230
|
(!(node.anonymous?)) || (hashes.member? node)
|
197
231
|
end
|
198
232
|
|
199
233
|
# Provides a string for the given node for use in a string signature
|
234
|
+
# Non-anonymous nodes will return their string form. Grounded anonymous
|
235
|
+
# nodes will return their hashed form.
|
236
|
+
# @return [String]
|
200
237
|
# @private
|
201
|
-
def string_for_node(node, hashes)
|
202
|
-
|
203
|
-
|
238
|
+
def self.string_for_node(node, hashes,target)
|
239
|
+
case
|
240
|
+
when node == target
|
241
|
+
"itself"
|
242
|
+
when node.anonymous? && hashes.member?(node)
|
204
243
|
hashes[node]
|
244
|
+
when node.anonymous?
|
245
|
+
"a blank node"
|
205
246
|
else
|
206
|
-
|
207
|
-
end
|
208
|
-
else
|
209
|
-
node.to_s
|
247
|
+
node.to_s
|
210
248
|
end
|
211
249
|
end
|
212
250
|
end
|
213
251
|
|
214
252
|
|
215
|
-
|
253
|
+
# Extend RDF::Enumerables with these functions.
|
216
254
|
module Enumerable
|
217
255
|
include RDF::Isomorphic
|
218
256
|
end
|
metadata
CHANGED
@@ -1,48 +1,80 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdf-isomorphic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Ben Lavender
|
13
|
+
- Arto Bendiken
|
8
14
|
autorequire:
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-03-12 00:00:00 +01:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: rdf
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 1
|
31
|
+
- 0
|
32
|
+
version: 0.1.0
|
17
33
|
type: :runtime
|
18
|
-
|
19
|
-
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rdf-spec
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
20
39
|
requirements:
|
21
40
|
- - ">="
|
22
41
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
- 1
|
45
|
+
- 0
|
46
|
+
version: 0.1.0
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
25
49
|
- !ruby/object:Gem::Dependency
|
26
50
|
name: rspec
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
30
53
|
requirements:
|
31
54
|
- - ">="
|
32
55
|
- !ruby/object:Gem::Version
|
33
|
-
|
34
|
-
|
56
|
+
segments:
|
57
|
+
- 1
|
58
|
+
- 3
|
59
|
+
- 0
|
60
|
+
version: 1.3.0
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
35
63
|
- !ruby/object:Gem::Dependency
|
36
64
|
name: yard
|
37
|
-
|
38
|
-
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
40
67
|
requirements:
|
41
68
|
- - ">="
|
42
69
|
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
|
45
|
-
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
- 5
|
73
|
+
- 3
|
74
|
+
version: 0.5.3
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
description: RDF.rb plugin for graph bijections and isomorphic equivalence.
|
46
78
|
email: blavender@gmail.com
|
47
79
|
executables: []
|
48
80
|
|
@@ -55,7 +87,6 @@ files:
|
|
55
87
|
- README
|
56
88
|
- UNLICENSE
|
57
89
|
- VERSION
|
58
|
-
- README.md
|
59
90
|
- lib/rdf/isomorphic.rb
|
60
91
|
has_rdoc: false
|
61
92
|
homepage: http://rdf.rubyforge.org/
|
@@ -70,20 +101,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
101
|
requirements:
|
71
102
|
- - ">="
|
72
103
|
- !ruby/object:Gem::Version
|
104
|
+
segments:
|
105
|
+
- 1
|
106
|
+
- 8
|
107
|
+
- 2
|
73
108
|
version: 1.8.2
|
74
|
-
version:
|
75
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
110
|
requirements:
|
77
111
|
- - ">="
|
78
112
|
- !ruby/object:Gem::Version
|
113
|
+
segments:
|
114
|
+
- 0
|
79
115
|
version: "0"
|
80
|
-
version:
|
81
116
|
requirements: []
|
82
117
|
|
83
|
-
rubyforge_project: rdf
|
84
|
-
rubygems_version: 1.3.
|
118
|
+
rubyforge_project: rdf
|
119
|
+
rubygems_version: 1.3.6
|
85
120
|
signing_key:
|
86
121
|
specification_version: 3
|
87
|
-
summary:
|
122
|
+
summary: RDF.rb plugin for graph bijections and isomorphic equivalence.
|
88
123
|
test_files: []
|
89
124
|
|
data/README.md
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
# RDF Isomorphism
|
2
|
-
|
3
|
-
Provides RDF Isomorphism functionality for RDF.rb RDF::Enumerables. That
|
4
|
-
includes RDF::Repository, RDF::Graph, query results, and more.
|
5
|
-
|
6
|
-
For more information about RDF.rb, see <http://rdf.rubyforge.org>
|
7
|
-
|
8
|
-
## Synopsis:
|
9
|
-
|
10
|
-
require 'rdf/isomorphic'
|
11
|
-
require 'rdf/ntriples'
|
12
|
-
a = RDF::Repository.load './tests/isomorphic/test1/test1-1.nt'
|
13
|
-
a.first
|
14
|
-
=> < RDF::Statement:0xd344c4(<http://example.org/a> <http://example.org/prop> <_:abc> .) >
|
15
|
-
|
16
|
-
b = RDF::Repository.load './tests/isomorphic/test1/test1-2.nt'
|
17
|
-
b.first
|
18
|
-
=> < RDF::Statement:0xd3801a(<http://example.org/a> <http://example.org/prop> <_:testing> .) >
|
19
|
-
|
20
|
-
a.isomorphic_with? b
|
21
|
-
=> true
|
22
|
-
a.bijection_to b
|
23
|
-
=> { #<RDF::Node:0xd345a0(_:abc)>=>#<RDF::Node:0xd38574(_:testing)> }
|
24
|
-
|
25
|
-
## Algorithm
|
26
|
-
|
27
|
-
More discussion on the algorithm used will be in a forthcoming blog post, but
|
28
|
-
it is very similar to the one described by Jeremy Carroll in
|
29
|
-
<http://www.hpl.hp.com/techreports/2001/HPL-2001-293.pdf>.
|
30
|
-
|
31
|
-
Generally speaking, the Carroll algorithm is a very good fit for RDF graphs. It
|
32
|
-
is a specialization of the naive factorial-time test for graph isomorphism,
|
33
|
-
wherin non-anonymous RDF data lets us eliminate vast quantities of options well
|
34
|
-
before we try them. Pathological cases, such as graphs which only contain
|
35
|
-
anonymous resources, will experience poor performance.
|
36
|
-
|
37
|
-
### Equality
|
38
|
-
|
39
|
-
Although it was considered to provide `==` to mean isomorphic, RDF isomorphism is a
|
40
|
-
factorial-complexity problem and it seemed better to perhaps not overwrite such
|
41
|
-
a commonly used method for that. But it's really useful for specs in RDF
|
42
|
-
libraries. Try this:
|
43
|
-
|
44
|
-
require 'rdf/isomorphic'
|
45
|
-
module RDF
|
46
|
-
module Isomorphic
|
47
|
-
alias_method :==, :isomorphic_with
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe 'something' do
|
52
|
-
context 'does' do
|
53
|
-
it 'should be equal' do
|
54
|
-
repository_a.should == repository_b
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
### Information
|
60
|
-
* Author: Ben Lavender <blavender@gmail.com>
|
61
|
-
* Source: <http://github.com/bhuga/RDF-Isomorphic>
|
62
|
-
* Issues: <http://github.com/bhuga/RDF-Isomorphic/issues>
|
63
|
-
|
64
|
-
### See also
|
65
|
-
* RDF.rb: <http://rdf.rubyforge.org>
|
66
|
-
* RDF.rb source: <http://github.com/bendiken/rdf>
|
67
|
-
|
68
|
-
### "License"
|
69
|
-
|
70
|
-
rdf-isomorphic is free and unemcumbered software in the public domain. For more information, see the accompanying UNLICENSE file or <http://unlicense.org>
|