Source code: com/hp/hpl/jena/reasoner/rulesys/test/TestRETE.java
1 /******************************************************************
2 * File: TestRETE.java
3 * Created by: Dave Reynolds
4 * Created on: 10-Jun-2003
5 *
6 * (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
7 * [See end of file]
8 * $Id: TestRETE.java,v 1.10 2005/02/21 12:18:14 andy_seaborne Exp $
9 *****************************************************************/
10 package com.hp.hpl.jena.reasoner.rulesys.test;
11
12 import java.util.*;
13
14 import com.hp.hpl.jena.graph.*;
15 import com.hp.hpl.jena.mem.GraphMem;
16 import com.hp.hpl.jena.reasoner.*;
17 import com.hp.hpl.jena.reasoner.rulesys.*;
18 import com.hp.hpl.jena.reasoner.rulesys.impl.*;
19 import com.hp.hpl.jena.reasoner.test.TestUtil;
20
21 import junit.framework.TestCase;
22 import junit.framework.TestSuite;
23
24 /**
25 *
26 * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
27 * @version $Revision: 1.10 $ on $Date: 2005/02/21 12:18:14 $
28 */
29 public class TestRETE extends TestCase {
30
31 // Useful constants
32 Node_RuleVariable x = new Node_RuleVariable("x", 0);
33 Node_RuleVariable y = new Node_RuleVariable("y", 1);
34 Node_RuleVariable z = new Node_RuleVariable("z", 2);
35 Node p = Node.createURI("p");
36 Node q = Node.createURI("q");
37 Node a = Node.createURI("a");
38 Node b = Node.createURI("b");
39 Node c = Node.createURI("c");
40 Node d = Node.createURI("d");
41 Node e = Node.createURI("e");
42 Node r = Node.createURI("r");
43 Node s = Node.createURI("s");
44 Node n1 = Node.createURI("n1");
45 Node n2 = Node.createURI("n2");
46 Node n3 = Node.createURI("n3");
47 Node n4 = Node.createURI("n4");
48 Node res = Node.createURI("res");
49
50 /**
51 * Boilerplate for junit
52 */
53 public TestRETE( String name ) {
54 super( name );
55 }
56
57 /**
58 * Boilerplate for junit.
59 * This is its own test suite
60 */
61 public static TestSuite suite() {
62 return new TestSuite( TestRETE.class );
63 // TestSuite suite = new TestSuite();
64 // suite.addTest(new TestRETE( "foo" ));
65 // return suite;
66 }
67
68 /**
69 * Test clause compiler and clause filter implementation.
70 */
71 public void testClauseFilter() {
72 doTestClauseFilter( new TriplePattern(a, p, x),
73 new Triple(a, p, b), new Node[]{b, null, null});
74 doTestClauseFilter( new TriplePattern(x, p, b),
75 new Triple(a, p, b), new Node[]{a, null, null});
76 doTestClauseFilter( new TriplePattern(a, p, x), new Triple(b, p, a), null);
77 doTestClauseFilter( new TriplePattern(a, p, x), new Triple(a, q, a), null);
78 doTestClauseFilter( new TriplePattern(x, p, x),
79 new Triple(a, p, a), new Node[]{a, null, null});
80 doTestClauseFilter( new TriplePattern(x, p, x), new Triple(a, p, b), null);
81 doTestClauseFilter(
82 new TriplePattern(a, p, Functor.makeFunctorNode("f", new Node[]{x, c})),
83 new Triple(a, p, a),
84 null);
85 doTestClauseFilter(
86 new TriplePattern(a, p, x),
87 new Triple(a, p, Functor.makeFunctorNode("f", new Node[]{b, c})),
88 new Node[]{Functor.makeFunctorNode("f", new Node[]{b, c}), null, null});
89 doTestClauseFilter(
90 new TriplePattern(a, p, Functor.makeFunctorNode("g", new Node[]{x, c})),
91 new Triple(a, p, Functor.makeFunctorNode("f", new Node[]{b, c})),
92 null);
93 doTestClauseFilter(
94 new TriplePattern(a, p, Functor.makeFunctorNode("f", new Node[]{x, c})),
95 new Triple(a, p, Functor.makeFunctorNode("f", new Node[]{b, c})),
96 new Node[] {b, null, null});
97 doTestClauseFilter(
98 new TriplePattern(x, p, Functor.makeFunctorNode("f", new Node[]{x, c})),
99 new Triple(a, p, Functor.makeFunctorNode("f", new Node[]{a, c})),
100 new Node[] {a, null, null});
101 doTestClauseFilter(
102 new TriplePattern(x, p, Functor.makeFunctorNode("f", new Node[]{x, c})),
103 new Triple(a, p, Functor.makeFunctorNode("f", new Node[]{b, c})),
104 null);
105 }
106
107 /**
108 * Helper for testing clause filters.
109 */
110 private void doTestClauseFilter(TriplePattern pattern, Triple test, Node[] expected) {
111 RETETestNode tnode = new RETETestNode();
112 RETEClauseFilter cf = RETEClauseFilter.compile(pattern, 3, new LinkedList());
113 cf.setContinuation(tnode);
114 cf.fire(test, true);
115 if (expected == null) {
116 assertTrue(tnode.firings == 0);
117 } else {
118 assertTrue(tnode.firings == 1);
119 assertTrue(tnode.isAdd);
120 assertEquals(new BindingVector(expected), tnode.env);
121 }
122 }
123
124 /**
125 * Inner class usable as a dummy RETENode end point for testing.
126 */
127 protected static class RETETestNode implements RETESinkNode {
128 /** The environment passed in */
129 BindingVector env;
130
131 /** The mode flag */
132 boolean isAdd;
133
134 /** True if the fire has been called */
135 int firings = 0;
136
137 /**
138 * Propagate a token to this node.
139 * @param env a set of variable bindings for the rule being processed.
140 * @param isAdd distinguishes between add and remove operations.
141 */
142 public void fire(BindingVector env, boolean isAdd) {
143 firings++;
144 this.env = env;
145 this.isAdd = isAdd;
146 }
147
148 /**
149 * Clone this node in the network across to a different context.
150 * @param netCopy a map from RETENodes to cloned instance so far.
151 * @param context the new context to which the network is being ported
152 */
153 public RETENode clone(Map netCopy, RETERuleContext context) {
154 // Dummy, not used in testing
155 return this;
156 }
157
158 }
159
160 /**
161 * Minimal rule tester to check basic pattern match.
162 */
163 public void testRuleMatcher() {
164 doRuleTest( "[r1: (?a p ?b), (?b q ?c) -> (?a, q, ?c)]" +
165 "[r2: (?a p ?b), (?b p ?c) -> (?a, p, ?c)]" +
166 "[r3: (?a p ?a), (n1 p ?c), (n1, p, ?a) -> (?a, p, ?c)]" +
167 "[r4: (n4 ?p ?a) -> (n4, ?a, ?p)]",
168 new Triple[] {
169 new Triple(n1, p, n2),
170 new Triple(n2, p, n3),
171 new Triple(n2, q, n3),
172 new Triple(n4, p, n4) },
173 new Triple[] {
174 new Triple(n1, p, n2),
175 new Triple(n2, p, n3),
176 new Triple(n2, q, n3),
177 new Triple(n4, p, n4),
178 new Triple(n1, p, n3),
179 new Triple(n1, q, n3),
180 new Triple(n4, n4, p),
181 });
182
183 doRuleTest( "[testRule1: (n1 p ?a) -> (n2, p, ?a)]" +
184 "[testRule2: (n1 q ?a) -> (n2, q, ?a)]" +
185 "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]" +
186 "[axiom1: -> (n1 p n3)]",
187 new Triple[] {},
188 new Triple[] {
189 new Triple(n1, p, n3),
190 new Triple(n2, p, n3)
191 });
192
193 doRuleTest( "[testRule1: (n1 p ?a) -> (n2, p, ?a)]" +
194 "[testRule2: (n1 q ?a) -> (n2, q, ?a)]" +
195 "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]" +
196 "[axiom1: -> (n1 p n3)]",
197 new Triple[] {
198 new Triple(n1, q, n4),
199 new Triple(n1, q, n3)
200 },
201 new Triple[] {
202 new Triple(n1, p, n3),
203 new Triple(n2, p, n3),
204 new Triple(n1, q, n4),
205 new Triple(n2, q, n4),
206 new Triple(n1, q, n3),
207 new Triple(n2, q, n3),
208 new Triple(res, p, n3)
209 });
210 doRuleTest( "[rule1: (?x p ?y), (?x q ?y) -> remove(0)]",
211 new Triple[] {
212 new Triple(n1, p, Util.makeIntNode(1)),
213 new Triple(n1, p, Util.makeIntNode(2)),
214 new Triple(n1, q, Util.makeIntNode(2))
215 },
216 new Triple[] {
217 new Triple(n1, p, Util.makeIntNode(1)),
218 new Triple(n1, q, Util.makeIntNode(2))
219 });
220 }
221
222 /**
223 * Perform a rule test on the raw RETE engine. This requires some fiddling
224 * with dummy parent graphs.
225 */
226 private void doRuleTest(String rules, Triple[] adds, Triple[] expected) {
227 List ruleList = Rule.parseRules(rules);
228 BasicForwardRuleInfGraph infgraph = new BasicForwardRuleInfGraph(null, new ArrayList(), null, new GraphMem());
229 // infgraph.setTraceOn(true);
230 RETEEngine engine = new RETEEngine(infgraph, ruleList);
231 infgraph.prepare();
232 engine.init(true, new FGraph(new GraphMem()));
233 for (int i = 0; i < adds.length; i++) {
234 engine.addTriple(adds[i], true);
235 }
236 engine.runAll();
237 TestUtil.assertIteratorValues(this, infgraph.find(null, null, null), expected);
238 }
239
240 /**
241 * Check that the rulestate cloning keeps two descendent graphs independent.
242 *
243 */
244 public void testRuleClone() {
245 String rules = "[testRule1: (a p ?x) (b p ?x) -> (n1 p ?x) ]" +
246 "[testRule2: (?x q ?y) -> (?x p ?y)]";
247 List ruleList = Rule.parseRules(rules);
248 Graph schema = new GraphMem();
249 schema.add(new Triple(a, q, c));
250 schema.add(new Triple(a, q, d));
251
252 Graph data1 = new GraphMem();
253 data1.add(new Triple(b, q, c));
254
255 Graph data2 = new GraphMem();
256 data2.add(new Triple(b, q, d));
257
258 GenericRuleReasoner reasoner = new GenericRuleReasoner(ruleList);
259 reasoner.setMode(GenericRuleReasoner.FORWARD_RETE);
260 Reasoner boundReasoner = reasoner.bindSchema(schema);
261 InfGraph infgraph1 = boundReasoner.bind(data1);
262 InfGraph infgraph2 = boundReasoner.bind(data2);
263
264 TestUtil.assertIteratorValues(this, infgraph1.find(null, p, null),
265 new Triple[] {
266 new Triple(a, p, c),
267 new Triple(a, p, d),
268 new Triple(b, p, c),
269 new Triple(n1, p, c)
270 });
271
272 TestUtil.assertIteratorValues(this, infgraph2.find(null, p, null),
273 new Triple[] {
274 new Triple(a, p, c),
275 new Triple(a, p, d),
276 new Triple(b, p, d),
277 new Triple(n1, p, d)
278 });
279 }
280 }
281
282
283 /*
284 (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
285 All rights reserved.
286
287 Redistribution and use in source and binary forms, with or without
288 modification, are permitted provided that the following conditions
289 are met:
290
291 1. Redistributions of source code must retain the above copyright
292 notice, this list of conditions and the following disclaimer.
293
294 2. Redistributions in binary form must reproduce the above copyright
295 notice, this list of conditions and the following disclaimer in the
296 documentation and/or other materials provided with the distribution.
297
298 3. The name of the author may not be used to endorse or promote products
299 derived from this software without specific prior written permission.
300
301 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
302 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
303 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
304 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
305 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
306 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
307 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
308 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
309 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
310 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
311 */