Exemplo n.º 1
0
def policy_buchi_pa(pa, weight_label='weight'):
    '''Computes the policy.'''
    if not pa.final:
        return float('Inf'), None

    vinit = generate_unique_node()
    pa.g.add_node(vinit)
    pa.g.add_edges_from([(vinit, init, {weight_label: 0}) for init in pa.init])

    prefix_costs, prefix_paths = nx.single_source_dijkstra(pa.g,
                                                           source=vinit,
                                                           weight=weight_label)
    pa.g.remove_node(vinit)

    opt_cost, opt_suffix_path = float('Inf'), None
    for final in pa.final:
        if final in prefix_costs:
            suffix_cost, suffix_path = source_to_target_dijkstra(
                pa.g,
                source=final,
                target=final,
                degen_paths=False,
                weight_key=weight_label)
            if prefix_costs[final] + suffix_cost < opt_cost:
                opt_cost = prefix_costs[final] + suffix_cost
                opt_suffix_path = suffix_path

    if opt_suffix_path is None:
        return float('Inf'), None

    opt_final = opt_suffix_path[0]
    return (opt_cost, [u[0] for u in prefix_paths[opt_final][1:]],
            [u[0] for u in opt_suffix_path])
Exemplo n.º 2
0
    def clean_final_states(self):
        # By construction all final states are reachable from
        # the initial states of global_pa

        # Find and remove the final states that cannot reach themselves
        unreaching_finals = set()
        unreaching_finals.update(self.global_pa.final)
        for final in self.global_pa.final:
            dist, _ = source_to_target_dijkstra(self.global_pa.g,
                                                final,
                                                final,
                                                degen_paths=False,
                                                weight_key='weight')
            if dist == float('inf'):
                # final cannot reach itself
                continue
            # final can reach itself
            unreaching_finals -= set([final])
        self.global_pa.final -= unreaching_finals
        # Remove unreaching finals from the set of initial states
        for state in unreaching_finals:
            if state in self.global_pa.init:
                del self.global_pa.init[state]
        # Finally, actually remove these states from the graph
        # (Can also remove states w/ fd(q) = \infty)
        self.global_pa.g.remove_nodes_from(unreaching_finals)
Exemplo n.º 3
0
def empty_language(p):
	# Not checking reachability from initial states
	# as such finals are not included by construction
	for final in p.final:
		dist, _ = source_to_target_dijkstra(p.g, final, final, degen_paths=False, weight_key='weight')
		if dist != float('inf'):
				# final can reach itself
				return False
	return True
Exemplo n.º 4
0
def empty_language(p):
	# Not checking reachability from initial states
	# as such finals are not included by construction
	for final in p.final:
		dist, _ = source_to_target_dijkstra(p.g, final, final, degen_paths=False, weight_key='weight')
		if dist != float('inf'):
				# final can reach itself
				return False
	return True
Exemplo n.º 5
0
def optimal_run(t, formula, opt_prop):
    try:
        logger.info('T has %d states', len(t.g))
        # Convert formula to Buchi automaton
        b = Buchi()
        b.from_formula(formula)
        logger.info('B has %d states', len(b.g))
        # Compute the product automaton
        p = ts_times_buchi(t, b)
        logger.info('P has %d states', len(p.g))
        logger.info('Set F has %d states', len(p.final))
        # Find the set S of states w/ opt_prop
        s = p.nodes_w_prop(opt_prop)
        logger.info('Set S has %d states', len(s))
        # Compute the suffix_cycle* and suffix_cycle_cost*
        suffix_cycle_cost, suffix_cycle_on_p = min_bottleneck_cycle(
            p.g, s, p.final)
        # Compute the prefix: a shortest path from p.init to suffix_cycle
        prefix_length = float('inf')
        prefix_on_p = ['']
        i_star = 0
        for init_state in p.init.keys():
            for i in range(0, len(suffix_cycle_on_p)):
                length, prefix = source_to_target_dijkstra(
                    p.g, init_state, suffix_cycle_on_p[i], degen_paths=True)
                if (length < prefix_length):
                    prefix_length = length
                    prefix_on_p = prefix
                    i_star = i

        if (prefix_length == float('inf')):
            raise Exception(__name__, 'Could not compute the prefix.')

        # Wrap suffix_cycle_on_p as required
        if i_star != 0:
            # Cut and paste
            suffix_cycle_on_p = suffix_cycle_on_p[i_star:] + suffix_cycle_on_p[
                1:i_star + 1]

        # Compute projection of prefix and suffix-cycle to T and return
        suffix_cycle = [x[0] for x in suffix_cycle_on_p]
        prefix = [x[0] for x in prefix_on_p]
        return (prefix_length, prefix, suffix_cycle_cost, suffix_cycle)
    except Exception as ex:
        if (len(ex.args) == 2):
            print("{}: {}".format(*ex.args))
        else:
            print("{}: Unknown exception {}: {}".format(
                __name__, type(ex), ex))
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_tb(exc_traceback)
            exit(1)
