/// <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 HashtableMatchedFactStorage(Atom template) { if (!typingLocked) typingLocked=true; this.template = template; factTable = new Hashtable(); predicateTables = new TypeAwareHashtable[template.Members.Length]; InitializePredicateStorage(); }
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()); } }
/// <summary> /// Instantiates a new Implication. /// </summary> /// <param name="label">The label of the new implication.</param> /// <param name="priority">The priority of the new implication.</param> /// <param name="mutex">String.Empty or the label of an implication mutexed by the new one.</param> /// <param name="precondition">String.Empty or the label of an implication that preconditions the new one.</param> /// <param name="deduction">The Atom used as a prototype for what this Implication tries to proove.</param> /// <param name="atomGroup">The top level group of atoms used in the query part (pattern matching) of the new Implication.</param> /// <see cref="org.nxbre.ie.rule.ImplicationPriority"/> public Implication(string label, ImplicationPriority priority, string mutex, string precondition, Atom deduction, AtomGroup atomGroup) : this(label, (int)priority, mutex, precondition, deduction, atomGroup) { }
/// <summary> /// Instantiates a new anonymous (not labelled) Fact based on an existing Atom. /// </summary> /// <param name="atom">The Atom that the fact will be built on.</param> public Fact(Atom atom):this(null, atom.Type, atom.Members) {}
/// <summary> /// Checks if the current Atom matches with another one, i.e. if they are of same type, /// contain the same number of predicates, and if their Individual predicates are equal. /// </summary> /// <description> /// This functions takes care of casting as it always tries to cast to the strongest type /// of two compared individuals. Since predicates can come from weakly-typed rule files /// (Strings) and other predicates can be generated by the user, this function tries to /// convert from String to the type of the other predicate (as String is considered not /// strongly typed). /// </description> /// <param name="atom">The other atom to determine the matching.</param> /// <returns>True if the two atoms match.</returns> public bool Matches(Atom atom) { if (!BasicMatches(atom)) return false; for(int i=0; i<members.Length; i++) { if ((members[i] is Individual) && (atom.members[i] is Function) && (!((Function)atom.members[i]).Evaluate((Individual)members[i]))) return false; else if ((members[i] is Function) && (atom.members[i] is Individual) && (!((Function)members[i]).Evaluate((Individual)atom.members[i]))) return false; else if ((members[i] is Function) && (atom.members[i] is Function) && (!(members[i].Equals(atom.members[i])))) return false; else if ((members[i] is Individual) && (atom.members[i] is Individual)) { if ((members[i].Value.GetType() == atom.members[i].Value.GetType()) && (!members[i].Equals(atom.members[i]))) // the two individuals are of same types: direct compare return false; else { // the two individuals are of different types object member1 = members[i].Value; object member2 = atom.members[i].Value; Reflection.CastToStrongType(ref member1, ref member2); if (!member1.Equals(member2)) return false; } } } return true; }
/// <summary> /// Check if the current intersects with another one, which means that they match together /// and that their Variable predicates at the same position are equal. /// </summary> /// <param name="atom">The other atom to determine the intersection.</param> /// <returns>True if the two atoms intersect.</returns> /// <remarks>IsIntersecting calls Matches first.</remarks> public bool IsIntersecting(Atom atom) { if (!Matches(atom)) return false; for(int i=0; i<members.Length; i++) if ((members[i] is Variable) && (members[i].Equals(atom.members[i]))) return true; return false; }
/// <summary> /// Instantiates a new Implication. /// </summary> /// <param name="label">The label of the new implication.</param> /// <param name="priority">The priority of the new implication.</param> /// <param name="mutex">String.Empty or the label of an implication mutexed by the new one.</param> /// <param name="precondition">String.Empty or the label of an implication that preconditions the new one.</param> /// <param name="deduction">The Atom used as a prototype for what this Implication tries to proove.</param> /// <param name="atomGroup">The top level group of atoms used in the query part (pattern matching) of the new Implication.</param> /// <param name="action">The implication action.</param> /// <see cref="org.nxbre.ie.rule.ImplicationAction"/> /// <see cref="org.nxbre.ie.rule.ImplicationPriority"/> public Implication(string label, int priority, string mutex, string precondition, Atom deduction, AtomGroup atomGroup, ImplicationAction action):base(label, atomGroup) { if (deduction.HasFunction) throw new BREException("Can not create Implication with functions in the Deduction: " + deduction.ToString()); if ((mutex != null) && (mutex == label)) throw new BREException("An Implication can not Mutex itself: " + mutex); if ((precondition != null) && (precondition == label)) throw new BREException("An Implication can not Pre-Condition itself: " + precondition); foreach(object member in AtomGroup.Members) if ((member is Atom) && (((Atom)member).HasNotStringIndividual)) throw new BREException("Can not create Implication with non-String individuals in the atoms: " + member.ToString()); if ((priority < (int)ImplicationPriority.Minimum) || (priority > (int)ImplicationPriority.Maximum)) throw new ArgumentOutOfRangeException("Priority must be in the [" + ImplicationPriority.Minimum + "-" + ImplicationPriority.Maximum + "] range."); if (action == ImplicationAction.Count) { if (deduction.IsFact) throw new BREException("A counting Implication must have one Variable predicate in its deduction atom."); int varCount = 0; foreach(IPredicate member in deduction.Members) { if (member is Variable) varCount++; if (varCount > 1) throw new BREException("A counting Implication must have only one Variable predicate in its deduction atom."); } } this.priority = priority; this.deduction = deduction; this.mutex = mutex; this.precondition = precondition; this.action = action; }
/// <summary> /// Checks if the current Atom basic matches with another one, i.e. if they are of same type, /// and contain the same number of predicates. /// </summary> /// <param name="atom">The other atom to determine the basic matching.</param> /// <returns>True if the two atoms basic match.</returns> public bool BasicMatches(Atom atom) { return ((atom.Type == Type) && (atom.members.Length == members.Length)); }
private void WriteEquivalentAtom(XmlElement target, Atom atom) { if (syntax == SaveFormatAttributes.Expanded) { XmlElement torso = Document.CreateElement("torso", DatalogNamespaceURL); WriteAtom(torso, atom, false); target.AppendChild(torso); } else { WriteAtom(target, atom, false); } }
/// <summary> /// Check if the current intersects with another one, which means that: /// - they Match() together, /// - their predicate types are similar, /// - if there are variables, at least one should be equal to the corresponding one. /// </summary> /// <param name="atom">The other atom to determine the intersection.</param> /// <returns>True if the two atoms intersect.</returns> /// <remarks>IsIntersecting calls Matches first.</remarks> public bool IsIntersecting(Atom atom) { if (!Matches(atom)) return false; for(int i=0; i<predicates.Length; i++) if (predicates[i].GetType() != atom.predicates[i].GetType()) return false; if (!HasVariable) return true; int nonMatchingVariables = 0; int variableCount = 0; for(int i=0; i<predicates.Length; i++) { variableCount++; if (predicates[i] is Variable) { if (!(predicates[i].Equals(atom.predicates[i]))) nonMatchingVariables++; } } if (variableCount < predicates.Length) return true; else return (nonMatchingVariables < variableCount); }
/// <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> /// Protected constructor used for cloning purpose. /// </summary> /// <param name="source"></param> protected Atom(Atom source) : this(source, (IPredicate[])source.predicates.Clone()) { }
/// <summary> /// If the passed atom matches one of the atoms in the Equivalent pair, returns the other atom, with its variable /// names modified to match the ones of the passed atom. /// </summary> /// <param name="atom">The atom that is potentially equivalent to one of the atoms of the Equivalent pair.</param> /// <returns>The atom equivalent to the passed one, or null if none of the atom pair is matching it.</returns> public Atom Get(Atom atom) { if (firstAtom.Matches(atom)) return Atom.TranslateVariables(atom, firstAtom, secondAtom); else if (secondAtom.Matches(atom)) return Atom.TranslateVariables(atom, secondAtom, firstAtom); else return null; }
// -------------------- 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> /// Instantiates a new Equivalent pair of atoms. /// </summary> /// <param name="label">The optional label, or null if not needed.</param> /// <param name="firstAtom">One of the atoms in the Equivalent pair.</param> /// <param name="secondAtom">The other atom of the Equivalent pair.</param> /// <remarks>This object is immutable</remarks> public Equivalent(string label, Atom firstAtom, Atom secondAtom) { this.label = label; this.firstAtom = firstAtom; this.secondAtom = secondAtom; }
/// <summary> /// Instantiates a new Equivalent pair of atoms. /// </summary> /// <param name="firstAtom">One of the atoms in the Equivalent pair.</param> /// <param name="secondAtom">The other atom of the Equivalent pair.</param> /// <remarks>This object is immutable</remarks> public Equivalent(Atom firstAtom, Atom secondAtom) : this(null, firstAtom, secondAtom) { }
/// <summary> /// Internal method used for exploring the potential hierarchy of equivalence and building the complete /// list of atoms equivalent to one atom. /// </summary> internal static ArrayList GetAll(ArrayList equivalentPairs, Atom atom, ArrayList equivalentAtoms) { if ((atom != null) && (!equivalentAtoms.Contains(atom))) { equivalentAtoms.Add(atom); foreach(Equivalent equivalentPair in equivalentPairs) GetAll(equivalentPairs, equivalentPair.Get(atom), equivalentAtoms); } // returning the list is not necessary at all but just convenient for the calling methods return equivalentAtoms; }
/// <summary> /// Instantiates a new labelled Fact based on an existing Atom and a provided Label. /// </summary> /// <param name="label">The Label of the new Fact.</param> /// <param name="atom">The Atom that the Fact will be built on.</param> public Fact(string label, Atom atom):this(label, atom.Type, atom.Members) {}
/// <summary> /// Translates variable names of a target atom with names from a template atom matching the position of a /// source atom. /// </summary> /// <remarks>Template and source atoms must match together else unpredictible result may occur.</remarks> /// <param name="template"></param> /// <param name="target"></param> /// <returns></returns> public static Atom TranslateVariables(Atom template, Atom source, Atom target) { IPredicate[] resultMembers = new IPredicate[target.Members.Length]; for(int i=0; i<target.Members.Length; i++) { IPredicate targetMember = target.Members[i]; if (targetMember is Variable) { int indexOfSourceMember = Array.IndexOf(source.Members, targetMember); if (indexOfSourceMember >= 0) resultMembers[i] = template.Members[indexOfSourceMember]; else resultMembers[i] = targetMember; } else resultMembers[i] = targetMember; } return new Atom(template.Negative, target.Type, resultMembers); }
/// <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); }
protected override void WriteAtom(XmlElement target, Atom atom, bool inFact) { XmlElement eAtom = eAtom = Document.CreateElement("Atom", DatalogNamespaceURL); if (atom is Fact) WriteLabel(eAtom, ((Fact)atom).Label); XmlElement rel = Document.CreateElement("Rel", DatalogNamespaceURL); rel.InnerText = atom.Type; if (atom is AtomFunction) { switch (((AtomFunction)atom).ResolutionType) { case AtomFunction.RelationResolutionType.Binder: rel.SetAttribute("uri", "nxbre://binder"); break; case AtomFunction.RelationResolutionType.NxBRE: rel.SetAttribute("uri", "nxbre://operator"); break; case AtomFunction.RelationResolutionType.Expression: rel.SetAttribute("uri", "nxbre://expression"); break; } } if (syntax != SaveFormatAttributes.Compact) { XmlElement opr = Document.CreateElement("op", DatalogNamespaceURL); opr.AppendChild(rel); eAtom.AppendChild(opr); } else { eAtom.AppendChild(rel); } for(int i=0; i<atom.Members.Length; i++) { IPredicate pre = atom.Members[i]; // build the predicate element depending on its type XmlElement predicate; if (pre is Variable) { predicate = Document.CreateElement("Var", DatalogNamespaceURL); predicate.InnerText = pre.Value.ToString(); } else if (pre is Individual) { if (pre.Value is HyperLink) { // we deal with the special case of hyperlinks HyperLink hl = (HyperLink)pre.Value; predicate = Document.CreateElement("Ind", DatalogNamespaceURL); predicate.SetAttribute("uri", hl.Uri); predicate.InnerText = hl.Text; } else { string sourceType = ((Individual)pre).SourceType; if ((forceDataTyping) && (!(pre.Value is string)) && ((sourceType == null) || (sourceType == String.Empty))) sourceType = Schema.GetSchemaTypeFromClr(pre.Value); if ((sourceType != null) && (sourceType != String.Empty)) { // we persist as a typed data predicate = Document.CreateElement("Data", DatalogNamespaceURL); predicate.SetAttribute("type", Schema.NS_URI, "xs:" + sourceType); predicate.InnerText = Schema.FromClr(pre.Value, sourceType); } else { // we persist as a String based individual predicate = Document.CreateElement("Ind", DatalogNamespaceURL); predicate.InnerText = pre.Value.ToString(); } } } else if (pre is Formula) { predicate = Document.CreateElement("Ind", DatalogNamespaceURL); predicate.SetAttribute("uri", (((Formula)pre).ResolutionType == Formula.FormulaResolutionType.Binder)?"nxbre://binder":"nxbre://expression"); predicate.InnerText = pre.Value.ToString(); } else if (pre is Function) { Function function = (Function)pre; predicate = Document.CreateElement("Ind", DatalogNamespaceURL); predicate.SetAttribute("uri", (function.ResolutionType == Function.FunctionResolutionType.NxBRE)?"nxbre://operator":(IsExpressionBinder(function.Binder)?"nxbre://expression":"nxbre://binder")); predicate.InnerText = pre.Value.ToString(); } else { // should never happen throw new BREException("Can not persist a rulebase containing a predicate of type: " + pre.GetType().FullName); } // add wrapper elements if necessary: if there is one or more slot, args can not be used string slotName = atom.SlotNames[i]; if (slotName != String.Empty) { XmlElement slot = Document.CreateElement("slot", DatalogNamespaceURL); XmlElement slotInd = Document.CreateElement("Ind", DatalogNamespaceURL); slotInd.InnerText = slotName; slot.AppendChild(slotInd); slot.AppendChild(predicate); eAtom.AppendChild(slot); } else if ((!atom.HasSlot) && (syntax == SaveFormatAttributes.Expanded)) { XmlElement argument = Document.CreateElement("arg", DatalogNamespaceURL); argument.SetAttribute("index", (i+1).ToString()); argument.AppendChild(predicate); eAtom.AppendChild(argument); } else { eAtom.AppendChild(predicate); } } if (atom.Negative) { XmlElement naf = Document.CreateElement("Naf", DatalogNamespaceURL); if (syntax == SaveFormatAttributes.Expanded) { XmlElement weak = Document.CreateElement("weak", DatalogNamespaceURL); weak.AppendChild(eAtom); naf.AppendChild(weak); } else { naf.AppendChild(eAtom); } target.AppendChild(naf); } else { target.AppendChild(eAtom); } }
/// <summary> /// Protected constructor used for cloning purpose. /// </summary> /// <param name="source">The atom to use as a template.</param> /// <param name="members">The members to use instead of the ones in the source, or null if the ones of the source must be used.</param> protected Atom(Atom source, IPredicate[] members) : this(source.negative, source.type, members) { this.slotNames = source.slotNames; }
/// <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); }
// ----------------- Static methods ---------------- /// <summary> /// Resolves all Function predicates by replacing them by their String representations. /// </summary> /// <param name="atom">The Atom to resolve.</param> /// <returns>A new Atom where all Function predicates have been resolved. If no /// Function predicate exists, it returns a clone of the current Atom.</returns> public static Atom ResolveFunctions(Atom atom) { if (atom.HasFunction) { IPredicate[] predicates = new IPredicate[atom.predicates.Length]; for(int i=0; i<atom.predicates.Length; i++) if (atom.predicates[i] is Function) predicates[i] = new Individual(atom.predicates[i].ToString()); else predicates[i] = atom.predicates[i]; return new Atom(atom.Negative, atom.Type, predicates); } else return (Atom)atom.Clone(); }
/// <summary> /// Instantiates a new anonymous (not labelled) Fact based on an existing Atom. /// </summary> /// <param name="atom">The Atom that the fact will be built on.</param> public Fact(Atom atom) : this(null, atom) { }
/// <summary> /// Checks if the current Atom basic matches with another one, i.e. if they are of same type, /// and contain the same number of predicates. /// </summary> /// <param name="atom">The other atom to determine the basic matching.</param> /// <returns>True if the two atoms basic match.</returns> public bool BasicMatches(Atom atom) { return ((atom.Type == Type) && (atom.predicates.Length == predicates.Length)); }
/// <summary> /// Instantiates a new labelled Fact based on an existing Atom and a provided Label. /// </summary> /// <param name="label">The Label of the new Fact.</param> /// <param name="atom">The Atom that the Fact will be built on.</param> public Fact(string label, Atom atom) : base(atom) { this.label = label; }
/// <summary> /// Checks if the current Atom matches with another one, i.e. if they are of same type, /// contain the same number of predicates, and if their Individual predicates are equal. /// </summary> /// <description> /// This functions takes care of casting as it always tries to cast to the strongest type /// of two compared individuals. Since predicates can come from weakly-typed rule files /// (Strings) and other predicates can be generated by the user, this function tries to /// convert from String to the type of the other predicate (as String is considered not /// strongly typed). /// </description> /// <param name="atom">The other atom to determine the matching.</param> /// <returns>True if the two atoms match.</returns> public bool Matches(Atom atom) { if (!BasicMatches(atom)) return false; for(int i=0; i<predicates.Length; i++) { if ((predicates[i] is Individual) && (atom.predicates[i] is Function) && (!((Function)atom.predicates[i]).Evaluate((Individual)predicates[i]))) return false; else if ((predicates[i] is Function) && (atom.predicates[i] is Individual) && (!((Function)predicates[i]).Evaluate((Individual)atom.predicates[i]))) return false; else if ((predicates[i] is Function) && (atom.predicates[i] is Function) && (!(predicates[i].Equals(atom.predicates[i])))) return false; else if ((predicates[i] is Individual) && (atom.predicates[i] is Individual)) { // we have two individuals if ((predicates[i].Value.GetType() == atom.predicates[i].Value.GetType()) && (!predicates[i].Equals(atom.predicates[i]))) { // the two individuals are of same types: direct compare return false; } else { // the two individuals are of different types ObjectPair pair = new ObjectPair(predicates[i].Value, atom.predicates[i].Value); Reflection.CastToStrongType(pair); if (!pair.First.Equals(pair.Second)) return false; } } } return true; }
private Fact BuildFact(Atom targetAtom, ArrayList processResult) { Atom factBuilder = FactBase.Populate(targetAtom, processResult, true); if (!factBuilder.IsFact) { if (IEImpl.StrictImplication) throw new BREException("Strict implication rejected the assertion of incompletely resolved fact: " + factBuilder); } else { return new Fact(factBuilder); } return null; }