private static string GetNodeLabel(ReteNode reteNode) { var labelParts = new List <object>(); labelParts.Add(reteNode.NodeType.ToString()); switch (reteNode.NodeType) { case NodeType.Type: labelParts.Add(reteNode.OutputType.Name); break; case NodeType.Selection: labelParts.AddRange(reteNode.Expressions.Select(x => $"{x.Value.Body}")); break; case NodeType.Join: labelParts.AddRange(reteNode.Expressions.Select(x => $"{x.Value.Body}")); break; case NodeType.Aggregate: labelParts.Add(reteNode.Properties.Single(x => x.Key == "Name").Value); labelParts.AddRange(reteNode.Expressions.Select(x => $"{x.Key}={x.Value.Body}")); break; case NodeType.Binding: labelParts.AddRange(reteNode.Expressions.Select(x => $"{x.Value.Body}")); break; case NodeType.Rule: labelParts.Add(reteNode.Rules.Single().Name); labelParts.AddRange(reteNode.Expressions.Select(x => $"{x.Key}={x.Value.Body}")); break; } var label = string.Join("\n", labelParts); return(label); }
/// <summary> /// To remove an existing production from the network, we start down at the bottom of the /// beta network, at the p-node for that production. The basic idea is to start walking from there /// up to the top of the net. At each node, we clean up any tokens it contains, and then get rid of /// the node | i.e., remove it from the children or successors lists on its predecessors (its parent /// and, for some nodes, its alpha memory as well), and deallocate it. We then move up to the /// predecessors. If the alpha memory is not being shared by another production, we deallocate it /// too. If the parent is not being shared by another production, then we apply the same procedure /// to it | clean up its tokens, etc. | and repeat this until we reach either a node being shared by /// some other production, or the top of the beta network. /// </summary> /// <param name="node">The node.</param> private void delete_node_and_any_unused_ancestors(ReteNode node) { if (node.Type == ReteNodeType.NCC) { delete_node_and_any_unused_ancestors(node); } if (node.Type == ReteNodeType.BetaMemory) { while (((BetaMemory) node).Items.Count > 0) { delete_token_and_descendents(((BetaMemory) node).Items[0]); } } if (node.Type == ReteNodeType.Negative) { while (((NegativeNode) node).Items.Count > 0) { delete_token_and_descendents(((NegativeNode) node).Items[0]); } } if (node.Type == ReteNodeType.NCC) { while (((NCCNode) node).Items.Count > 0) { delete_token_and_descendents(((NCCNode) node).Items[0]); } } if (node.Type == ReteNodeType.NCCPartner) { while (((NCCPartnerNode) node).NewResultBuffer.Count > 0) { delete_token_and_descendents(((NCCPartnerNode) node).NewResultBuffer[0]); } } if (node.Type == ReteNodeType.Production) { ProductionNode pNode = (ProductionNode) node; while (pNode.Items.Count > 0) { delete_token_and_descendents(pNode.Items[0]); //pNode.items.RemoveFirst(); } //_rules_that_fired.Remove(pNode); } if (node.Type == ReteNodeType.Join) { if (((JoinNode) node).IsRightUnlinked == false) { ((JoinNode) node).AlphaMemory.Successors.Remove(node); } if (((JoinNode) node).IsLeftUnlinked == false) { node.Parent.Children.Remove(node); } --((JoinNode) node).AlphaMemory.ReferenceCount; if (((JoinNode) node).AlphaMemory.ReferenceCount == 0) { delete_alpha_memory(((JoinNode) node).AlphaMemory); } } if (node.Type == ReteNodeType.Negative) { if (((NegativeNode) node).IsRightUnlinked == false) { ((NegativeNode) node).AlphaMemory.Successors.Remove(node); } --((NegativeNode) node).AlphaMemory.ReferenceCount; if (((NegativeNode) node).AlphaMemory.ReferenceCount == 0) { delete_alpha_memory(((NegativeNode) node).AlphaMemory); } } if (node.Type == ReteNodeType.Join) { JoinNode tmpNode = (JoinNode) node; ((BetaMemory) tmpNode.Parent).AllChildren.Remove(node); if (((BetaMemory) tmpNode.Parent).AllChildren.Count == 0) { delete_node_and_any_unused_ancestors(((JoinNode) node).Parent); } } else if (node.Parent.Children.Count == 0) { delete_node_and_any_unused_ancestors(node.Parent); } }
/// <summary> /// The build-or-share-network-for-conditions helper function takes a list of conditions, builds /// or shares a network structure for them underneath the given parent node, and returns the /// lowermost node in the new-or-shared network. Note that the list of conditions can contain /// negative conditions or NCC's, not just positive conditions. /// </summary> /// <param name="parent">The parent.</param> /// <param name="conds">The conds.</param> /// <param name="earlier_conds">The earlier_conds.</param> /// <returns></returns> private ReteNode build_or_share_network_for_conditions(ReteNode parent, IEnumerable<LeftHandSideCondition> conds, List<LeftHandSideCondition> earlier_conds) { List<LeftHandSideCondition> conds_higher_up; ReteNode current_node = parent; if (earlier_conds == null) conds_higher_up = new List<LeftHandSideCondition>(); else conds_higher_up = earlier_conds; foreach (LeftHandSideCondition cond in conds) { if (cond.ConditionType == ConditionType.Positive) { current_node = build_or_share_beta_memory_node(current_node); List<TestAtJoinNode> tests = get_join_tests_from_condition(cond, conds_higher_up); AlphaMemory am = build_or_share_alpha_memory(cond); current_node = build_or_share_join_node(current_node as BetaMemory, am, tests); } else if (cond.ConditionType == ConditionType.Negative) { List<TestAtJoinNode> tests = get_join_tests_from_condition(cond, conds_higher_up); AlphaMemory am = build_or_share_alpha_memory(cond); current_node = build_or_share_negative_node(current_node, am, tests); } else if (cond.ConditionType == ConditionType.NCC) { current_node = build_or_share_ncc_nodes(current_node as JoinNode, cond, conds_higher_up); } else if (cond.ConditionType == ConditionType.Function) { current_node = build_builtin_node(current_node, cond, conds_higher_up); } conds_higher_up.Add(cond); } return current_node; }
/// <summary> /// The function for creating new negative nodes is similar to the ones for creating beta memories /// and join nodes. However, one additional consideration is important with negative conditions, /// and also with conjunctive negations. Any time there is a variable <v> which is tested in a negative /// condition and bound in one or more other (positive) conditions, at least one of these positive /// conditions must come before the negative condition. Recall that when we add a production to /// the network, the network-construction routines are given a list of its conditions in some order. If /// all conditions are positive, any order will work. Negative conditions require the aforementioned /// constraint on the order, though, because negative nodes need to be able to access the appropriate /// variable bindings in tokens, and the tokens "seen" by negative nodes indicate only variable /// bindings from earlier conditions, i.e., conditions higher up in the network. /// </summary> /// <param name="parent">The parent.</param> /// <param name="am">The am.</param> /// <param name="tests">The tests.</param> /// <returns></returns> private ReteNode build_or_share_negative_node(ReteNode parent, AlphaMemory am, ICollection<TestAtJoinNode> tests) { foreach (ReteNode child in parent.Children) { if (child.Type == ReteNodeType.Negative) { NegativeNode negativenodeChild = (NegativeNode) child; if (negativenodeChild.AlphaMemory == am) { if (negativenodeChild.Tests.Count == 0 && tests.Count == 0) { return child; } else { //Need to compare tests... throw new ApplicationException("Unhandled..."); } } } } NegativeNode new_node = new NegativeNode(); new_node.Type = ReteNodeType.Negative; // "negative"; new_node.Parent = parent; parent.Children.AddToFront(new_node); foreach (TestAtJoinNode test in tests) { new_node.Tests.Add(test); } new_node.AlphaMemory = am; am.Successors.AddToFront(new_node); ++am.ReferenceCount; new_node.NearestAncestorWithSameAmem = find_nearest_ancestor_with_same_amem(parent, am); update_new_node_with_matches_from_above(new_node); // *** Right Unlinking *** if (new_node.Items.Count == 0) { am.Successors.Remove(new_node); new_node.IsRightUnlinked = true; } // *** End Right Unlinking *** return new_node; }
/// <summary> /// We will use several helper functions to make the main add-production procedure simpler. /// The first one, build-or-share-beta-memory-node, looks for an existing beta memory node that is /// a child of the given parent node. If there is one, it returns it so it can be shared by the new /// production; otherwise the function builds a new one and returns it. This pseudocode assumes /// that beta memories are not indexed; if indexing is used, the procedure would take an extra /// argument specifying which field(s) the memory must be indexed on. /// </summary> /// <param name="parent">The parent.</param> /// <returns></returns> private ReteNode build_or_share_beta_memory_node(ReteNode parent) { if (parent is DummyTopNode) { return parent; } foreach (ReteNode child in parent.Children) { if (child.Type == ReteNodeType.BetaMemory) { return child; } } BetaMemory new_rete = new BetaMemory(); new_rete.Type = ReteNodeType.BetaMemory; new_rete.Parent = parent; new_rete.Label = "B" + (++_next_beta_node); parent.Children.AddToFront(new_rete); update_new_node_with_matches_from_above(new_rete); return new_rete; }
/// <summary> /// Build_builtin_nodes the specified parent. /// </summary> /// <param name="parent">The parent.</param> /// <param name="c">The c.</param> /// <param name="earlier_conds">The earlier_conds.</param> /// <returns></returns> private BuiltinMemory build_builtin_node(ReteNode parent, Condition c, IList<LeftHandSideCondition> earlier_conds) { BuiltinMemory new_node = new BuiltinMemory(c.ToString()); new_node.Type = ReteNodeType.Builtin; new_node.Parent = parent; parent.Children.AddToFront(new_node); new_node.Builtin = ((FuncTerm) c.Attribute).Builtin; int cntOfEarlierConditions = earlier_conds.Count - 1; if (c.Id.TermType == TermType.Variable) { for (int i = cntOfEarlierConditions; i >= 0; i--) { Condition earlier_cond = earlier_conds[i]; if (earlier_cond.ConditionType == ConditionType.Positive) { for (int f2 = 0; f2 < 3; f2++) { Variable o = earlier_cond.Fields[f2] as Variable; if (o != null && o.Equals(c.Id)) { VariableSubstituter vs = new VariableSubstituter(); vs.FieldNumber = f2; vs.NumberOfLevelsUp = (cntOfEarlierConditions - i); vs.BindingPair.Variable = o; new_node.LeftArgument = vs; f2 = 3; i = -1; //escape loop of cntOfEarlierConditions } } } } } else { new_node.LeftArgument = new ConstantSubstitutor(c.Id); } if (c.Value.TermType == TermType.Variable) { for (int i = cntOfEarlierConditions; i >= 0; i--) { Condition earlier_cond = earlier_conds[i]; if (earlier_cond.ConditionType == ConditionType.Positive) { for (int f2 = 0; f2 < 3; f2++) { Variable o = earlier_cond.Fields[f2] as Variable; if (o != null && o.Equals(c.Value)) { VariableSubstituter vs = new VariableSubstituter(); vs.FieldNumber = f2; vs.NumberOfLevelsUp = (cntOfEarlierConditions - i); vs.BindingPair.Variable = o; new_node.RightArgument = vs; f2 = 3; i = -1; //escape loop of cntOfEarlierConditions } } } } } else { new_node.RightArgument = new ConstantSubstitutor(c.Value); } update_new_node_with_matches_from_above(new_node); return new_node; }
/// <summary> /// Adds the production. /// </summary> /// <param name="new_production">The new_production.</param> /// <param name="lhs">The LHS.</param> private void AddProduction(ReteNode new_production, IEnumerable<LeftHandSideCondition> lhs) { ReteNode current_node = build_or_share_network_for_conditions(_dummy_top_node, lhs, null); new_production.Parent = current_node; current_node.Children.AddToFront(new_production); update_new_node_with_matches_from_above(new_production); }
/// <summary> /// Called when [rete node] is visited. /// </summary> /// <param name="node">The node.</param> public virtual void OnReteNode(ReteNode node) { }
/// <summary> /// Controls the activation flow based on node type /// </summary> /// <param name="child">The child.</param> /// <param name="w">The w.</param> private void right_activation(ReteNode child, WME w) { switch (child.Type) { case ReteNodeType.Join: join_node_right_activation(child as JoinNode, w); break; case ReteNodeType.Negative: negative_node_right_activation(child as NegativeNode, w); break; default: throw new ApplicationException("???"); } }
/// <summary> /// Similarly, whenever a new token tok = ht;wi is added to a beta memory, we add tok to /// t.children and to w.tokens. We also fill in the new node field on the token. To simplify our /// pseudocode, it is convenient to define a "helper" function make-token which builds a new token /// and initializes its various fields as necessary for tree-based removal. Although we write this as /// a separate function, it would normally be coded "inline" for efficiency. /// </summary> /// <param name="node">The node.</param> /// <param name="parent">The parent.</param> /// <param name="w">The w.</param> /// <returns></returns> private Token make_token(ReteNode node, Token parent, WME w) { Token tok = new Token(); tok.Parent = parent; tok.WME = w; tok.Node = node; parent.Children.AddToFront(tok); if (w != null) { w.Tokens.AddToFront(tok); } return tok; }
/// <summary> /// Left_activations the specified new_node. /// </summary> /// <param name="new_node">The new_node.</param> /// <param name="tok">The tok.</param> /// <param name="w">The w.</param> private void left_activation(ReteNode new_node, Token tok, WME w) { switch (new_node.Type) { case ReteNodeType.BetaMemory: beta_memory_left_activation(new_node as BetaMemory, tok, w); break; case ReteNodeType.Negative: negative_node_left_activation(new_node as NegativeNode, tok, w); break; case ReteNodeType.Builtin: builtin_node_left_activation(new_node as BuiltinMemory, tok, w); break; case ReteNodeType.NCCPartner: ncc_partner_node_left_activation(new_node as NCCPartnerNode, tok, w); break; case ReteNodeType.NCC: ncc_node_left_activation(new_node as NCCNode, tok, w); break; case ReteNodeType.Join: join_node_left_activation(new_node as JoinNode, tok); break; case ReteNodeType.Production: p_node_activation(new_node as ProductionNode, tok, w); break; case ReteNodeType.Mutex: mutex_node_activation(new_node as MutexNode, tok, w); break; case ReteNodeType.Aggregator: aggregator_node_activation(new_node as AggregatorNode, tok, w); break; default: throw new ApplicationException("Unknown left_activation type: " + new_node.GetType().Name); } }
/// <summary> /// For right unlinking, we need to initialize the nearest-ancestor-with-same-amem fields on /// newly-created join and negative nodes. The find-nearest-ancestor-with-same-amem procedure /// finds the appropriate value. It starts at a given node and walks up the beta network, returning /// the first node it finds that uses a given alpha memory. Note that this node may be in the /// subnetwork for a conjunctive negation. /// </summary> /// <param name="node">The node.</param> /// <param name="am">The am.</param> /// <returns></returns> private ReteNode find_nearest_ancestor_with_same_amem(ReteNode node, AlphaMemory am) { if (node is DummyTopNode) { return null; } if (node.Type == ReteNodeType.Dummy) { return null; } if (node.Type == ReteNodeType.Join) { if (((JoinNode) node).AlphaMemory == am) { return node; } } if (node.Type == ReteNodeType.Negative) { if (((NegativeNode) node).AlphaMemory == am) { return node; } } if (node.Type == ReteNodeType.NCC) { return find_nearest_ancestor_with_same_amem(((NCCNode) node).Partner.Parent, am); } return find_nearest_ancestor_with_same_amem(node.Parent, am); }
private string Id(ReteNode reteNode) { return($"{reteNode.Id}"); }
/// <summary> /// Called when [rete node] is visited. /// </summary> /// <param name="node">The node.</param> public override void OnReteNode(ReteNode node) { _sb.AppendLine(node.ToString()); }
/// <summary> /// The update-new-node-with-matches-from-above procedure initializes the memory node to store /// tokens for any existing matches for the earlier conditions. /// </summary> /// <remarks> /// Finally, we give the update-new-node-with-matches-from-above procedure. This is needed /// to ensure that newly added productions are immediately matched against the current working /// memory. The procedure's job is to ensure that the given new-node's left-activation procedure is /// called with all the existing matches for the previous conditions, so that the new-node can take /// any appropriate actions (e.g., a beta memory stores the matches as new tokens, and a p-node /// signals new complete matches for the production). How update-new-node-with-matches-from- /// above achieves this depends on what kind of node the new-node's parent is. If the parent is a /// beta memory (or a node for a negated condition, as we will discuss later), this is straightforward, /// since the parent has a list (items) of exactly the matches we want. But if the parent node is a /// join node, we want to find these matches, we iterate over the WMEs and tokens in the join node's alpha /// and beta memories and perform the join tests on each pair. The pseudocode below uses a trick to /// do this: while temporarily pretending the new-node is the only child of the join node, it runs the /// join node's right-activation procedure for all the WMEs in its alpha memory; any new matches /// will automatically be propagated to the new-node. For a variation of this implementation, see /// (Tambe et al., 1988); for a general discussion, see (Lee and Schor, 1992). /// </remarks> /// <param name="newNode">The new node.</param> private void update_new_node_with_matches_from_above(ReteNode newNode) { ReteNode parent = newNode.Parent; switch (parent.Type) { case ReteNodeType.BetaMemory: BetaMemory tmpBetaMem = (BetaMemory) parent; foreach (Token tok in tmpBetaMem.Items) { left_activation(newNode, tok, new WME()); } break; case ReteNodeType.Builtin: BuiltinMemory tmpBiMem = (BuiltinMemory) parent; foreach (Token tok in tmpBiMem.Items) { left_activation(newNode, tok, new WME()); } break; case ReteNodeType.Join: BigList<ReteNode> saved_list_of_children = parent.Children.Clone(); parent.Children.Clear(); parent.Children.AddToFront(newNode); JoinNode tmpJNode = (JoinNode) parent; if (tmpJNode.AlphaMemory != null) { foreach (ItemInAlphaMemory item in tmpJNode.AlphaMemory.Items) { right_activation(parent, item.WME); } } parent.Children.Clear(); parent.Children.AddRange(saved_list_of_children); break; case ReteNodeType.Negative: NegativeNode tmpNNode = (NegativeNode) parent; foreach (Token tok in tmpNNode.Items) { if (tok.JoinResults.Count == 0) { left_activation(newNode, tok, null); } } break; case ReteNodeType.NCC: NCCNode tmpNCCNode = (NCCNode) parent; foreach (Token tok in tmpNCCNode.Items) { if (tok.NCCResults.Count == 0) { left_activation(newNode, tok, null); } } break; } }
internal ReteLink(ReteNode source, ReteNode target) { Source = source; Target = target; }