Beispiel #1
0
    def pick_one(self):
        index = 0
        r = random()
        while r > 0:
            r -= self.paths[index].prob
            index += 1

        new_path = Path(self.paths[index - 1].nodes)
        new_path.normalize_score = self.paths[index - 1].normalize_score
        new_path.prob = self.paths[index - 1].prob

        return new_path
def testConvertDHCtoUHC():
    from uhc import uhc
    from dhc import dhc
    from graph import Path
    instances = [
        '',
        'a,a',
        'a,b',
        'a,b b,a',
        'a,b b,c c,a',
        'a,b b,c a,c',
        'a,b b,c c,d',
        'a,b b,c c,d d,a',
        'a,b b,c c,d a,d',
        'a,b b,c c,d a,d d,b c,a',
    ]
    for instance in instances:
        convertedInstance = convertDHCtoUHC(instance)
        instanceSolution = dhc(instance)
        convertedInstanceSolution = uhc(convertedInstance)
        revertedSolution = revertSolution(convertedInstanceSolution)

        utils.tprint(instance, 'maps to', convertedInstance,\
              ' solutions were: ', instanceSolution, ';', convertedInstanceSolution)
        utils.tprint('revertedSolution', revertedSolution)

        if revertedSolution == 'no':
            assert instanceSolution == 'no'
        else:
            g = Graph(instance, weighted=False)
            path = Path.fromString(revertedSolution)
            # print('g', g, 'path', path)
            assert g.isHamiltonCycle(path) or g.isHamiltonCycle(path.reverse())
Beispiel #3
0
def testTspDirK():
    testvals = [
        (';1', 'no'),
        ('a,a,3;1', 3),
        ('a,a,3 ; 1 ', 3),
        ('a,a,3;2', 'no'),
        ('a,b,4;1', 'no'),
        ('a,b,4;2', 'no'),
        ('a,b,4;3', 'no'),
        ('a,b,4 b,a,9;1', 13),
        ('a,b,4 b,a,9;2', 13),
        ('a,b,4 b,a,9;3', 'no'),
        ('a,b,4 b,a,9 b,c,6 c,a,10;2', 13),
        ('a,b,4 b,a,9 b,c,6 c,a,10;3', 20),
        ('a,b,4 b,a,9 b,c,6 c,a,1;2', 11),
        ('a,b,4 b,a,9 b,c,6 c,a,10;4', 'no'),
        ('a,b,4 b,a,9 b,c,6 c,a,10 a,c,2 c,b,3;3', 14),
        ('a,b,4 b,a,9 b,c,6 c,a,10 a,c,2 c,b,3;4', 'no'),
    ]
    for (inString, solution) in testvals:
        val = tspDirK(inString)
        utils.tprint(inString.strip(), ':', val)
        if solution == 'no':
            assert val == solution
        else:
            (graphString, K) = inString.split(';')
            graph = Graph(graphString, directed=True)
            K = int(K)
            cycle = Path.fromString(val)
            dist = graph.cycleLength(cycle)
            assert graph.isCycle(cycle)
            assert len(cycle) >= K
            assert dist == solution
Beispiel #4
0
    def choose_path_for_ant(self, pheromone_influence, heuristic_influence):
        chosen_edges = []
        node = self.graph.start
        while not (node == self.graph.end):
            possible_edges = self.graph.edges_adjacent_to_node[node][:]
            if (chosen_edges): possible_edges.remove(chosen_edges[-1])

            # Calculate the probabilities.
            # If another ant in current iteration has already been in this node, having come
            # through the same edge, then the probabilities are already calculated.
            if (chosen_edges 
                and node in self.edge_choice_probabilities 
                and chosen_edges[-1] in self.edge_choice_probabilities[node]
            ):
                probabilities = self.edge_choice_probabilities[node][chosen_edges[-1]]
                if (self.verbosity == 2):
                    for i in range (len(possible_edges)):
                        print (possible_edges[i], "[prob:", "{:.3f}".format(probabilities[i]), end="], ")
            else:
                sum = 0
                for edge in possible_edges:
                    sum += self.pheromone_array[edge]**pheromone_influence
                    sum += (1/self.graph.edges_lenght[edge])**heuristic_influence

                probabilities = []
                for edge in possible_edges:
                    probabilities.append((self.pheromone_array[edge]**pheromone_influence \
                                              + (1/self.graph.edges_lenght[edge])**heuristic_influence) \
                                             / sum)
                    if (self.verbosity == 2):
                        print (edge, "[prob:", "{:.3f}".format(probabilities[-1]), end="], ")
                
                # Store the result for future ants.
                if (chosen_edges): 
                    if (not node in self.edge_choice_probabilities): 
                        self.edge_choice_probabilities[node] = {}
                    self.edge_choice_probabilities[node][chosen_edges[-1]] = probabilities

            # Draw the next edge.    
            drawn_edge = random.choices(possible_edges, probabilities, k=1)[0]
            if (self.verbosity == 2):
                print("drawn edge: ", drawn_edge)
            chosen_edges.append(drawn_edge)
            if (node == drawn_edge[0]): 
                node = drawn_edge[1]
            else:
                assert (node == drawn_edge[1])
                node = drawn_edge[0]
        
        assert (chosen_edges)
        lenght = 0
        for edge in chosen_edges: lenght += self.graph.edges_lenght[edge]
        
        if (self.verbosity >= 1):
            if (self.verbosity == 2): print()
            print ("Chosen path: ", chosen_edges, "\nLenght: ", lenght, end="\n\n")
        return Path(chosen_edges, lenght)
