Exemplo n.º 1
0
def test_orderfcn_error():
    G = ModeGraph()
    G.add_nodes_from([1, 2, 3])

    G.add_edge(1, 3, modes=[1])
    G.add_edge(1, 2, modes=[0])
    G.set_order_fcn(lambda n: n)
Exemplo n.º 2
0
def test_cycle_indices():
    G = ModeGraph()
    G.add_nodes_from([5, 1, 2, 3, 4])

    def order_fcn(n):
        if n == 5:
            return 0
        else:
            return n

    G.add_edge(2, 1, modes=[0])
    G.add_edge(1, 1, modes=[0])
    G.add_edge(1, 2, modes=[1])
    G.set_order_fcn(order_fcn)

    cycle1 = [(2, 0), (1, 0), (1, 1)]

    A1 = G.index_matrix(cycle1).todense()

    np.testing.assert_equal(
        A1,
        np.array([[0, 0, 0],
                  [0, 1, 1],
                  [1, 0, 0],
                  [0, 0, 0],
                  [0, 0, 0]])
    )
Exemplo n.º 3
0
class Abstraction(object):
    """Discrete abstraction of the hyper box defined by *lower_bounds*
       and *upper_bounds*. Time discretization is *tau* and space
       discretization is given by *eta*. Mode transitions are added
       with :py:func:`add_mode`."""

    def __init__(self, lower_bounds, upper_bounds, eta, tau):
        self.eta = eta
        self.tau = float(tau)

        self.lower_bounds = np.array(lower_bounds, dtype=np.float64)
        self.upper_bounds = np.array(upper_bounds, dtype=np.float64)
        self.upper_bounds += (self.upper_bounds - self.lower_bounds) % eta  # make domain number of eta's
        self.nextMode = 1

        # number of discrete points along each dimension
        self.n_dim = np.round((self.upper_bounds - self.lower_bounds) / eta).astype(int)

        # Create a graph and populate it with nodes
        self.graph = ModeGraph()
        for midx in itertools.product(*[range(dim) for dim in self.n_dim]):
            cell_lower_bounds = self.lower_bounds + np.array(midx) * eta
            cell_upper_bounds = cell_lower_bounds + eta
            self.graph.add_node(
                midx,
                lower_bounds=tuple(cell_lower_bounds),
                upper_bounds=tuple(cell_upper_bounds),
                mid=(cell_lower_bounds + cell_upper_bounds) / 2,
            )
        self.graph.set_order_fcn(self.node_to_idx)

    def __len__(self):
        """ Size of abstraction """
        return len(self.graph)

    def point_to_midx(self, point):
        """ Return the node multiindex corresponding to the continuous
            point *point*. """
        assert len(point) == len(self.n_dim)
        if not self.contains(point):
            raise ("Point outside domain")
        midx = np.floor((np.array(point) - self.lower_bounds) / self.eta).astype(np.uint64)
        return tuple(midx)

    def contains(self, point):
        """ Return ``True`` if *point* is within the abstraction domain,
        ``False`` otherwise. """
        if np.any(self.lower_bounds >= point) or np.any(self.upper_bounds <= point):
            return False
        return True

    def plot_planar(self):
        """ Plot a 2D abstraction. """
        assert len(self.lower_bounds) == 2
        ax = plt.axes()
        for node, attr in self.graph.nodes_iter(data=True):
            plt.plot(attr["mid"][0], attr["mid"][1], "ro")
        for n1, n2, mode in self.graph.edges_iter(data="mode"):
            mid1 = self.graph.node[n1]["mid"]
            mid2 = self.graph.node[n2]["mid"]
            col = "b" if mode == 1 else "g"
            ax.arrow(mid1[0], mid1[1], mid2[0] - mid1[0], mid2[1] - mid1[1], fc=col, ec=col)

        plt.show()

    def add_mode(self, vf, mode_name=None):
        """ Add new dynamic mode to the abstraction, given by
        the vector field *vf*. """

        if mode_name is None:
            mode_name = self.nextMode
            self.nextMode += 1

        def dummy_vf(z, t):
            return vf(z)

        tr_out = 0
        for node, attr in self.graph.nodes_iter(data=True):
            x_fin = scipy.integrate.odeint(dummy_vf, attr["mid"], np.arange(0, self.tau, self.tau / 100))
            if self.contains(x_fin[-1]):
                midx = self.point_to_midx(x_fin[-1])
                if self.graph.has_edge(node, midx):
                    # Edge already present, append mode
                    self.graph[node][midx]["modes"] += mode_name
                else:
                    # Create new edge with single mode
                    self.graph.add_edge(node, midx, modes=[mode_name])
            else:
                tr_out += 1
        if tr_out > 0:
            print "Warning: ", tr_out, " transitions out of ", len(
                self.graph
            ), " in mode ", mode_name, " go out of domain"

    def node_to_idx(self, node):
        """ Given a node at discrete multiindex :math:`(x,y,z)`,
            return the index :math:`L_z ( L_y x + y ) + z`,
            where :math:`L_z, L_y` are the (discrete) lengths of the
            hyper box domain,
            and correspondingly for higher/lower dimensions. The function
            is a 1-1 mapping between
            the nodes in the abstraction and the positive integers,
            and thus suitable as
            order_function in :py:func:`prefix_suffix_feasible`. """
        assert len(node) == len(self.n_dim)
        ret = node[0]
        for i in range(1, len(self.n_dim)):
            ret *= self.n_dim[i]
            ret += node[i]
        return ret

    def idx_to_node(self, idx):
        """ Inverse of :py:func:`node_to_idx` """
        assert idx < np.product(self.n_dim)
        node = [0] * len(self.n_dim)
        for i in reversed(range(len(self.n_dim))):
            node[i] = int(idx % self.n_dim[i])
            idx = np.floor(idx / self.n_dim[i])
        return tuple(node)