Exemplo n.º 6
0
    def globalPolicy(self, ts=None):
        '''Computes the global policy.'''
        paths = nx.shortest_path(self.g)

        policy = None
        suffix_cost = None
        for init in self.init:
            if self.g.has_node(init):
                for final in self.final:
                    if self.g.has_node(final):
                        prefix = paths[init][final]
                        cost, suffix = source_to_target_dijkstra(
                            self.g, final, final)
                        if prefix and len(suffix) > 2:
                            _, prefix = source_to_target_dijkstra(
                                self.g, init, final)
                            comp_policy = (list(prefix), list(suffix))
                            if not policy or cost < suffix_cost:
                                policy = comp_policy
                                suffix_cost = cost
        prefix, suffix = policy
        return prefix, suffix, suffix_cost
Exemplo n.º 7
0
def optimal_run(t, formula, opt_prop):
    try:
        logger.info('T has %d states', len(t.g))
        # Convert formula to Buchi automaton
        b = Buchi()
        b.from_formula(formula)
        logger.info('B has %d states', len(b.g))
        # Compute the product automaton
        p = ts_times_buchi(t, b)
        logger.info('P has %d states', len(p.g))
        logger.info('Set F has %d states', len(p.final))
        # Find the set S of states w/ opt_prop
        s = p.nodes_w_prop(opt_prop)
        logger.info('Set S has %d states', len(s))
        # Compute the suffix_cycle* and suffix_cycle_cost*
        suffix_cycle_cost, suffix_cycle_on_p = min_bottleneck_cycle(p.g, s, p.final)
        # Compute the prefix: a shortest path from p.init to suffix_cycle
        prefix_length = float('inf')
        prefix_on_p = ['']
        i_star = 0
        for init_state in p.init.keys():
            for i in range(0,len(suffix_cycle_on_p)):
                length, prefix = source_to_target_dijkstra(p.g, init_state, suffix_cycle_on_p[i], degen_paths = True)
                if(length < prefix_length):
                    prefix_length = length
                    prefix_on_p = prefix
                    i_star = i

        if(prefix_length == float('inf')):
            raise Exception(__name__, 'Could not compute the prefix.')

        # Wrap suffix_cycle_on_p as required
        if i_star != 0:
            # Cut and paste
            suffix_cycle_on_p = suffix_cycle_on_p[i_star:] + suffix_cycle_on_p[1:i_star+1]

        # Compute projection of prefix and suffix-cycle to T and return
        suffix_cycle = map(lambda x: x[0], suffix_cycle_on_p)
        prefix = map(lambda x: x[0], prefix_on_p)
        return (prefix_length, prefix, suffix_cycle_cost, suffix_cycle)
    except Exception as ex:
        if(len(ex.args) == 2):
            print "%s: %s" % ex.args
        else:
            print "%s: Unknown exception %s: %s" % (__name__, type(ex), ex)
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_tb(exc_traceback)
            exit(1)
Exemplo n.º 8
0
    def globalPolicy(self, ts=None):
        '''Computes the global policy.'''
        paths = nx.shortest_path(self.g)

        policy = None
        policy_len = None
        for init in self.init:
            for final in self.final:
                prefix = paths[init][final]
                _, suffix = source_to_target_dijkstra(self.g, final, final)
                if prefix and len(suffix) > 2:
                    comp_policy = (list(prefix), list(suffix))
                    comp_len = len(comp_policy[0]) + len(comp_policy[1])
                    if not policy or policy_len > comp_len:
                        policy = comp_policy
                        policy_len = comp_len

        prefix, suffix = policy
        return [v[0] for v in prefix], [v[0] for v in suffix]
Exemplo n.º 9
0
	def clean_final_states(self):
		# By construction all final states are reachable from
		# the initial states of global_pa

		# Find and remove the final states that cannot reach themselves
		unreaching_finals = set()
		unreaching_finals.update(self.global_pa.final)
		for final in self.global_pa.final:
			dist, _ = source_to_target_dijkstra(self.global_pa.g, final, final, degen_paths=False, weight_key='weight')
			if dist == float('inf'):
				# final cannot reach itself
				continue
			# final can reach itself
			unreaching_finals -= set([final])
		self.global_pa.final -= unreaching_finals
		# Remove unreaching finals from the set of initial states
		for state in unreaching_finals:
			if state in self.global_pa.init:
				del self.global_pa.init[state]
		# Finally, actually remove these states from the graph
		# (Can also remove states w/ fd(q) = \infty)
		self.global_pa.g.remove_nodes_from(unreaching_finals)
