def __lv2ibt(segment): stack = [] has_crit_b = False if segment.empty(): raise EmptyError( "An empty Segment cannot be transformed into a BTree") for i in range(segment.length() - 1, -1, -1): v = segment[i] if v.is_leaf(): stack.append(Leaf(v)) elif v.is_critical(): stack.append(Leaf(v)) if has_crit_b: raise IllFormedError( "A ill-formed Segment cannot be transformed into a BTree" ) else: has_crit_b = True else: if len(stack) < 2: raise IllFormedError( "A ill-formed Segment cannot be transformed into a BTree" ) lv = stack.pop() rv = stack.pop() stack.append(Node(v, lv, rv)) if len(stack) != 1: raise IllFormedError( "A ill-formed Segment cannot be transformed into a BTree") else: return has_crit_b, stack[0]
def __node_uacc_local_compute(stack, d, phi, v, psi_l, res, psi_r, i, k): """Computes when the value is a node""" if len(stack) < 2: raise IllFormedError( "uacc_local cannot be applied if there is a node that does not have two children " "in the current instance") # We get the values of two sub upward accumulation lv = stack.pop() rv = stack.pop() if d == 0: # The current node is an ancestor of a critical value by the left # That is, there is a critical value on its left children in a BTree representation # We process and stack the value of a partial accumulation val = phi(v.get_value()) stack.append(psi_l(lv, val, rv)) res[i] = None elif d == 1: # The current node is an ancestor of a critical value by the left # That is, there is a critical value on its left children in a BTree representation # We process and stack the value of a partial accumulation val = phi(v.get_value()) stack.append(psi_r(lv, val, rv)) res[i] = None d = 0 else: # We did not meet a critical value, we can process a normal upward accumulation with k val = k(lv, v.get_value(), rv) res[i] = TaggedValue(val, v.get_tag()) stack.append(val) d = d - 1 return d, stack, res
def dacc_local(self, gl, gr, c): """Computes local downward accumulation for the current instance using an accumulative parameter resulting of a global downward accumulation Parameters ---------- gl : callable Function to make a downward accumulation to the left gr : callable Function to make a downward accumulation to the right c Initial value of the accumulator Raises ------ IllFormedError: If the current instance does not represent a correct linearized subtree That is there are several leaves that doesn't have a parent in a BTree representation or if there is not a value to accumulate from above """ # We update not finished accumulation locally using the value from the parent in the global # representation of a linearized tree stack = [c] res = Segment([None] * self.length()) for i in range(self.length()): v = self[i] if v.is_leaf() or v.is_critical(): if len(stack) == 0: raise IllFormedError( "dacc_local cannot be applied if there are two leaf values, or critical " "values that do not have a parent") # We get the accumulated value passed from the last parent val = stack.pop() res[i] = TaggedValue(val, v.get_tag()) else: # v.is_node() if len(stack) == 0: raise IllFormedError( "dacc_local cannot be applied if there is not a value to accumulate from " "above") val = stack.pop() # We get the accumulated value passed from the last parent # And two new ones, one for the left children, and one to the right, using # the gr and gl functions res[i] = TaggedValue(val, v.get_tag()) stack.append(gr(val, v.get_value())) stack.append(gl(val, v.get_value())) return res
def uacc_update(self, seg2, k, lc, rc): """Makes an update of the current accumulation, using initial values and the top accumulated values Precondition ------------- The lengths of self and seg2 should be equal Parameters ---------- seg2 : :obj:`Segment` Result of a local accumulation k : callable The function used to reduce a BTree into a single value lc Top value of the left children in a global structure rc Top value of the left children in a global structure Raises ------ IllFormedError If the current instance does not represent a correct linearized subtree That is there is a node that doesn't have two children """ assert self.length() == seg2.length(), "uacc_update cannot needs to " \ "Segment of same size as input" stack = [rc, lc] d = MINUS_INFINITY res = Segment([None] * self.length()) for i in reversed(range(seg2.length())): v1 = self[i] v2 = seg2[i] # We update the accumulation from seg2 # We stack the values already updated to process updates on nodes if v1.is_leaf(): # The result of the accumulation is the node made in seg2 res[i] = v2 stack.append(v2.get_value()) d = d + 1 elif v1.is_node(): res, stack, d = self.__node_uacc_update_compute( stack, d, res, k, v1, v2, i) else: # v1.is_critical() if len(stack) < 2: raise IllFormedError( "uacc_update cannot be applied if there is a node that " "does not have two children in the current instance") # We need two sub accumulation values to process the accumulation of a critical node lv = stack.pop() rv = stack.pop() val = k(lv, v1.get_value(), rv) res[i] = TaggedValue(val, v1.get_tag()) stack.append(val) d = 0 return res
def __rev_segment_to_trees(lb, glob): stack = [] for i in range(lb.length() - 1, -1, -1): if glob[i] == TAG_LEAF: stack.append(lb[i]) else: # gt[i] == VTag_Node lbt = stack.pop() rbt = stack.pop() stack.append(__graft(lb[i], lbt, rbt)) if len(stack) != 1: raise IllFormedError( "A ill-formed list of incomplete BTree cannot be transformed into a BTree" ) return stack[0]
def dacc_global(self, psi_d, c): """Performs sequential downwards accumulation Precondition ------------- self should bot have critical nodes Parameters ---------- psi_d : callable A function used to respect the closure property on gr and gl (initial functions used for up accumulation) to make partial downward accumulation c Initial value of the accumulator Raises ------ IllFormedError If the current instance does not represent a correct linearized subtree That is there are several leaves that doesn't have a parent in a BTree representation """ stack = [c] res = Segment([None] * self.length()) assert not self.has_critical( ), "dacc_global cannot be applied to Segment which contains a critical node" for i in range(self.length()): v = self[i] if len(stack) == 0: raise IllFormedError( "dacc_global cannot be applied to ill-formed Segments that is two leaf values do not have a parent" ) # We add the previous accumulation as a new value of our result val = stack.pop() res[i] = TaggedValue(val, v.get_tag()) # If the current value is node, we need to update the value to pass to the right, and left children # These values are contained in the stack if v.is_node(): (to_l, to_r) = v.get_value() stack.append(psi_d(val, to_r)) stack.append(psi_d(val, to_l)) return res
def uacc_global(self, psi_n): """Performs sequential upwards accumulation Precondition ------------- self should not have critical nodes Parameters ---------- psi_n : callable A function used to respect the closure property on k (the initial function used for accumulation) to allow partial computation Raises ------ IllformedError If the current instance does not represent a correct linearized subtree That is there is a node that doesn't have two children """ assert not self.has_critical( ), "uacc_global cannot be applied to a Segments which contains a critical" stack = [] res = Segment([None] * self.length()) for i in reversed(range(self.length())): g = self[i] # We process a global accumulation using a stack to store previous accumulation, # to get them for the accumulation on nodes if g.is_leaf(): res[i] = g val = g.get_value() else: # g.is_node() if len(stack) < 2: raise IllFormedError( "uacc_global cannot be applied if there is a node that does not have two children " "in the current instance") lv = stack.pop() rv = stack.pop() val = psi_n(lv, g.get_value(), rv) res[i] = TaggedValue(val, g.get_tag()) stack.append(val) # We get the top value of the accumulation return res
def reduce_global(self, psi_n): """Makes a global reduction using local reductions of Segments Precondition ------------- self should empty, and should not have critical nodes Parameters ---------- psi_n : callable A function used to respect the closure property on k (the initial function used for reduction) to allow partial computation Raises ------ IllFormedError If the current instance does not represent a list of top values of a correct list of subtrees That is for each node, there is not exist two children, of there is a critical value in the current instance """ assert not self.has_critical(), "reduce_global cannot be applied to a" \ "Segments which contains a critical" assert self != [], "reduce_global cannot be applied to an empty Segment" stack = [] for g in reversed(self): # We stack every value we already reduced if g.is_leaf(): # Nothing to calculate, we only stack the value stack.append(g.get_value()) else: # g.is_node() # We get two sub reductions to make a total reduction of the current node if len(stack) < 2: raise IllFormedError( "reduce_global cannot be applied if there is a node that" "does not have two children in the current instance") lv = stack.pop() rv = stack.pop() # We process and stack a reduction stack.append(psi_n(lv, g.get_value(), rv)) top = stack.pop() return top
def __node_uacc_update_compute(stack, d, res, k, v1, v2, i): """Computes when the value is a node""" if len(stack) < 2: raise IllFormedError( "uacc_update cannot be applied if there is a node that does not have two children " "in the current instance") # We need two sub accumulation values to process the accumulation of a node lv = stack.pop() rv = stack.pop() if d in (0, 1): # We met a critical value before, so the accumulation is not completed yet val = k(lv, v1.get_value(), rv) res[i] = TaggedValue(val, v1.get_tag()) stack.append(val) d = 0 else: # We did not meet a critical value before, so the accumulation is completed yet res[i] = v2 stack.append(v2.get_value()) d = d - 1 return res, stack, d
def __node_reduce_local_compute(stack, d, k, psi_l, phi, v, psi_r): """Computes when the value is a node""" if len(stack) < 2: raise IllFormedError( "reduce_local cannot be applied if there is a node that does not have " "two children in the current instance") # We get two sub-reductions to make a reduction with the current node value lv = stack.pop() rv = stack.pop() if d == 0: # The current node is an ancestor of a critical value by the left # That is, there is a critical value on its left children in a BTree representation # We process and stack a partial reduction stack.append(psi_l(lv, phi(v.get_value()), rv)) elif d == 1: # The current node is an ancestor of a critical value by the right # That is, there is a critical value on its right children in a BTree representation # We process and stack a partial reduction stack.append(psi_r(lv, phi(v.get_value()), rv)) d = 0 else: # We did not meet a critical value, we process and stack a normal reduction stack.append(k(lv, v.get_value(), rv)) return stack, d
def uacc_local(self, k, phi, psi_l, psi_r): """Computes local upwards accumulation and reduction Precondition ------------- self should empty Parameters ---------- k : callable The function used to reduce a BTree into a single value phi : callable A function used to respect the closure property psi_l : callable A function used to respect the closure property to make partial computation on the left psi_r : callable A function used to respect the closure property to make partial computation on the right Raises ------ IllFormedError If the current instance does not represent a correct linearized subtree That is there is a node that doesn't have two children which can be either a leaf value or a critical value """ assert self != [], "uacc_local cannot be applied to an empty Segment" stack = [] d = MINUS_INFINITY res = Segment([None] * self.length()) has_crit = False for i in reversed(range(self.length())): v = self[i] # We stack all the values of previous accumulation if v.is_leaf(): res[i] = v stack.append(v.get_value()) d = d + 1 elif v.is_node(): if len(stack) < 2: raise IllFormedError( "uacc_local cannot be applied if there is a node that does not have two children " "in the current instance") # We get the values of two sub upward accumulation lv = stack.pop() rv = stack.pop() if d == 0: # The current node is an ancestor of a critical value by the left # That is, there is a critical value on its left children in a BTree representation # We process and stack the value of a partial accumulation val = phi(v.get_value()) stack.append(psi_l(lv, val, rv)) res[i] = None elif d == 1: # The current node is an ancestor of a critical value by the left # That is, there is a critical value on its left children in a BTree representation # We process and stack the value of a partial accumulation val = phi(v.get_value()) stack.append(psi_r(lv, val, rv)) res[i] = None d = 0 else: # We did not meet a critical value, we can process a normal upward accumulation with k val = k(lv, v.get_value(), rv) res[i] = TaggedValue(val, v.get_tag()) stack.append(val) d = d - 1 else: # v.is_critical() # The current value is critical. We make a partial accumulation with phi and stack the result stack.append(phi(v.get_value())) res[i] = None d = 0 has_crit = True top = stack.pop() tag = "N" if has_crit else "L" # We return both the top values for following global upward accumulation, and the current accumulated subtree return TaggedValue(top, tag), res
def reduce_local(self, k, phi, psi_l, psi_r): """Reduces a local Segment into a value Precondition ------------- self should not be empty Parameters ---------- k : callable The function used to reduce a BTree into a single value phi : callable A function used to respect the closure property psi_l : callable A function used to respect the closure property to make partial computation on the left psi_r : callable A function used to respect the closure property to make partial computation on the right Raises ------ IllFormedError If the current instance does not represent a correct linearized subtree That is there is a node that does not have two children which can be either a leaf value or a critical value """ assert self != [], "reduce_local cannot be applied to an empty Segment" stack = [] d = MINUS_INFINITY has_critical = False for v in reversed(self): # Starts by the end, that is the most deep leaves # We stack every elements we already reduced if v.is_leaf(): # We cannot reduce a leaf value stack.append(v.get_value()) d = d + 1 elif v.is_node(): if len(stack) < 2: raise IllFormedError( "reduce_local cannot be applied if there is a node that does not have" "two children in the current instance") # We get two sub-reductions to make a reduction with the current node value lv = stack.pop() rv = stack.pop() if d == 0: # The current node is an ancestor of a critical value by the left # That is, there is a critical value on its left children in a BTree representation # We process and stack a partial reduction stack.append(psi_l(lv, phi(v.get_value()), rv)) elif d == 1: # The current node is an ancestor of a critical value by the right # That is, there is a critical value on its right children in a BTree representation # We process and stack a partial reduction stack.append(psi_r(lv, phi(v.get_value()), rv)) d = 0 else: # We did not meet a critical value, we process and stack a normal reduction stack.append(k(lv, v.get_value(), rv)) else: # v.is_critical() # we process and stack the reduction of critical value stack.append(phi(v.get_value())) has_critical = True d = 0 top = stack.pop() if has_critical: # The current instance represented a node in the global structure of a linearized tree return TaggedValue(top, "N") else: # The current instance represented a leaf in the global structure of a linearized tree return TaggedValue(top, "L")