Beispiel #5
0
def tspDir(inString):
    graph = Graph(inString)
    firstNode = graph.chooseNode()
    if firstNode == None:  # graph contains no vertices
        return ''  # this is a positive instance -- we return the empty cycle
    (cycle, distance) = shortestCycleWithPrefix(graph, Path([firstNode]), 0)
    if cycle != None:
        return str(cycle)
    else:
        return 'no'
Beispiel #6
0
def find_best_route(graph, start, end):
    """
	Find the best route through the grid between the given start node and the given 
	end node.
	"""
    if start == end:
        best_route_found = True
    else:
        best_route_found = False
        graph.nodes[start].distance = 0
        reachable_unprocessed_nodes = set([graph.nodes[start]])

    while not best_route_found:
        # 1. loop over the reachable but unprocessed nodes. Find the one with the
        #    least distance from the start (X).
        # 2. For each node Y connected to X, take d(start,X) + d(X,Y) and see if it
        #    is less than d(start, Y). If so, update node Y with a new distance and
        #    a new previous node (X).
        # 3. If the end has been reached, iterate over all items in the reachable
        #    but unprocessed nodes. If d(start,end) <= d(start,node) for all of these
        #    nodes, then we have found the best route.

        # Find the unprocessed node closest to the start
        closest_distance = None
        for node in reachable_unprocessed_nodes:
            if closest_distance is None or node.distance < closest_distance:
                closest_node = node
                closest_distance = node.distance

        assert closest_node.processed == False,                               \
               "Processed node is in list of unprocessed nodes"

        if closest_node.node_id == end:
            best_route_found = True

        # Process this node
        if not best_route_found:
            reachable_unprocessed_nodes.remove(closest_node)
            closest_node.processed = True
            for (node_id, distance) in closest_node.connections:
                node = graph.nodes[node_id]
                if node.distance is None or closest_node.distance + distance < node.distance:
                    node.distance = closest_node.distance + distance
                    reachable_unprocessed_nodes.add(node)
                    node.previous_node = closest_node

    # Create the path of the best route.
    current_node = graph.nodes[end]
    best_route = [current_node]
    if start != end:
        while current_node.previous_node is not None:
            current_node = current_node.previous_node
            best_route.insert(0, current_node)

    return Path(best_route)
def tspPath(inString):
    graphStr, source, dest = [x.strip() for x in inString.split(";")]
    graph = Graph(graphStr, weighted=True, directed=False)

    # If there are two vertices, our conversion won't work correctly,
    # so treat this as a special case.
    if len(graph) == 2:
        if graph.containsEdge(Edge([source, dest])):
            return str(Path([source, dest]))
        else:
            return 'no'

    # add an overwhelmingly negative edge from source to dest, thus
    # forcing that to be part of a shortest Ham cycle.
    sumOfWeights = graph.sumEdgeWeights()
    fakeEdge = Edge([source, dest])
    if graph.containsEdge(fakeEdge) == True:
        graph.removeEdge(fakeEdge)
    graph.addEdge(fakeEdge, -sumOfWeights)
    tspSoln = tsp(str(graph))
    # print('graph', graph)
    # print('tspSoln', tspSoln)
    if tspSoln == 'no':
        return 'no'

    # convert from string to Path object
    tspSoln = Path.fromString(tspSoln)

    rotatedSoln = tspSoln.rotateToFront(source)
    if rotatedSoln[-1] != dest:
        # Probably oriented the wrong way (or else fakeEdge wasn't
        # used -- see below). Reverse then rotate source to front
        # again
        rotatedSoln = rotatedSoln.reverse().rotateToFront(source)

    # If dest still isn't at the end of the cycle, then the orginal graph didn't
    # have a Ham path from source to dest (but it did have a Ham
    # cycle -- a cycle that did *not* include fakeEdge).
    if rotatedSoln[-1] != dest:
        return 'no'
    else:
        return str(rotatedSoln)