Exemplo n.º 10
0
def min_bottleneck_cycle(g, s, f):
    """ Returns the minimum bottleneck cycle from s to f in graph g.

    An implementation of the Min-Bottleneck-Cycle Algortihm
    in S.L. Smith, J. Tumova, C. Belta, D. Rus " Optimal Path Planning
    for Surveillance with Temporal Logic Constraints", in IJRR.

    Parameters
    ----------
    g : NetworkX graph

    s : A set of nodes
        These nodes satisfy the optimizing proposition.

    f : A set of nodes
        These nodes are the final states of B x T product.

    Returns
    -------
    cycle : List of node labels.
        The minimum bottleneck cycle S->F->S

    Examples
    --------

    Notes
    -----

    """

    global pp_installed
    if not pp_installed:
        raise Exception('This functionality is not enables because, '
                        'Parallel Python not installed!')

    # Start job server
    job_server = pp.Server(ppservers=pp_servers, secret='trivial')

    # Compute shortest S->S and S->F paths
    logger.info('S->S+F')
    #d = subset_to_subset_dijkstra_path_value(g, s, s|f, degen_paths = False)
    jobs = job_dispatcher(job_server, subset_to_subset_dijkstra_path_value, list(s), 1, '0', (g, s|f, 'sum', False, 'weight'), data_source)
    d = dict()
    for i in range(0,len(jobs)):
        d.update(jobs[i]())
        jobs[i]=''
    del jobs
    logger.info('Collected results for S->S+F')

    # Create S->S, S->F dict of dicts
    g_s_edges = []
    d_s_to_f = dict()
    for src in d.keys():
        for dest in d[src].keys():
            if dest in s:
                w = d[src][dest]
                g_s_edges.append((src,dest,w))
            if dest in f:
                # We allow degenerate S->F paths
                w = 0 if src == dest else d[src][dest]
                if src not in d_s_to_f:
                    d_s_to_f[src] = dict()
                d_s_to_f[src][dest] = w

    # Create the G_s graph
    g_s = nx.MultiDiGraph()
    g_s.add_weighted_edges_from(g_s_edges)
    # Remove d and g_s_edges to save memory
    del d
    del g_s_edges

    # Compute shortest F->S paths
    logger.info('F->S')
    #d_f_to_s = subset_to_subset_dijkstra_path_value(g, f, s, degen_paths = True)
    jobs = job_dispatcher(job_server, subset_to_subset_dijkstra_path_value, list(f), 1, '1', (g, s, 'sum', True, 'weight'), data_source)
    d_f_to_s = dict()
    for i in range(0,len(jobs)):
        d_f_to_s.update(jobs[i]())
        jobs[i]=''
    del jobs
    logger.info('Collected results for F->S')

    # Compute shortest S-bottleneck paths between verices in s
    logger.info('S-bottleneck')
    #d_bot = subset_to_subset_dijkstra_path_value(g_s, s, s, combine_fn = (lambda a,b: max(a,b)), degen_paths = False)
    jobs = job_dispatcher(job_server, subset_to_subset_dijkstra_path_value, list(s), 1, '2', (g_s, s, 'max', False, 'weight'), data_source)

    d_bot = dict()
    for i in range(0,len(jobs)):
        d_bot.update(jobs[i]())
        jobs[i]=''
    del jobs
    logger.info('Collected results for S-bottleneck')

    # Find the triple \in F x S x S that minimizes C(f,s1,s2)
    logger.info('Path*')
    jobs = job_dispatcher(job_server, find_best_cycle, list(f), 1, '3', (s, d_f_to_s, d_s_to_f, d_bot), data_source)
    cost_star = float('inf')
    len_star = float('inf')
    cycle_star = None
    for i in range(0,len(jobs)):
        this_cost, this_len, this_cycle = jobs[i]()
        jobs[i]=''
        if (this_cost < cost_star or (this_cost == cost_star and this_len < len_star)):
            cost_star = this_cost
            len_star = this_len
            cycle_star = this_cycle
    del jobs
    logger.info('Collected results for Path*')
    logger.info('Cost*: %d, Len*: %d, Cycle*: %s', cost_star, len_star, cycle_star)

    if cost_star == float('inf'):
        raise Exception(__name__, 'Failed to find a satisfying cycle, spec cannot be satisfied.')

    else:
        logger.info('Extracting Path*')
        (ff, s1, s2) = cycle_star
        # This is the F->S1 path
        (cost_ff_to_s1, path_ff_to_s1) = source_to_target_dijkstra(g, ff, s1, degen_paths = True, cutoff = d_f_to_s[ff][s1])
        # This is the S2->F path
        (cost_s2_to_ff, path_s2_to_ff) = source_to_target_dijkstra(g, s2, ff, degen_paths = True, cutoff = d_s_to_f[s2][ff])
        if s1 == s2 and ff != s1:
            # The path will be F->S1==S2->F
            path_star = path_ff_to_s1[0:-1] + path_s2_to_ff
            assert(cost_star == (cost_ff_to_s1 + cost_s2_to_ff))
            assert(len_star == (cost_ff_to_s1 + cost_s2_to_ff))
        else:
            # The path will be F->S1->S2->F
            # Extract the path from s_1 to s_2
            (bot_cost_s1_to_s2, bot_path_s1_to_s2) = source_to_target_dijkstra(g_s, s1, s2, combine_fn = 'max', degen_paths = False, cutoff = d_bot[s1][s2][0])
            assert(cost_star == max((cost_ff_to_s1 + cost_s2_to_ff),bot_cost_s1_to_s2))
            path_s1_to_s2 = []
            cost_s1_to_s2 = 0
            for i in range(1,len(bot_path_s1_to_s2)):
                source = bot_path_s1_to_s2[i-1]
                target = bot_path_s1_to_s2[i]
                cost_segment, path_segment = source_to_target_dijkstra(g, source, target, degen_paths = False)
                path_s1_to_s2 = path_s1_to_s2[0:-1] + path_segment
                cost_s1_to_s2 += cost_segment
            assert(len_star == cost_ff_to_s1 + cost_s1_to_s2 + cost_s2_to_ff)

            # path_ff_to_s1 and path_s2_to_ff can be degenerate paths,
            # but path_s1_to_s2 cannot, thus path_star is defined as this:
            # last ff is kept to make it clear that this is a suffix-cycle
            path_star = path_ff_to_s1[0:-1] + path_s1_to_s2[0:-1] + path_s2_to_ff

        return (cost_star, path_star)
