public Sequent Apply(Sequent sequent) { UnaryFormula implicationFormula = (UnaryFormula)sequent.RightHandSide.Formulas.Where( x => x is UnaryFormula formula && formula.Connective == UnaryConnective.Necessity ).FirstOrDefault(); if (implicationFormula == null) { return(null); } var formula = implicationFormula.Formula.Clone(); if (formula.WorldIndex.IsGround && formula.FreeVariables.Count == 0) { formula.WorldIndex.AddSymbol(LogicSolver.WorldService.GetNewWorldConstant()); } else { var skolemVariables = new List <WorldSymbol>(formula.WorldIndex.Symbols); skolemVariables.AddRange(formula.FreeVariables.Select(x => x.ToWorldSymbol())); formula.WorldIndex.AddSymbol(LogicSolver.WorldService.GetNewWorldFunction(skolemVariables)); } sequent.RightHandSide.Formulas.Remove(implicationFormula); sequent.RightHandSide.Formulas.Add(formula); sequent.Justification = "R7 (" + sequent.Name + ")"; return(sequent); }
public Sequent Apply(Sequent sequent) { QuantifierFormula implicationFormula = (QuantifierFormula)sequent.RightHandSide.Formulas.Where ( x => x is QuantifierFormula formula && formula.Quantifier == QuantifierConnective.ForAll ).FirstOrDefault(); if (implicationFormula == null) { return(null); } var formula = implicationFormula.Formula.Clone(); Terminal a; if (implicationFormula.FreeVariables.Count == 0 && implicationFormula.WorldIndex.IsGround) { a = termNamer.GetNewConstant(); } else { var skolemVariables = new List <Terminal>(formula.WorldIndex.Symbols.Select(x => x.ToTerminal())); skolemVariables.AddRange(implicationFormula.FreeVariables); a = termNamer.GetNewFunction(skolemVariables); } formula.ApplySubstitution(new Substitution(implicationFormula.Variable, a)); sequent.RightHandSide.Formulas.Remove(implicationFormula); sequent.RightHandSide.Formulas.Add(formula); sequent.Justification = "R9 (" + sequent.Name + ")"; return(sequent); }
public List <Sequent> Apply(Sequent leftS, Sequent rightS, bool recurse = true) { leftS = leftS.Clone(); rightS = rightS.Clone(); var ps = leftS.LeftHandSide.Formulas; var qs = rightS.RightHandSide.Formulas; //if ((leftS.LeftHandSide.Count == 0 && leftS.RightHandSide.Count != 0) && // (rightS.RightHandSide.Count == 0 && rightS.LeftHandSide.Count != 0)) //{ // ps = rightS.LeftHandSide.Formulas; // qs = leftS.RightHandSide.Formulas; //} foreach (var formulaP in ps) { relation.AddWorldIndex(formulaP.WorldIndex); foreach (var formulaQ in qs) { relation.AddWorldIndex(formulaQ.WorldIndex); var unification = formulaP.MUnify(relation, formulaQ); if (unification == null) { continue; } leftS.LeftHandSide.Formulas.ForEach(x => x.ApplySubstitution(unification)); rightS.LeftHandSide.Formulas.ForEach(x => x.ApplySubstitution(unification)); leftS.RightHandSide.Formulas.ForEach(x => x.ApplySubstitution(unification)); rightS.RightHandSide.Formulas.ForEach(x => x.ApplySubstitution(unification)); var result = new Sequent(); result.LeftHandSide.Formulas.AddRange(leftS.LeftHandSide.Formulas); result.LeftHandSide.Formulas.AddRange(rightS.LeftHandSide.Formulas); result.RightHandSide.Formulas.AddRange(leftS.RightHandSide.Formulas); result.RightHandSide.Formulas.AddRange(rightS.RightHandSide.Formulas); result.LeftHandSide.Formulas.Remove(formulaP); result.RightHandSide.Formulas.Remove(formulaQ); result.Name = "R1 (" + leftS.Name + ", " + rightS.Name + ") with " + unification.ToString(); return(new List <Sequent>() { result }); } } if (recurse) { return(Apply(rightS, leftS, false)); } return(null); }
private static void AddSequent(Hashtable cases, Sequent s) { object key = s.head.Predicate; if (key is Variable) { key = "WILDCARD"; } ArrayList list = (ArrayList)cases[key]; if (list == null) { list = new ArrayList(); cases[key] = list; } list.Add(s); }
public Sequent Apply(Sequent sequent) { UnaryFormula implicationFormula = (UnaryFormula)sequent.LeftHandSide.Formulas.Where( x => x is UnaryFormula formula && formula.Connective == UnaryConnective.Negation ).FirstOrDefault(); if (implicationFormula == null) { return(null); } sequent.LeftHandSide.Formulas.Remove(implicationFormula); var formula = implicationFormula.Formula.Clone(); sequent.RightHandSide.Formulas.Add(formula); sequent.Justification = "R5 (" + sequent.Name + ")"; return(sequent); }
public Sequent Apply(Sequent sequent) { QuantifierFormula implicationFormula = (QuantifierFormula)sequent.LeftHandSide.Formulas.Where ( x => x is QuantifierFormula formula && formula.Quantifier == QuantifierConnective.ForAll ).FirstOrDefault(); if (implicationFormula == null) { return(null); } var formula = implicationFormula.Formula.Clone(); formula.ApplySubstitution(new Substitution(implicationFormula.Variable, LogicSolver.TermNamer.GetNewVariable())); sequent.LeftHandSide.Formulas.Remove(implicationFormula); sequent.LeftHandSide.Formulas.Add(formula); sequent.Justification = "R10 (" + sequent.Name + ")"; return(sequent); }
public Sequent Apply(Sequent sequent) { UnaryFormula implicationFormula = (UnaryFormula)sequent.LeftHandSide.Formulas.Where ( x => x is UnaryFormula formula && formula.Connective == UnaryConnective.Necessity ).FirstOrDefault(); if (implicationFormula == null) { return(null); } sequent.LeftHandSide.Formulas.Remove(implicationFormula); var formula = implicationFormula.Formula.Clone(); formula.WorldIndex.AddSymbol(LogicSolver.WorldService.GetNewWorldVariable()); sequent.LeftHandSide.Formulas.Add(formula); sequent.Justification = "R8 (" + sequent.Name + ")"; return(sequent); }
public bool Solve(Formula formula) { formula.Simplify(); var initialSequent = new Sequent(formula); var queue = new Queue <Sequent>(); var sequentCounter = 0; initialSequent.Name = sequentCounter.ToString(); sequentCounter++; Console.WriteLine(); Console.WriteLine(initialSequent.Name + " :|: " + initialSequent); Console.WriteLine(); queue.Enqueue(initialSequent); var sequentList = new List <Sequent>(); while (queue.Count != 0) { var sequent = queue.Dequeue(); Parallel.ForEach(rules, x => { var result = x.Apply(sequent.Clone()); if (result != null) { result.Name = sequentCounter.ToString(); sequentCounter++; queue.Enqueue(result); Log.Add(result.ToString()); Console.WriteLine(result.Name + " :|: " + result); if (result.IsReduced) { sequentList.Add(result); } } }); } try { var res = resolutionRule.Apply(sequentList[0], sequentList[1]); Console.WriteLine(); if (res == null) { return(false); } else if (res[0].IsEmpty) { Console.WriteLine("Found Proof: " + res[0].Name); return(true); } return(false); } catch { return(false); } }
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); }
public Hashtable env; // substitution environment: Resource => Resource public Ground(Sequent src, Hashtable env) { this.src = src; this.env = env; }
public List <Sequent> Apply(Sequent leftS, Sequent rightS) { return(Apply(leftS, rightS, true)); }
public Sequent src; // evidence #endregion Fields #region Constructors public Ground(Sequent src, Hashtable env) { this.src = src; this.env = env; }
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; }
private static void AddSequent(Hashtable cases, Sequent s) { object key = s.head.Predicate; if (key is Variable) key = "WILDCARD"; ArrayList list = (ArrayList)cases[key]; if (list == null) { list = new ArrayList(); cases[key] = list; } list.Add(s); }