Beispiel #8
0
def testShortestCycleWithPrefix():
    graph = Graph('''
              a,b,5 b,a,3 b,c,6
              c,a,7 c,d,8 d,a,9 a,c,1 d,b,2
                      ''',
                  directed=True)
    path = Path(['a'])
    val = shortestCycleWithPrefix(graph, path, 0)
    cycle, dist = val
    utils.tprint(val)
    assert graph.isHamiltonCycle(cycle)
    assert dist == 14
def bruteForceSearch(digraph, start, end, maxTotalDist, maxDistOutdoors):
    """
    Finds the shortest path from start to end using brute-force approach.
    The total distance travelled on the path must not exceed maxTotalDist, and
    the distance spent outdoor on this path must not exceed maxDisOutdoors.

    Parameters:
        digraph: instance of class Digraph or its subclass
        start, end: start & end building numbers (strings)
        maxTotalDist : maximum total distance on a path (integer)
        maxDistOutdoors: maximum distance spent outdoors on a path (integer)

    Assumes:
        start and end are numbers for existing buildings in graph

    Returns:
        The shortest-path from start to end, represented by
        a list of building numbers (in strings), [n_1, n_2, ..., n_k],
        where there exists an edge from n_i to n_(i+1) in digraph,
        for all 1 <= i < k.

        If there exists no path that satisfies maxTotalDist and
        maxDistOutdoors constraints, then raises a ValueError.
    """
    stack = [Path([SmartNode(start)], 0, 0)]
    valid_paths = []

    while stack:
        path = stack.pop(-1)

        for edge in digraph.childrenOf(path.get_current_position()):
            if not path.is_node_visited(edge.getDestination()):
                new_path = path.clone()
                new_path.add_edge(edge)
                # "...consider first finding all valid paths that satisfy
                # the max distance outdoors constraint,..."
                if new_path.distance_outdoors <= maxDistOutdoors:
                    if new_path.is_end_reached(end):
                        valid_paths.append(new_path)
                    else:
                        stack.append(new_path)
    # "... and then going through those paths and returning the shortest,
    # rather than trying to fulfill both constraints at once..."
    valid_paths = filter(lambda path: path.total_distance <= maxTotalDist,
                         valid_paths)
    # min will raise a ValueError if an empty sequence is passed to it
    shortest = min(valid_paths, key=lambda x: x.total_distance)
    return shortest.get_node_list()
Beispiel #10
0
def verifyTspD(I, S, H):
    if S == 'no': return 'unsure'
    # extract G,L from I, and convert to correct data types etc.
    (G, L) = I.split(';')
    G = Graph(G, directed=False)
    L = int(L)

    # split the hint string into a list of vertices, which will
    # form a Hamilton cycle of length at most L, if the hint is correct
    cycle = Path.fromString(H)

    # verify the hint is a Hamilton cycle, and has length at most L
    if G.isHamiltonCycle(cycle) and \
               G.cycleLength(cycle) <= L:
        return 'correct'
    else:
        return 'unsure'
Beispiel #11
0
    def _pathN2G(self, neopath):
        path = Path()
        n0 = self._nodeN2G(neopath.start_node)

        for rel in neopath:
            relation = self._relN2G(rel)
            path.relations.append(relation)

        for n in neopath.nodes:
            path.nodes.append(self._nodeN2G(n))

        for i in range(len(path.nodes)):
            path.elements.append(path.nodes[i])
            if path.relations and i < len(path.relations):
                path.elements.append(path.relations[i])

        return path