Exemplo n.º 11
0
def min_bottleneck_cycle(g, s, f):
    """ Returns the minimum bottleneck cycle from s to f in graph g.

    An implementation of the Min-Bottleneck-Cycle Algortihm
    in S.L. Smith, J. Tumova, C. Belta, D. Rus " Optimal Path Planning
    for Surveillance with Temporal Logic Constraints", in IJRR.

    Parameters
    ----------
    g : NetworkX graph

    s : A set of nodes
        These nodes satisfy the optimizing proposition.

    f : A set of nodes
        These nodes are the final states of B x T product.

    Returns
    -------
    cycle : List of node labels.
        The minimum bottleneck cycle S->F->S

    Examples
    --------

    Notes
    -----

    """

    global pp_installed
    if not pp_installed:
        raise Exception('This functionality is not enables because, '
                        'Parallel Python not installed!')

    # Start job server
    job_server = pp.Server(ppservers=pp_servers, secret='trivial')

    # Compute shortest S->S and S->F paths
    logger.info('S->S+F')
    #d = subset_to_subset_dijkstra_path_value(g, s, s|f, degen_paths = False)
    jobs = job_dispatcher(job_server, subset_to_subset_dijkstra_path_value,
                          list(s), 1, '0', (g, s | f, 'sum', False, 'weight'),
                          data_source)
    d = dict()
    for i in range(0, len(jobs)):
        d.update(jobs[i]())
        jobs[i] = ''
    del jobs
    logger.info('Collected results for S->S+F')

    # Create S->S, S->F dict of dicts
    g_s_edges = []
    d_s_to_f = dict()
    for src in d.keys():
        for dest in d[src].keys():
            if dest in s:
                w = d[src][dest]
                g_s_edges.append((src, dest, w))
            if dest in f:
                # We allow degenerate S->F paths
                w = 0 if src == dest else d[src][dest]
                if src not in d_s_to_f:
                    d_s_to_f[src] = dict()
                d_s_to_f[src][dest] = w

    # Create the G_s graph
    g_s = nx.MultiDiGraph()
    g_s.add_weighted_edges_from(g_s_edges)
    # Remove d and g_s_edges to save memory
    del d
    del g_s_edges

    # Compute shortest F->S paths
    logger.info('F->S')
    #d_f_to_s = subset_to_subset_dijkstra_path_value(g, f, s, degen_paths = True)
    jobs = job_dispatcher(job_server, subset_to_subset_dijkstra_path_value,
                          list(f), 1, '1', (g, s, 'sum', True, 'weight'),
                          data_source)
    d_f_to_s = dict()
    for i in range(0, len(jobs)):
        d_f_to_s.update(jobs[i]())
        jobs[i] = ''
    del jobs
    logger.info('Collected results for F->S')

    # Compute shortest S-bottleneck paths between verices in s
    logger.info('S-bottleneck')
    #d_bot = subset_to_subset_dijkstra_path_value(g_s, s, s, combine_fn = (lambda a,b: max(a,b)), degen_paths = False)
    jobs = job_dispatcher(job_server, subset_to_subset_dijkstra_path_value,
                          list(s), 1, '2', (g_s, s, 'max', False, 'weight'),
                          data_source)

    d_bot = dict()
    for i in range(0, len(jobs)):
        d_bot.update(jobs[i]())
        jobs[i] = ''
    del jobs
    logger.info('Collected results for S-bottleneck')

    # Find the triple \in F x S x S that minimizes C(f,s1,s2)
    logger.info('Path*')
    jobs = job_dispatcher(job_server, find_best_cycle, list(f), 1, '3',
                          (s, d_f_to_s, d_s_to_f, d_bot), data_source)
    cost_star = float('inf')
    len_star = float('inf')
    cycle_star = None
    for i in range(0, len(jobs)):
        this_cost, this_len, this_cycle = jobs[i]()
        jobs[i] = ''
        if (this_cost < cost_star
                or (this_cost == cost_star and this_len < len_star)):
            cost_star = this_cost
            len_star = this_len
            cycle_star = this_cycle
    del jobs
    logger.info('Collected results for Path*')
    logger.info('Cost*: %d, Len*: %d, Cycle*: %s', cost_star, len_star,
                cycle_star)

    if cost_star == float('inf'):
        raise Exception(
            __name__,
            'Failed to find a satisfying cycle, spec cannot be satisfied.')

    else:
        logger.info('Extracting Path*')
        (ff, s1, s2) = cycle_star
        # This is the F->S1 path
        (cost_ff_to_s1,
         path_ff_to_s1) = source_to_target_dijkstra(g,
                                                    ff,
                                                    s1,
                                                    degen_paths=True,
                                                    cutoff=d_f_to_s[ff][s1])
        # This is the S2->F path
        (cost_s2_to_ff,
         path_s2_to_ff) = source_to_target_dijkstra(g,
                                                    s2,
                                                    ff,
                                                    degen_paths=True,
                                                    cutoff=d_s_to_f[s2][ff])
        if s1 == s2 and ff != s1:
            # The path will be F->S1==S2->F
            path_star = path_ff_to_s1[0:-1] + path_s2_to_ff
            assert (cost_star == (cost_ff_to_s1 + cost_s2_to_ff))
            assert (len_star == (cost_ff_to_s1 + cost_s2_to_ff))
        else:
            # The path will be F->S1->S2->F
            # Extract the path from s_1 to s_2
            (bot_cost_s1_to_s2, bot_path_s1_to_s2) = source_to_target_dijkstra(
                g_s,
                s1,
                s2,
                combine_fn='max',
                degen_paths=False,
                cutoff=d_bot[s1][s2][0])
            assert (cost_star == max((cost_ff_to_s1 + cost_s2_to_ff),
                                     bot_cost_s1_to_s2))
            path_s1_to_s2 = []
            cost_s1_to_s2 = 0
            for i in range(1, len(bot_path_s1_to_s2)):
                source = bot_path_s1_to_s2[i - 1]
                target = bot_path_s1_to_s2[i]
                cost_segment, path_segment = source_to_target_dijkstra(
                    g, source, target, degen_paths=False)
                path_s1_to_s2 = path_s1_to_s2[0:-1] + path_segment
                cost_s1_to_s2 += cost_segment
            assert (len_star == cost_ff_to_s1 + cost_s1_to_s2 + cost_s2_to_ff)

            # path_ff_to_s1 and path_s2_to_ff can be degenerate paths,
            # but path_s1_to_s2 cannot, thus path_star is defined as this:
            # last ff is kept to make it clear that this is a suffix-cycle
            path_star = path_ff_to_s1[0:-1] + path_s1_to_s2[
                0:-1] + path_s2_to_ff

        return (cost_star, path_star)