private static ArrayList prove(Hashtable cases, SelectableSource world, Statement[] goal, int maxNumberOfSteps) { // This is the main routine from euler.js. // cases: Resource (predicate) => IList of Sequent if (world != null) // cache our queries to the world, if a world is provided { world = new SemWeb.Stores.CachedSource(world); } StatementMap cached_subproofs = new StatementMap(); // Create the queue and add the first item. ArrayList queue = new ArrayList(); { QueueItem start = new QueueItem(); start.env = new Hashtable(); start.rule = new Sequent(Statement.All, goal, null); start.src = null; start.ind = 0; start.parent = null; start.ground = new ArrayList(); queue.Add(start); if (Debug) { Console.Error.WriteLine("Euler: Queue: " + start); } } // The evidence array holds the results of this proof. ArrayList evidence = new ArrayList(); // Track how many steps we take to not go over the limit. int step = 0; // Process the queue until it's empty or we reach our step limit. while (queue.Count > 0) { // deal with the QueueItem at the top of the queue QueueItem c = (QueueItem)queue[queue.Count - 1]; queue.RemoveAt(queue.Count - 1); ArrayList g = new ArrayList(c.ground); // have we done too much? step++; if (maxNumberOfSteps != -1 && step >= maxNumberOfSteps) { if (Debug) { Console.Error.WriteLine("Euler: Maximum number of steps exceeded. Giving up."); } return(null); } // if each statement in the body of the sequent has been proved if (c.ind == c.rule.body.Length) { // if this is the top-level sequent being proved; we've found a complete evidence for the goal if (c.parent == null) { EvidenceItem ev = new EvidenceItem(); ev.head = new Statement[c.rule.body.Length]; bool canRepresentHead = true; for (int i = 0; i < c.rule.body.Length; i++) { ev.head[i] = evaluate(c.rule.body[i], c.env); if (ev.head[i].AnyNull) // can't represent it: literal in subject position, for instance { canRepresentHead = false; } } ev.body = c.ground; ev.env = c.env; if (Debug) { Console.Error.WriteLine("Euler: Found Evidence: " + ev); } if (!canRepresentHead) { continue; } evidence.Add(ev); // this is a subproof of something; whatever it is a subgroup for can // be incremented } else { // if the rule had no body, it was just an axiom and we represent that with Ground if (c.rule.body.Length != 0) { g.Add(new Ground(c.rule, c.env)); } // advance the parent being proved and put the advanced // parent into the queue; unify the parent variable assignments // with this one's QueueItem r = new QueueItem(); r.rule = c.parent.rule; r.src = c.parent.src; r.ind = c.parent.ind; r.parent = c.parent.parent; r.env = (Hashtable)c.parent.env.Clone(); r.ground = g; unify(c.rule.head, c.env, r.rule.body[r.ind], r.env, true); r.ind++; queue.Add(r); if (Debug) { Console.Error.WriteLine("Euler: Queue Advancement: " + r); } // The number of live children for this parent is decremented since we are // done with this subproof, but we store the result for later. if (c.parent.solutions == null) { c.parent.solutions = new StatementList(); } c.parent.solutions.Add(evaluate(r.rule.body[r.ind - 1], r.env)); decrementLife(c.parent, cached_subproofs); } // this sequent still has parts of the body left to be proved; try to // find evidence for the next statement in the body, and if we find // evidence, queue up that evidence } else { // this is the next part of the body that we need to try to prove Statement t = c.rule.body[c.ind]; // Try to process this predicate with a user-provided custom // function that resolves things like mathematical relations. // euler.js provides similar functionality, but the system // of user predicates is completely different here. RdfRelation b = FindUserPredicate(t.Predicate); if (b != null) { Resource[] args; Variable[] unifyResult; Resource value = evaluate(t.Object, c.env); if (!c.rule.callArgs.ContainsKey(t.Subject)) { // The array of arguments to this relation is just the subject of the triple itself args = new Resource[] { evaluate(t.Subject, c.env) }; unifyResult = new Variable[1]; if (t.Subject is Variable) { unifyResult[0] = (Variable)t.Subject; } } else { // The array of arguments to this relation comes from a pre-grouped arg list. args = (Resource[])c.rule.callArgs[t.Subject]; unifyResult = new Variable[args.Length]; for (int i = 0; i < args.Length; i++) { if (args[i] is Variable) { unifyResult[i] = (Variable)args[i]; args[i] = evaluate(args[i], c.env); } } } // Run the user relation on the array of arguments (subject) and on the object. if (b.Evaluate(args, ref value)) { // If it succeeds, we press on. // The user predicate may update the 'value' variable and the argument // list array, and so we want to unify any variables in the subject // or object of this user predicate with the values given to us // by the user predicate. Hashtable newenv = (Hashtable)c.env.Clone(); if (t.Object is Variable) { unify(value, null, t.Object, newenv, true); } for (int i = 0; i < args.Length; i++) { if (unifyResult[i] != null) { unify(args[i], null, unifyResult[i], newenv, true); } } Statement grnd = evaluate(t, newenv); if (grnd != Statement.All) // sometimes not representable, like if literal as subject { g.Add(new Ground(new Sequent(grnd, new Statement[0], null), new Hashtable())); } QueueItem r = new QueueItem(); r.rule = c.rule; r.src = c.src; r.ind = c.ind; r.parent = c.parent; r.env = newenv; r.ground = g; r.ind++; queue.Add(r); // Note: Since we are putting something back in for c, we don't touch the life counter on the parent. } else { // If the predicate fails, decrement the life of the parent. decrementLife(c.parent, cached_subproofs); } continue; } // t can be proved either by the use of a rule // or if t literally exists in the world Statement t_resolved = evaluate(t, c.env); // If resolving this statement requires putting a literal in subject or predicate position, we // can't prove it. if (t_resolved == Statement.All) { decrementLife(c.parent, cached_subproofs); continue; } ArrayList tcases = new ArrayList(); // See if we have already tried to prove this. if (cached_subproofs.ContainsKey(t_resolved)) { StatementList cached_solutions = (StatementList)cached_subproofs[t_resolved]; if (cached_solutions == null) { if (Debug) { Console.Error.WriteLine("Euler: Dropping queue item because we have already failed to prove it: " + t_resolved); } } else { foreach (Statement s in cached_solutions) { if (Debug) { Console.Error.WriteLine("Euler: Using Cached Axiom: " + s); } Sequent seq = new Sequent(s); tcases.Add(seq); } } } else { // get all of the rules that apply to the predicate in question if (t_resolved.Predicate != null && cases.ContainsKey(t_resolved.Predicate)) { tcases.AddRange((IList)cases[t_resolved.Predicate]); } if (cases.ContainsKey("WILDCARD")) { tcases.AddRange((IList)cases["WILDCARD"]); } // if t has no unbound variables and we've matched something from // the axioms, don't bother looking at the world, and don't bother // proving it any other way than by the axiom. bool lookAtWorld = true; foreach (Sequent seq in tcases) { if (seq.body.Length == 0 && seq.head == t_resolved) { lookAtWorld = false; tcases.Clear(); tcases.Add(seq); break; } } // if there is a seprate world, get all of the world // statements that witness t if (world != null && lookAtWorld) { MemoryStore w = new MemoryStore(); if (Debug) { Console.Error.WriteLine("Running " + c); } if (Debug) { Console.Error.WriteLine("Euler: Ask World: " + t_resolved); } world.Select(t_resolved, w); foreach (Statement s in w) { if (Debug) { Console.Error.WriteLine("Euler: World Select Response: " + s); } Sequent seq = new Sequent(s); tcases.Add(seq); } } } // If there is no evidence or potential evidence (i.e. rules) // for t, then we will dump this QueueItem by not queuing any // subproofs. // Otherwise we try each piece of evidence in turn. foreach (Sequent rl in tcases) { ArrayList g2 = (ArrayList)c.ground.Clone(); if (rl.body.Length == 0) { g2.Add(new Ground(rl, new Hashtable())); } QueueItem r = new QueueItem(); r.rule = rl; r.src = rl; r.ind = 0; r.parent = c; r.env = new Hashtable(); r.ground = g2; if (unify(t, c.env, rl.head, r.env, true)) { QueueItem ep = c; // euler path while ((ep = ep.parent) != null) { if (ep.src == c.src && unify(ep.rule.head, ep.env, c.rule.head, c.env, false)) { break; } } if (ep == null) { // It is better for caching subproofs to work an entire proof out before // going on, which means we want to put the new queue item at the // top of the stack. queue.Add(r); c.liveChildren++; if (Debug) { Console.Error.WriteLine("Euler: Queue from Axiom: " + r); } } } } // If we did not add anything back into the queue for this item, then // we decrement the life of the parent. if (c.liveChildren == 0) { decrementLife(c.parent, cached_subproofs); } } } return(evidence); }
private static ArrayList prove(Hashtable cases, SelectableSource world, Statement[] goal, int maxNumberOfSteps) { // This is the main routine from euler.js. // cases: Resource (predicate) => IList of Sequent if (world != null) // cache our queries to the world, if a world is provided world = new SemWeb.Stores.CachedSource(world); StatementMap cached_subproofs = new StatementMap(); // Create the queue and add the first item. ArrayList queue = new ArrayList(); { QueueItem start = new QueueItem(); start.env = new Hashtable(); start.rule = new Sequent(Statement.All, goal, null); start.src = null; start.ind = 0; start.parent = null; start.ground = new ArrayList(); queue.Add(start); if (Debug) Console.Error.WriteLine("Euler: Queue: " + start); } // The evidence array holds the results of this proof. ArrayList evidence = new ArrayList(); // Track how many steps we take to not go over the limit. int step = 0; // Process the queue until it's empty or we reach our step limit. while (queue.Count > 0) { // deal with the QueueItem at the top of the queue QueueItem c = (QueueItem)queue[queue.Count-1]; queue.RemoveAt(queue.Count-1); ArrayList g = new ArrayList(c.ground); // have we done too much? step++; if (maxNumberOfSteps != -1 && step >= maxNumberOfSteps) { if (Debug) Console.Error.WriteLine("Euler: Maximum number of steps exceeded. Giving up."); return null; } // if each statement in the body of the sequent has been proved if (c.ind == c.rule.body.Length) { // if this is the top-level sequent being proved; we've found a complete evidence for the goal if (c.parent == null) { EvidenceItem ev = new EvidenceItem(); ev.head = new Statement[c.rule.body.Length]; bool canRepresentHead = true; for (int i = 0; i < c.rule.body.Length; i++) { ev.head[i] = evaluate(c.rule.body[i], c.env); if (ev.head[i].AnyNull) // can't represent it: literal in subject position, for instance canRepresentHead = false; } ev.body = c.ground; ev.env = c.env; if (Debug) Console.Error.WriteLine("Euler: Found Evidence: " + ev); if (!canRepresentHead) continue; evidence.Add(ev); // this is a subproof of something; whatever it is a subgroup for can // be incremented } else { // if the rule had no body, it was just an axiom and we represent that with Ground if (c.rule.body.Length != 0) g.Add(new Ground(c.rule, c.env)); // advance the parent being proved and put the advanced // parent into the queue; unify the parent variable assignments // with this one's QueueItem r = new QueueItem(); r.rule = c.parent.rule; r.src = c.parent.src; r.ind = c.parent.ind; r.parent = c.parent.parent; r.env = (Hashtable)c.parent.env.Clone(); r.ground = g; unify(c.rule.head, c.env, r.rule.body[r.ind], r.env, true); r.ind++; queue.Add(r); if (Debug) Console.Error.WriteLine("Euler: Queue Advancement: " + r); // The number of live children for this parent is decremented since we are // done with this subproof, but we store the result for later. if (c.parent.solutions == null) c.parent.solutions = new StatementList(); c.parent.solutions.Add(evaluate(r.rule.body[r.ind-1], r.env)); decrementLife(c.parent, cached_subproofs); } // this sequent still has parts of the body left to be proved; try to // find evidence for the next statement in the body, and if we find // evidence, queue up that evidence } else { // this is the next part of the body that we need to try to prove Statement t = c.rule.body[c.ind]; // Try to process this predicate with a user-provided custom // function that resolves things like mathematical relations. // euler.js provides similar functionality, but the system // of user predicates is completely different here. RdfRelation b = FindUserPredicate(t.Predicate); if (b != null) { Resource[] args; Variable[] unifyResult; Resource value = evaluate(t.Object, c.env); if (!c.rule.callArgs.ContainsKey(t.Subject)) { // The array of arguments to this relation is just the subject of the triple itself args = new Resource[] { evaluate(t.Subject, c.env) }; unifyResult = new Variable[1]; if (t.Subject is Variable) unifyResult[0] = (Variable)t.Subject; } else { // The array of arguments to this relation comes from a pre-grouped arg list. args = (Resource[])((ICloneable)c.rule.callArgs[t.Subject]).Clone(); unifyResult = new Variable[args.Length]; for (int i = 0; i < args.Length; i++) { if (args[i] is Variable) { unifyResult[i] = (Variable)args[i]; args[i] = evaluate(args[i], c.env); } } } // Run the user relation on the array of arguments (subject) and on the object. if (b.Evaluate(args, ref value)) { // If it succeeds, we press on. // The user predicate may update the 'value' variable and the argument // list array, and so we want to unify any variables in the subject // or object of this user predicate with the values given to us // by the user predicate. Hashtable newenv = (Hashtable)c.env.Clone(); if (t.Object is Variable) unify(value, null, t.Object, newenv, true); for (int i = 0; i < args.Length; i++) if (unifyResult[i] != null) unify(args[i], null, unifyResult[i], newenv, true); Statement grnd = evaluate(t, newenv); if (grnd != Statement.All) // sometimes not representable, like if literal as subject g.Add(new Ground(new Sequent(grnd, new Statement[0], null), new Hashtable())); QueueItem r = new QueueItem(); r.rule = c.rule; r.src = c.src; r.ind = c.ind; r.parent = c.parent; r.env = newenv; r.ground = g; r.ind++; queue.Add(r); // Note: Since we are putting something back in for c, we don't touch the life counter on the parent. } else { // If the predicate fails, decrement the life of the parent. if (c.parent != null) decrementLife(c.parent, cached_subproofs); } continue; } // t can be proved either by the use of a rule // or if t literally exists in the world Statement t_resolved = evaluate(t, c.env); // If resolving this statement requires putting a literal in subject or predicate position, we // can't prove it. if (t_resolved == Statement.All) { if (c.parent != null) decrementLife(c.parent, cached_subproofs); continue; } ArrayList tcases = new ArrayList(); // See if we have already tried to prove this. if (cached_subproofs.ContainsKey(t_resolved)) { StatementList cached_solutions = (StatementList)cached_subproofs[t_resolved]; if (cached_solutions == null) { if (Debug) Console.Error.WriteLine("Euler: Dropping queue item because we have already failed to prove it: " + t_resolved); } else { foreach (Statement s in cached_solutions) { if (Debug) Console.Error.WriteLine("Euler: Using Cached Axiom: " + s); Sequent seq = new Sequent(s); tcases.Add(seq); } } } else { // get all of the rules that apply to the predicate in question if (t_resolved.Predicate != null && cases.ContainsKey(t_resolved.Predicate)) tcases.AddRange((IList)cases[t_resolved.Predicate]); if (cases.ContainsKey("WILDCARD")) tcases.AddRange((IList)cases["WILDCARD"]); // if t has no unbound variables and we've matched something from // the axioms, don't bother looking at the world, and don't bother // proving it any other way than by the axiom. bool lookAtWorld = true; foreach (Sequent seq in tcases) { if (seq.body.Length == 0 && seq.head == t_resolved) { lookAtWorld = false; tcases.Clear(); tcases.Add(seq); break; } } // if there is a seprate world, get all of the world // statements that witness t if (world != null && lookAtWorld) { MemoryStore w = new MemoryStore(); if (Debug) Console.Error.WriteLine("Running " + c); if (Debug) Console.Error.WriteLine("Euler: Ask World: " + t_resolved); world.Select(t_resolved, w); foreach (Statement s in w) { if (Debug) Console.Error.WriteLine("Euler: World Select Response: " + s); Sequent seq = new Sequent(s); tcases.Add(seq); } } } // If there is no evidence or potential evidence (i.e. rules) // for t, then we will dump this QueueItem by not queuing any // subproofs. // Otherwise we try each piece of evidence in turn. foreach (Sequent rl in tcases) { ArrayList g2 = (ArrayList)c.ground.Clone(); if (rl.body.Length == 0) g2.Add(new Ground(rl, new Hashtable())); QueueItem r = new QueueItem(); r.rule = rl; r.src = rl; r.ind = 0; r.parent = c; r.env = new Hashtable(); r.ground = g2; if (unify(t, c.env, rl.head, r.env, true)) { QueueItem ep = c; // euler path while ((ep = ep.parent) != null) if (ep.src == c.src && unify(ep.rule.head, ep.env, c.rule.head, c.env, false)) break; if (ep == null) { // It is better for caching subproofs to work an entire proof out before // going on, which means we want to put the new queue item at the // top of the stack. queue.Add(r); c.liveChildren++; if (Debug) Console.Error.WriteLine("Euler: Queue from Axiom: " + r); } } } // If we did not add anything back into the queue for this item, then // we decrement the life of the parent. if (c.liveChildren == 0 && c.parent != null) decrementLife(c.parent, cached_subproofs); } } return evidence; }