Beispiel #12
0
def testTsp():
    testvals = [
        ('a,b,5', 'no', None),
        ('a,b,5 b,c,6 c,d,8 d,a,9 a,c,1 d,b,2', 'a,b,d,c', 16),
        ('a,b,5 b,c,6 c,d,8 d,a,9 a,c,1 d,b,200', 'a,b,c,d', 28),
        ('a,b,5 b,c,6 d,a,9 a,c,1', 'no', None),
    ]
    for (inString, solution, length) in testvals:
        val = tsp(inString)
        utils.tprint(inString, ':', val)
        if solution == 'no':
            assert val == solution
        else:
            g = Graph(inString, weighted=True, directed=False)
            p = Path.fromString(val)
            assert g.isHamiltonCycle(p)
            assert g.cycleLength(p) == length
Beispiel #13
0
def tspDirK(inString):
    (graphString, K) = inString.split(';')
    K = int(K)
    # treat K=0 as a special case. Since the empty cycle satisfies
    # this, we return a positive solution which is the empty string.
    if K==0:
        return ''

    graph = Graph(graphString)
    bestCycle = None; bestDistance = None
    for firstNode in graph:
        (cycle, distance) = shortestKCycleWithPrefix(graph, K, Path([firstNode]), 0)
        if cycle != None:
            if bestDistance == None or distance < bestDistance:
                (bestCycle, bestDistance) = (cycle, distance)
    if bestCycle != None:
        return str(bestCycle)
    else:
        return 'no'
Beispiel #14
0
def testUhc():
    testVals = [
        ('', True),
        ('a,b', False),
        ('a,b b,c', False),
        ('a,b b,c c,a', True),
        ('a,b b,c c,a a,d', False),
        ('a,b b,c c,a a,d b,d', True),
        ('aB,aA aB,aC aA,aC', True),
    ]
    for (inString, hasUhc) in testVals:
        result = uhc(inString)
        utils.tprint(inString, ':', result)
        if not hasUhc:
            assert result == 'no'
        else:
            g = Graph(inString, weighted=False, directed=False)
            path = Path.fromString(result)
            assert g.isHamiltonCycle(path)
def directedDFS(digraph, start, end, maxTotalDist, maxDistOutdoors):
    """
    Finds the shortest path from start to end using directed depth-first.
    search approach. The total distance travelled on the path must not
    exceed maxTotalDist, and the distance spent outdoor on this path must
    not exceed maxDisOutdoors.

    Parameters:
        digraph: instance of class Digraph or its subclass
        start, end: start & end building numbers (strings)
        maxTotalDist : maximum total distance on a path (integer)
        maxDistOutdoors: maximum distance spent outdoors on a path (integer)

    Assumes:
        start and end are numbers for existing buildings in graph

    Returns:
        The shortest-path from start to end, represented by
        a list of building numbers (in strings), [n_1, n_2, ..., n_k],
        where there exists an edge from n_i to n_(i+1) in digraph,
        for all 1 <= i < k.

        If there exists no path that satisfies maxTotalDist and
        maxDistOutdoors constraints, then raises a ValueError.
    """
    stack = [Path([SmartNode(start)], 0, 0)]

    while stack:
        path = stack.pop(-1)

        for edge in digraph.childrenOf(path.get_current_position()):
            if not path.is_node_visited(edge.getDestination()):
                new_path = path.clone()
                new_path.add_edge(edge)
                if (new_path.distance_outdoors <= maxDistOutdoors
                        and new_path.total_distance <= maxTotalDist):
                    if new_path.is_end_reached(end):
                        return new_path.get_node_list()
                    else:
                        stack.append(new_path)

    raise ValueError()
Beispiel #16
0
def testTspPath():
    testvals = [
        ('a,b,5;a;b', 5),
        ('a,b,5 b,c,6 c,d,8 d,a,9 a,c,1 d,b,2;a;b', 11),
        ('a,b,5 b,c,6 c,d,8 d,a,9 a,c,1 d,b,2 c,e,10 d,e,20;a;b', 33),
        ('a,b,5 b,c,6 d,a,9 a,c,1 d,b,2;a;b', 'no'),
        ('a,b,5 b,c,6 d,a,9 a,c,1;a;b', 'no'),
    ]
    for (inString, solution) in testvals:
        val = tspPath(inString)
        utils.tprint(inString.strip(), ':', val)
        if solution == 'no':
            assert val == solution
        else:
            graphStr, source, dest = [x.strip() for x in inString.split(";")]
            graph = Graph(graphStr, directed=False)
            path = Path.fromString(val)
            dist = graph.pathLength(path)
            assert graph.isHamiltonPath(path)
            assert dist == solution
