/// <summary> /// Populates the Variable predicates of a target Atom, using another Atom as a template /// and a Fact as the source of data, i.e. Individual predicates. /// </summary> /// <param name="data">The data for populating the Atom.</param> /// <param name="template">The template of Atom being populated.</param> /// <param name="target">The members to populate.</param> public static void Populate(Fact data, Atom template, IPredicate[] members) { for(int i=0;i<members.Length;i++) if (members[i] is Variable) { int j = Array.IndexOf(template.Members, members[i]); if (j >= 0) members[i] = data.Members[j]; } }
public void Add(Fact fact, Atom matchingAtom) { if (!factTable.ContainsKey(fact.GetLongHashCode())) factTable.Add(fact.GetLongHashCode(), fact); Fact resolved = Fact.Resolve(false, fact, matchingAtom); for (int i=0; i<resolved.Members.Length; i++) { object predicateValue = resolved.GetPredicateValue(i); AddFactForPredicateValue(fact, i, predicateValue); // if we are not strictly enforcing typing and if the predicate value is not a string, then also store it as a string if ((!StrictTyping) && (!(predicateValue is string))) AddFactForPredicateValue(fact, i, predicateValue.ToString()); } }
public void Remove(Fact fact) { factTable.Remove(fact.GetLongHashCode()); // if facttable is empty, then we re-initialize the predicate storage if (factTable.Count == 0) InitializePredicateStorage(); }
/// <summary> /// Instantiates a new Fact event definition. /// </summary> /// <remarks> /// DO NOT ASSERT OR RETRACT FACTS WHEN HANDLING THIS EVENT! /// </remarks> /// <param name="fact">The Fact that generated the event.</param> public NewFactEventArgs(Fact fact):this(fact, null) {}
/// <summary> /// Retracts (removes) a Fact from the current working memory. /// </summary> /// <param name="fact">The Fact to retract.</param> /// <returns>True if the Fact has been retracted from the FactBase, otherwise False.</returns> public bool Retract(Fact fact) { CheckInitialized(); return WM.FB.Retract(fact); }
/// <summary> /// Modify a Fact by Retracting it and Asserting the replacement one. /// If the new Fact has no label (null or Empty), then the Label of the existing fact is kept. /// </summary> /// <param name="currentFact">The Fact to modify.</param> /// <param name="newFact">The Fact to modify to.</param> /// <returns>True if <term>currentFact</term> has been retracted from the FactBase, otherwise False ; this whether <term>newFact</term> already exists in the factbase, or not.</returns> public bool Modify(Fact currentFact, Fact newFact) { CheckInitialized(); return WM.FB.Modify(currentFact, newFact); }
/// <summary> /// Asserts (adds) a Fact in the current working memory. /// </summary> /// <param name="fact">The Fact to assert.</param> /// <returns>True if the Fact was added to the Fact Base, i.e. if it was really new!</returns> public bool Assert(Fact fact) { CheckInitialized(); return WM.FB.Assert(fact); }
public void Discount() { ie.LoadRuleBase(new RuleML08DatalogAdapter(ruleFilesFolder + "discount.ruleml", FileAccess.Read)); Process(); Assert.AreEqual(3, deducted, "(1) Deducted"); Assert.AreEqual(6, ie.FactsCount, "(1) Total Facts Count"); deductionsToCheck = new string[] {"discount{Peter Miller,Honda,5.0 percent}", "discount{Peter Miller,Porsche,7.5 percent}"}; qrs = ie.RunQuery(new Query(new AtomGroup(AtomGroup.LogicalOperator.And, new Atom("discount", new Variable("customer"), new Variable("product"), new Variable("amount"))))); Assert.AreEqual(2, qrs.Count, "(1) Query Result Size"); ParseResult(); Assert.IsFalse(wrongDeduction, "(1) Query Results"); // Fact 4 - this is a bummer Fact fact4 = new Fact("bummer", "spending", new Individual("John Q. Doe"), new Individual("min 5000 euro"), new Individual("previous year"), new Individual("current year")); ie.Assert(fact4); // Fact 5 ie.Assert(new Fact("spending", new Individual("Jean Dupont"), new Individual("min 5000 euro"), new Individual("previous year"))); Process(); Assert.AreEqual(3, deducted, "(2) Deducted"); Assert.AreEqual(11, ie.FactsCount, "(2) Total Facts Count"); // Run anonymous (not named) query deductionsToCheck = new string[] {"discount{Peter Miller,Honda,5.0 percent}", "discount{Peter Miller,Porsche,7.5 percent}", "discount{Jean Dupont,Honda,5.0 percent}", "discount{Jean Dupont,Porsche,7.5 percent}"}; qrs = ie.RunQuery(0); Assert.AreEqual(4, qrs.Count, "(2) Query Result Size"); ParseResult(); Assert.IsFalse(wrongDeduction, "(2) Query Results"); }
public void RetractingImplication() { ie.LoadRuleBase(NewTestAdapter()); Fact factToRetract = new Fact("toRetract", new Individual("easy target")); Assert.IsFalse(ie.FactExists(factToRetract), "1st Pre-check before target assertion"); ie.Assert(factToRetract); Assert.IsTrue(ie.FactExists(factToRetract), "2nd Pre-check before target assertion"); // this process should leave the target unchanged Process(); Assert.IsTrue(ie.FactExists(factToRetract), "1st process: fact unchanged"); Assert.AreEqual(0, deleted, "1st process: deleted"); Assert.AreEqual(0, deducted, "1st process: deleted"); // after asserting the trigger, process should retract the target Fact factRetractionTrigger = new Fact("retractionTrigger", new Individual("easy target")); Fact factRetractionConfirmation = new Fact("retractionConfirmation", new Individual("easy target")); ie.Assert(factRetractionTrigger); Process(); Assert.IsFalse(ie.FactExists(factToRetract), "2nd process: fact retracted"); Assert.IsTrue(ie.FactExists(factRetractionConfirmation), "2nd process: retracting implication positive pre-conditionned"); Assert.AreEqual(1, deleted, "2nd process: deleted"); Assert.AreEqual(1, deducted, "2nd process: deducted"); }
/// <summary> /// Asserts (adds) a Fact in the current working memory. /// </summary> /// <param name="fact">The Fact to assert.</param> /// <returns>True if the Fact was added to the Fact Base, i.e. if it was really new!</returns> public bool Assert(Fact fact) { return IE.Assert(fact); }
/// <summary> /// Retracts (removes) a Fact from the current working memory. /// </summary> /// <param name="fact">The Fact to retract.</param> public void Retract(Fact fact) { IE.Retract(fact); }
/// <summary> /// Creates a new Fact. /// </summary> /// <remarks> /// This method is an helper of the regular Fact instantiation. /// </remarks> /// <param name="type">The Type of the new Fact to assert.</param> /// <param name="individuals">The Array of Individual predicates or the Individual predicate that the Fact will contain.</param> /// <returns>A new Fact of desired Type and individuals.</returns> public Fact NewFact(string type, object individuals) { Fact newFact; if (individuals.GetType().IsArray) { Array individualsArray = (Array) individuals; Individual[] members = new Individual[individualsArray.Length]; for (int i=0; i<individualsArray.Length; i++) members[i] = new Individual(individualsArray.GetValue(i)); newFact = new Fact(type, members); } else newFact = new Fact(type, new Individual(individuals)); return newFact; }
/// <summary> /// Modify a Fact by Retracting it and Asserting the replacement one. /// If the new Fact has no label (null or Empty), then the Label of the existing fact is kept. /// </summary> /// <param name="currentFactLabel">The label of the Fact to modify.</param> /// <param name="newFact">The Fact to modify to.</param> /// <returns>True if <term>currentFact</term> has been retracted from the FactBase, otherwise False ; this whether <term>newFact</term> already exists in the factbase, or not.</returns> public bool Modify(string currentFactLabel, Fact newFact) { return IE.Modify(currentFactLabel, newFact); }
/// <summary> /// Modify a Fact by Retracting it and Asserting the replacement one. /// If the new Fact has no label (null or Empty), then the Label of the existing fact is kept. /// </summary> /// <param name="currentFact">The Fact to modify.</param> /// <param name="newFact">The Fact to modify to.</param> /// <returns>True if <term>currentFact</term> has been retracted from the FactBase, otherwise False ; this whether <term>newFact</term> already exists in the factbase, or not.</returns> public bool Modify(Fact currentFact, Fact newFact) { return IE.Modify(currentFact, newFact); }
/// <summary> /// Returns true if a Fact exists in the current working memory. /// </summary> /// <param name="fact">The Fact to check existence.</param> /// <returns>True if the Fact exists.</returns> public bool FactExists(Fact fact) { return IE.FactExists(fact); }
/// <summary> /// Prepare the atom to be pattern matched by replacing in a fact: /// - all the predicates that match function predicates in the passed atom with /// the string representation of these function predicates, /// - in fully mode, all the predicates that match individual predicates in the passed atom with /// the string representation of these individual predicates. /// </summary> /// <remarks> /// This operation must be done *if and only if* the fact matches the atom. /// </remarks> /// <param name="fully">Forces resolution of non-string individual to String.</param> /// <param name="factToResolve">The fact that must be resolved.</param> /// <param name="atom">The atom with which the current fact matches.</param> /// <returns>A new fact with only String individuals.</returns> public static Fact Resolve(bool fully, Fact factToResolve, Atom atom) { IPredicate[] predicates = new IPredicate[factToResolve.Members.Length]; for(int i=0; i<factToResolve.Members.Length; i++) { if ((atom.Members[i] is Function) || ((fully) && (atom.Members[i] is Individual) && (!(factToResolve.Members[i].Value is System.String)))) { predicates[i] = new Individual(atom.Members[i].ToString()); } else { predicates[i] = factToResolve.Members[i]; } } return (Fact)factToResolve.CloneWithNewMembers(predicates); }
private Fact(Fact source, IPredicate[] members) : base(source, members) { this.label = source.label; }
public virtual void SimilarNafMatching() { // regression test for bug 1332214 ie.LoadRuleBase(NewTestAdapter()); Assert.IsTrue(ie.Assert(new Fact("naf-probe", new Individual(123), new Individual("bar"))), "Asserted naf-probe"); Assert.IsTrue(ie.Assert(new Fact("naf-switch", new Individual(123), new Individual("pivot"), new Individual("baz"))), "Asserted first naf-switch"); Fact switchToKill = new Fact("naf-switch", new Individual(123), new Individual("pivot"), new Individual("foo")); Assert.IsTrue(ie.Assert(switchToKill), "Asserted second naf-switch"); Process(); Assert.AreEqual(0, deducted, "Deducted"); Assert.IsTrue(ie.Retract(switchToKill)); Process(); Assert.AreEqual(1, deducted, "Deducted"); }
private void CommonDiscountVisio2003(IRuleBaseAdapter rba) { ie.LoadRuleBase(rba); Process(); Assert.AreEqual(3, deducted, "(1) Deducted"); Assert.AreEqual(6, ie.FactsCount, "(2) Total Facts Count"); deductionsToCheck = new string[] {"Discount{Peter Miller,Honda,5.0}", "Discount{Peter Miller,Porsche,7.5}"}; qrs = ie.RunQuery(new Query(new AtomGroup(AtomGroup.LogicalOperator.And, new Atom("Discount", new Variable("customer"), new Variable("product"), new Variable("amount"))))); Assert.AreEqual(2, qrs.Count, "(1) Query Result Size"); ParseResult(); Assert.IsFalse(wrongDeduction, "(1) Query Results"); // Spending JQDoe Assert.IsTrue(ie.Assert(new Fact("Spending", new Individual("John Q. Doe"), new Individual(2004), new Individual(123.45f))), "jqdoeSpending asserted"); Fact jqdoePremiumRating = new Fact("Customer Rating", new Individual("John Q. Doe"), new Individual("Premium")); Assert.IsTrue(ie.Assert(jqdoePremiumRating), "jqdoePremiumRating asserted"); // SpendingJDupont Assert.IsTrue(ie.Assert(new Fact("Spending", new Individual("Jean Dupont"), new Individual(2004), new Individual(3245.25f))), "jdupontSpending asserted"); Process(); Assert.IsFalse(ie.FactExists(jqdoePremiumRating), "jqdoePremiumRating was retracted"); Assert.AreEqual(4, deducted, "(2) Deducted"); Assert.AreEqual(12, ie.FactsCount, "(2) Total Facts Count"); // Run named queries deductionsToCheck = new string[] {"Discount{Peter Miller,Honda,5.0}", "Discount{Peter Miller,Porsche,7.5}", "Discount{Jean Dupont,Honda,5.0}", "Discount{Jean Dupont,Porsche,7.5}"}; qrs = ie.RunQuery("Calculated Discounts"); Assert.AreEqual(4, qrs.Count, "(2) Query Result Size"); ParseResult(); Assert.IsFalse(wrongDeduction, "(2) Query Results"); deductionsToCheck = new string[] {"Customer Rating{Peter Miller,Premium}", "Customer Rating{John Q. Doe,Regular}", "Customer Rating{Jean Dupont,Premium}"}; qrs = ie.RunQuery("Customer Ratings"); Assert.AreEqual(3, qrs.Count, "(3) Query Result Size"); ParseResult(); Assert.IsFalse(wrongDeduction, "(3) Query Results"); }
public void FactExistence() { ie.LoadRuleBase(NewTestAdapter()); Assert.IsTrue(ie.FactExists("Porsche Luxury"), "Existing fact label"); Assert.IsFalse(ie.FactExists("Porsche Regular"), "Non-existing fact label"); Fact factHondaRegular = new Fact("regular", new Individual("Honda")); Fact factHondaLuxury = new Fact("luxury", new Individual("Honda")); Assert.IsTrue(ie.FactExists(factHondaRegular), "Existing fact"); Assert.IsFalse(ie.FactExists(factHondaLuxury), "Non-existing fact"); }
/// <summary> /// Returns true if a Fact exists in the current working memory. /// </summary> /// <param name="fact">The Fact to check existence.</param> /// <returns>True if the Fact exists.</returns> public bool FactExists(Fact fact) { CheckInitialized(); return WM.FB.Exists(fact); }
public void ImplicationModifySupport() { ie.LoadRuleBase(NewTestAdapter()); Assert.IsTrue(ie.Assert(new Fact("modifyTrigger", new Individual("foo"), new Individual("bar"))), "Asserted trigger fact"); ie.NewWorkingMemory(WorkingMemoryTypes.Isolated); Process(); Assert.AreEqual(0, deducted, "(1) Deducted"); deductionsToCheck = new string[] {"modifyTarget{bar,after}"}; // test with non labeled fact ie.NewWorkingMemory(WorkingMemoryTypes.Isolated); Fact toModify = new Fact("modifyTarget", new Individual("bar"), new Individual("before")); Assert.IsFalse(ie.FactExists(toModify), "(2) Target fact not present"); Assert.IsTrue(ie.Assert(toModify), "(2) Asserted target fact"); Assert.IsTrue(ie.FactExists(toModify), "(2) Target fact present"); int initialFactsCount = ie.FactsCount; Process(); Assert.AreEqual(initialFactsCount, ie.FactsCount, "(2) Stable FactsCount"); Assert.AreEqual(0, deducted, "(2) Deducted"); Assert.AreEqual(0, deleted, "(2) Deleted"); Assert.AreEqual(1, modified, "(2) Modified"); Assert.IsFalse(ie.FactExists(toModify), "(2) Target fact retracted"); // test with labeled fact ie.NewWorkingMemory(WorkingMemoryTypes.Isolated); string label = "label of modifyTarget"; toModify = new Fact(label, "modifyTarget", new Individual("bar"), new Individual("before")); Assert.IsFalse(ie.FactExists(toModify), "(3) Target fact not present"); Assert.IsFalse(ie.FactExists(label), "(3) Target fact label not present"); Assert.IsTrue(ie.Assert(toModify), "(3) Asserted target fact"); Assert.IsTrue(ie.FactExists(toModify), "(3) Target fact present"); Assert.IsTrue(ie.FactExists(label), "(3) Target fact label present"); initialFactsCount = ie.FactsCount; Process(); Assert.AreEqual(initialFactsCount, ie.FactsCount, "(3) Stable FactsCount"); Assert.AreEqual(0, deducted, "(3) Deducted"); Assert.AreEqual(0, deleted, "(3) Deleted"); Assert.AreEqual(1, modified, "(3) Modified"); Assert.IsFalse(ie.FactExists(toModify), "(3) Target fact retracted"); Assert.IsTrue(ie.FactExists(label), "(3) Target fact label present"); }
/// <summary> /// Modify a Fact by Retracting it and Asserting the replacement one. /// If the new Fact has no label (null or Empty), then the Label of the existing fact is kept. /// </summary> /// <param name="currentFactLabel">The label of the Fact to modify.</param> /// <param name="newFact">The Fact to modify to.</param> /// <returns>True if <term>currentFact</term> has been retracted from the FactBase, otherwise False ; this whether <term>newFact</term> already exists in the factbase, or not.</returns> public bool Modify(string currentFactLabel, Fact newFact) { Fact currentFact = GetFact(currentFactLabel); if (currentFact != null) return WM.FB.Modify(currentFact, newFact); else return false; }
public void MutexSupport() { ie.LoadRuleBase(NewTestAdapter()); Fact locker = new Fact("locker", "flag", new Individual("lock"), new Individual("mutexLock")); ie.Assert(locker); ie.Assert(new Fact("flag", new Individual("probe"), new Individual("triggeredA"))); ie.Assert(new Fact("flag", new Individual("probe"), new Individual("triggeredB"))); Process(); Assert.AreEqual(1, deducted, "(1) Deducted"); // no garantee if A or B is deducted because they implications have same priority level ie.Retract(locker); Process(); Assert.AreEqual(1, deducted, "(2) Deducted"); // C should be deducted ie.Assert(new Fact("flag", new Individual("probe"), new Individual("triggeredC"))); deductionsToCheck = new string[] {"mutexC{probe}"}; NewFactEvent honf = new NewFactEvent(HandleOrderedNewFact); ie.NewFactHandler += honf; Process(); Assert.AreEqual(1, deducted, "(3) Deducted"); Assert.IsFalse(wrongDeduction, "Deductions OK"); ie.NewFactHandler -= honf; deductionsToCheck = null; // nothing must be rededucted as Process(); Assert.AreEqual(0, deducted, "(3) Deducted"); }
private int RunImplication(Implication implication) { int implicationResultsCount = 0; FactBase.ProcessResultSet processResults = WM.FB.ProcessAtomGroup(implication.AtomGroup); if (implication.Action == ImplicationAction.Count) { if (HasLogListener) ForceDispatchLog("Counting Implication '" + implication.Label + "' counted: " + processResults.Count, LogEventImpl.DEBUG); bool variableFound = false; IPredicate[] members = (IPredicate[])implication.Deduction.Members.Clone(); for(int i=0; !variableFound && i<members.Length; i++) { if (members[i] is Variable) { members[i] = new Individual(processResults.Count); variableFound = true; break; } } if ((IEImpl.StrictImplication) && (!variableFound)) throw new BREException("Strict counting implication rejected the assertion due to lack of variable predicate: " + implication.Deduction); Fact deductedFact = new Fact(implication.Deduction.Type, members); implicationResultsCount++; // counting implication factbase action bool result = WM.FB.Assert(deductedFact); if ((result) && (NewFactHandler != null)) NewFactHandler(new NewFactEventArgs(deductedFact)); if (HasLogListener) ForceDispatchLog((result?"Asserted":"Ignored Assertion of ") + " Fact: " + deductedFact.ToString(), LogEventImpl.DEBUG); } else if ((implication.Action == ImplicationAction.Assert) || (implication.Action == ImplicationAction.Retract)) { // loop on each result and try to build a new fact out of the predicates coming for each result foreach(ArrayList processResult in processResults) { Fact deductedFact = BuildFact(implication.Deduction, processResult); if (deductedFact != null) { implicationResultsCount++; if (implication.Action == ImplicationAction.Retract) { // retracting implication factbase action bool result = WM.FB.Retract(deductedFact); if ((result) && (DeleteFactHandler != null)) DeleteFactHandler(new NewFactEventArgs(deductedFact)); if (HasLogListener) ForceDispatchLog((result?"Retracted":"Ignored Retraction of ") + " Fact: " + deductedFact.ToString(), LogEventImpl.DEBUG); } else { // asserting implication factbase action bool result = WM.FB.Assert(deductedFact); if ((result) && (NewFactHandler != null)) NewFactHandler(new NewFactEventArgs(deductedFact)); if (HasLogListener) ForceDispatchLog((result?"Asserted":"Ignored Assertion of ") + " Fact: " + deductedFact.ToString(), LogEventImpl.DEBUG); } } } } else if (implication.Action == ImplicationAction.Modify) { foreach(ArrayList processResult in processResults) { // look for facts to modify by: // - resolving variable predicates of the deduction // - replacing formulas with variables // and performing a search in the fact base Atom modificationTargetLookup = FactBase.BuildQueryFromDeduction(implication.Deduction, processResult); if (HasLogListener) ForceDispatchLog("Modifying Implication '" + implication.Label + "' will target matches of: " + modificationTargetLookup, LogEventImpl.DEBUG); foreach(Fact factToModify in FactBase.ExtractFacts(WM.FB.ProcessAtomGroup(new AtomGroup(AtomGroup.LogicalOperator.And, modificationTargetLookup)))) { if (HasLogListener) ForceDispatchLog("-> found target: " + factToModify, LogEventImpl.DEBUG); // for each fact, perform the modification Fact deductedFact = BuildFact(implication.Deduction, FactBase.EnrichResults(processResult, modificationTargetLookup, factToModify)); if (HasLogListener) ForceDispatchLog("-> modified target: " + deductedFact, LogEventImpl.DEBUG); if ((deductedFact != null) && (!factToModify.Equals(deductedFact))) { implicationResultsCount++; bool result = WM.FB.Modify(factToModify, deductedFact); if ((result) && (ModifyFactHandler != null))ModifyFactHandler(new NewFactEventArgs(factToModify, deductedFact)); if (HasLogListener) ForceDispatchLog((result?"Modified":"Ignored Modification of ") + " Fact: " + factToModify.ToString(), LogEventImpl.DEBUG); } } } } else throw new BREException("Implication action not supported: " + implication.Action); return implicationResultsCount; }
protected override void WriteFact(XmlElement target, Fact fact) { if (syntax == SaveFormatAttributes.Expanded) { XmlElement formula = Document.CreateElement("formula", DatalogNamespaceURL); WriteAtom(formula, fact, true); target.AppendChild(formula); } else { WriteAtom(target, fact, true); } }
/// <summary> /// Instantiates a new Fact event definition. /// </summary> /// <remarks> /// DO NOT ASSERT OR RETRACT FACTS WHEN HANDLING THIS EVENT! /// </remarks> /// <param name="fact">The Fact that generated the event.</param> /// <param name="fact">The Other Fact that generated the event.</param> public NewFactEventArgs(Fact fact, Fact otherFact) { this.fact = fact; this.otherFact = otherFact; }
// -------------------- Static Members -------------------- /// <summary> /// Populates the Variable predicates of a target Atom, using another Atom as a template /// and a Fact as the source of data, i.e. Individual predicates. /// </summary> /// <param name="data">The data for populating the Atom.</param> /// <param name="template">The template of Atom being populated.</param> /// <param name="target">The members to populate.</param> public static void Populate(Fact data, Atom template, IPredicate[] members) { for(int i=0;i<members.Length;i++) if (members[i] is Variable) { // try to locate a Variable with the same name in the template int j = Array.IndexOf(template.Members, members[i]); if (j >= 0) { members[i] = data.Members[j]; } else { // try to locate a Slot with the same name in the template j = Array.IndexOf(template.SlotNames, members[i].Value); if (j >= 0) members[i] = data.Members[j]; } } }
/// <summary> /// Prepare the atom to be pattern matched by replacing in a fact: /// - all the predicates that match function predicates in the passed atom with /// the string reprentation of these function predicates, /// - all the predicates that match individual predicates in the passed atom with /// the string reprentation of these individual predicates. /// </summary> /// <remarks> /// This operation must be done *if and only if* the fact matches the atom. /// </remarks> /// <param name="factToResolve">The fact that must be resolved.</param> /// <param name="atom">The atom with which the current fact matches.</param> /// <returns>A new fact with only String individuals.</returns> public static Fact Resolve(Fact factToResolve, Atom atom) { IPredicate[] predicates = new IPredicate[factToResolve.Members.Length]; for(int i=0; i<factToResolve.Members.Length; i++) if ((atom.Members[i] is Function) || ((atom.Members[i] is Individual) && (!(factToResolve.Members[i].Value is System.String)))) predicates[i] = new Individual(atom.Members[i].ToString()); else predicates[i] = factToResolve.Members[i]; return new Fact(factToResolve.Type, predicates); }
/// <summary> /// Prepare the atom to be pattern matched by replacing in a fact: /// - all the predicates that match function predicates in the passed atom with /// the string representation of these function predicates, /// - all the predicates that match individual predicates in the passed atom with /// the string representation of these individual predicates. /// </summary> /// <remarks> /// This operation must be done *if and only if* the fact matches the atom. /// </remarks> /// <param name="factToResolve">The fact that must be resolved.</param> /// <param name="atom">The atom with which the current fact matches.</param> /// <returns>A new fact with only String individuals.</returns> public static Fact Resolve(Fact factToResolve, Atom atom) { return Resolve(true, factToResolve, atom); }