Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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)