Beispiel #17
0
    def perform_algorithm(self, iterations, population_size, evaporation_factor, 
                          pheromone_influence, heuristic_influence):
        self.initialize_pheromone_array()
        self.edge_choice_probabilities = {}
        shortest_path = Path([], float("inf"))

        for i in range(iterations):
            if (self.verbosity >= 1):
                print ("=== Iteration ", i, end="\n\n")
            self.edge_choice_probabilities.clear()
            for j in range(population_size):
                if (self.verbosity >= 1):
                    print ("Ant ", j)
                path = self.choose_path_for_ant(pheromone_influence, heuristic_influence)
                self.drop_pheromone_on_path(path)
                if (path.lenght < shortest_path.lenght):
                    shortest_path = path
            self.update_pheromone_array(evaporation_factor, shortest_path)
            if (i % self.state_displaying_period == 0):
                self.printer.display_state(self.pheromone_array, i)
        self.printer.display_state(self.pheromone_array, iterations)
def verifyTspDPolytime(I, S, H):
    # reject excessively long solutions and hints
    if len(S) > len(I) or len(H) > len(I):
        return 'unsure'
    # The remainder of the program is identical to verifyTspD.py
    # ...
    if S == 'no': return 'unsure'
    # extract G,L from I, and convert to correct data types etc.
    (G, L) = I.split(';')
    G = Graph(G, directed=False)
    L = int(L)

    # split the hint string into a list of vertices, which will
    # form a Hamilton cycle of length at most L, if the hint is correct
    cycle = Path.fromString(H)

    # verify the hint is a Hamilton cycle, and has length at most L
    if G.isHamiltonCycle(cycle) and \
               G.cycleLength(cycle) <= L:
        return 'correct'
    else:
        return 'unsure'
Beispiel #19
0
def testTspDir():
    testvals = [
        ('aC,aB,1 aA,aB,1 aC,aA,1 aB,aA,1 aB,aC,1 aA,aC,1', 3),
        ('', 0),
        ('a,a,3', 3),
        ('a,b,4', 'no'),
        ('a,b,4 b,a,9', 13),
        ('a,b,4 b,a,9 b,c,6 c,a,10', 20),
        ('a,b,4 b,a,9 b,c,6 c,a,10 a,c,2 c,b,3', 14),
        ('a,b,4 b,a,9 b,c,6 c,a,10 a,c,2 c,b,300', 20),
        ('a,b,4 b,a,9 b,c,6 c,a,10 a,c,2 c,b,3 c,d,1', 'no'),
    ]
    for (inString, solution) in testvals:
        val = tspDir(inString)
        utils.tprint(inString.strip(), ':', val)
        if solution == 'no':
            assert val == solution
        else:
            graph = Graph(inString, directed=True)
            cycle = Path.fromString(val)
            dist = graph.cycleLength(cycle)
            assert graph.isHamiltonCycle(cycle)
            assert dist == solution
Beispiel #20
0
def testHalfUhc():
    testvals = [
        ('', 'yes'),
        ('a,b', 'no'),
        ('a,a', 'yes'),
        ('a,b b,c', 'no'),
        ('a,b b,c c,a', 'yes'),
        ('a,b b,c c,a d,e d,f', 'yes'),
        ('a,b b,c c,a d,e f,g', 'no'),
        ('a,b b,c c,a a,d', 'yes'),
        ('a,b b,c c,a a,d b,d', 'yes'),
        ('a,b b,c c,d d,e e,f f,g g,h h,i i,j h,a', 'yes'),
        ('a,b b,c c,d d,e e,f f,g g,h h,i i,j d,a', 'no'),
    ]
    for (inString, solution) in testvals:
        val = halfUhc(inString)
        utils.tprint(inString, ':', val)
        if solution == 'no':
            assert val == solution
        else:
            g = Graph(inString, weighted=False, directed=False)
            path = Path.fromString(val)
            assert g.isCycle(path)
            assert g.cycleLength(path) >= len(g) / 2
Beispiel #21
0
 def generate_initial_population(self):
     for i in range(self.popsize):
         new_path = Path(self.graph.get_random_path(self.start, self.end))
         self.paths.append(new_path)