def retrace_negative_loop(predecessor, start): arbitrage_loop = [start] next_node = start while True: next_node = predecessor[next_node] if next_node not in arbitrage_loop: arbitrage_loop.insert(0, next_node) else: arbitrage_loop.insert(0, next_node) arbitrage_loop = arbitrage_loop[:last_index_in_list( arbitrage_loop, next_node) + 1] return arbitrage_loop
def _retrace_negative_loop(self, start): """ In development. :return: negative loop path """ arbitrage_loop = [start] next_node = start # todo: could refactor to make the while statement `while next_node not in arbitrage_loop` while True: next_node = self.predecessor[next_node].soft_pop() if next_node not in arbitrage_loop: arbitrage_loop.insert(0, next_node) # else, loop is finished. else: arbitrage_loop.insert(0, next_node) arbitrage_loop = arbitrage_loop[:last_index_in_list( arbitrage_loop, next_node) + 1] return arbitrage_loop
def _retrace_negative_loop(self, start, loop_from_source=False, source='', ensure_profit=False): """ @:param loop_from_source: look at docstring of bellman_ford :return: negative loop path """ arbitrage_loop = [start] # todo: could refactor to make the while statement `while next_node not in arbitrage_loop` if not loop_from_source: next_node = start while True: next_node = self.predecessor_to[next_node].pop()[1] # if negative cycle is complete if next_node in arbitrage_loop: arbitrage_loop = arbitrage_loop[:last_index_in_list( arbitrage_loop, next_node) + 1] arbitrage_loop.insert(0, next_node) self.reset_predecessor_iteration() return arbitrage_loop arbitrage_loop.insert(0, next_node) else: if source not in self.graph: raise ValueError("source not in graph.") next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1] arbitrage_loop.insert(0, next_node) # todo: refactor this so it is not while True, instead while not next_to_each_other while True: next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1] # if this edge has been traversed over, negative cycle is complete. if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]): arbitrage_loop.insert(0, next_node) arbitrage_loop = arbitrage_loop[:last_index_in_list( arbitrage_loop, next_node) + 1] if ensure_profit: # the weight of the path that will be taken to make arbitrage_loop start and end at source return_path_weight = self.distance_to[arbitrage_loop[ 0]] + self.distance_from[arbitrage_loop[-1]] loop_weight = 0 if return_path_weight > 0: # todo: this is not the most efficient way to get the weight of arbitrage_loop for i in range(len(arbitrage_loop) - 1): loop_weight += self.graph[arbitrage_loop[i]][ arbitrage_loop[i + 1]]['weight'] scalar = return_path_weight / abs(loop_weight) + 1 if scalar.is_integer(): scalar += 1 else: scalar = math.ceil(scalar) arbitrage_loop *= scalar self.predecessor_to[arbitrage_loop[0]].pop() def _pop_arbitrage_loop(loop, predecessor): while predecessor[loop[0]].empty: loop.pop(0) # add the path from source -> min_distance_to_node to the beginning of arbitrage_loop while arbitrage_loop[0] != source: _pop_arbitrage_loop(arbitrage_loop, self.predecessor_to) next_node = self.predecessor_to[ arbitrage_loop[0]].pop()[1] # if this edge has already been traversed over/ added to arbitrage_loop, must exit the cycle. if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]): self.predecessor_to[arbitrage_loop[0]].pop() # this prevents an error where every edge from a node has been traversed over. # todo: how could we (efficiently) find the last closed loop? it would be best to pop from # its predecessors. _pop_arbitrage_loop(arbitrage_loop, self.predecessor_to) next_node = self.predecessor_to[ arbitrage_loop[0]].pop()[1] arbitrage_loop.insert(0, next_node) # add the path from arbitrage_loop[-1] -> source to the end of arbitrage_loop while arbitrage_loop[-1] != source: next_node = self.predecessor_from[ arbitrage_loop[-1]].peek()[1] if next_to_each_other(arbitrage_loop, arbitrage_loop[-1], next_node): self.predecessor_from[arbitrage_loop[-1]].pop() # next_node equals the second least predecessor_to of arbitrage_loop[-1] so as to not reenter a # negative cycle # try: # next_node = self.predecessor_from[arbitrage_loop[-1]].pop()[1] arbitrage_loop.append(next_node) self.reset_predecessor_iteration() return arbitrage_loop else: arbitrage_loop.insert(0, next_node)
def _retrace_negative_loop(self, start, loop_from_source=False, source='', ensure_profit=False, unique_paths=False): """ @:param loop_from_source: look at docstring of bellman_ford :return: negative loop path """ if unique_paths and start in self.seen_nodes: raise SeenNodeError arbitrage_loop = [start] # todo: could refactor to make the while statement `while next_node not in arbitrage_loop` if not loop_from_source: next_node = start while True: next_node = self.predecessor_to[next_node].pop()[1] # if negative cycle is complete if next_node in arbitrage_loop: arbitrage_loop = arbitrage_loop[:last_index_in_list(arbitrage_loop, next_node) + 1] arbitrage_loop.insert(0, next_node) self.reset_predecessor_iteration() return arbitrage_loop # if next_node in arbitrage_loop, next_node in self.seen_nodes. thus, this conditional must proceed # checking if next_node in arbitrage_loop if unique_paths and next_node in self.seen_nodes: raise SeenNodeError(next_node) arbitrage_loop.insert(0, next_node) self.seen_nodes.add(next_node) else: if source not in self.graph: raise ValueError("source not in graph.") # todo: i do not remember to which edge case this refers, test to see which then specify in the comment. # adding the predecessor to start to arbitrage loop outside the while loop prevents an edge case. next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1] if unique_paths and next_node in self.seen_nodes: raise SeenNodeError(next_node) arbitrage_loop.insert(0, next_node) # todo: refactor this so it is not while True, instead while not next_to_each_other while True: next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1] # if this edge has been traversed over, negative cycle is complete. if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]): arbitrage_loop.insert(0, next_node) arbitrage_loop = arbitrage_loop[:last_index_in_list(arbitrage_loop, next_node) + 1] if ensure_profit: # todo: is this inefficient because it iterates over arbitrage_loop twice? once to check if in, # once to get index? if source in arbitrage_loop: index = arbitrage_loop.index(source) arbitrage_loop = arbitrage_loop[index:] + arbitrage_loop[:index] # the weight of the path that will be taken to make arbitrage_loop start and end at source return_path_weight = self.distance_to[arbitrage_loop[0]] + self.distance_from[arbitrage_loop[-1]] loop_weight = 0 if return_path_weight > 0: # todo: this is not the most efficient way to get the weight of arbitrage_loop for i in range(len(arbitrage_loop) - 1): loop_weight += self.graph[arbitrage_loop[i]][arbitrage_loop[i + 1]]['weight'] scalar = return_path_weight / abs(loop_weight) + 1 if scalar.is_integer(): scalar += 1 else: scalar = math.ceil(scalar) arbitrage_loop *= scalar self.predecessor_to[arbitrage_loop[0]].pop() def _pop_arbitrage_loop(loop, predecessor): while predecessor[loop[0]].empty: loop.pop(0) # add the path from source -> min_distance_to_node to the beginning of arbitrage_loop while arbitrage_loop[0] != source: _pop_arbitrage_loop(arbitrage_loop, self.predecessor_to) next_node = self.predecessor_to[arbitrage_loop[0]].pop()[1] # if this edge has already been traversed over/ added to arbitrage_loop, must exit the cycle. if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]): self.predecessor_to[arbitrage_loop[0]].pop() # this prevents an error where every edge from a node has been traversed over. _pop_arbitrage_loop(arbitrage_loop, self.predecessor_to) next_node = self.predecessor_to[arbitrage_loop[0]].pop()[1] arbitrage_loop.insert(0, next_node) # add the path from arbitrage_loop[-1] -> source to the end of arbitrage_loop while arbitrage_loop[-1] != source: next_node = self.predecessor_from[arbitrage_loop[-1]].peek()[1] if next_to_each_other(arbitrage_loop, arbitrage_loop[-1], next_node): self.predecessor_from[arbitrage_loop[-1]].pop() arbitrage_loop.append(next_node) self.reset_predecessor_iteration() return arbitrage_loop else: if unique_paths and next_node in self.seen_nodes: raise SeenNodeError(next_node) arbitrage_loop.insert(0, next_node) self.seen_nodes.add(next_node)