# Path: hylaa/util.py
# class Freezable():
#     'a class where you can freeze the fields (prevent new fields from being created)'
# 
#     _frozen = False
# 
#     def freeze_attrs(self):
#         'prevents any new attributes from being created in the object'
#         self._frozen = True
# 
#     def __setattr__(self, key, value):
#         if self._frozen and not hasattr(self, key):
#             raise TypeError("{} does not contain attribute '{}' (object was frozen)".format(self, key))
# 
#         object.__setattr__(self, key, value)
# 
# Path: hylaa/time_elapse.py
# class TimeElapser(Freezable):
#     'Object which computes the time-elapse function for a single mode at multiples of the time step'
# 
#     def __init__(self, mode, step_size):
#         self.mode = mode
#         self.step_size = step_size
#         self.dims = self.mode.a_csr.shape[0]
#         self.inputs = 0 if self.mode.b_csr is None else self.mode.b_csr.shape[1]
# 
#         self.time_elapse_obj = None
# 
#         self.freeze_attrs()
# 
#     def get_basis_matrix(self, step_num):
#         '''perform the computation for the the basis matrix and input effects matrix at the passed-in step
# 
#         returns a tuple (basis_matrix, input_effects matrix)
#         '''
# 
#         if self.time_elapse_obj is None:
#             Timers.tic('init time_elapse_obj')
#             self.time_elapse_obj = TimeElapseExpmMult(self)
#             Timers.toc('init time_elapse_obj')
# 
#         Timers.tic('step')
#         self.time_elapse_obj.assign_basis_matrix(step_num)
#         Timers.toc('step')
# 
#         basis_mat = self.time_elapse_obj.cur_basis_matrix
#         input_effects_mat = self.time_elapse_obj.cur_input_effects_matrix
# 
#         # post-conditions check
#         assert isinstance(basis_mat, np.ndarray), "cur_basis_mat should be an np.array, " + \
#             "but it was {}".format(type(basis_mat))
# 
#         assert basis_mat.shape == (self.dims, self.dims), \
#             "cur_basis mat shape({}) should be {}".format(basis_mat.shape, (self.dims, self.dims))
# 
#         if self.inputs == 0 or step_num == 0: # 0-th step input should be null
#             assert input_effects_mat is None
#         else:
#             assert isinstance(input_effects_mat, np.ndarray)
# 
#             if self.time_elapse_obj.use_lgg:
#                 assert input_effects_mat.shape == (self.dims, self.inputs + self.dims)
#             else:
#                 assert input_effects_mat.shape == (self.dims, self.inputs)
# 
#         return basis_mat, input_effects_mat
# 
#     def use_lgg_approx(self):
#         '''
#         set this time elapse object to use lgg approximation model
#         '''
# 
#         self.time_elapse_obj.use_lgg_approx()
# 
# Path: hylaa/lputil.py
# def from_box(box_list, mode):
# def from_zonotope(center, generator_list, mode):
# def from_constraints(csr, rhs, mode, types=None, names=None, dims=None):
# def set_basis_matrix(lpi, basis_mat):
# def add_input_effects_matrix(lpi, input_mat, mode, lgg_beta=None):
# def check_intersection(lpi, lc, tol=1e-13):
# def add_init_constraint(lpi, vec, rhs, basis_matrix=None, input_effects_list=None, row_index=None):
# def try_replace_init_constraint(lpi, old_row_index, direction, rhs, basis_mat=None, input_effects_list=None):
# def aggregate_chull(lpi_list, mode):
# def _aggregate_chull_recursive(lpi_list, mode):
# def aggregate(lpi_list, direction_matrix, mode):
# def get_basis_matrix(lpi):
# def scale_with_bm(lpi, amount):
# def bloat(lpi, amount, var_name_prefix='bloat'):
# def add_reset_variables(lpi, mode_id, transition_index, # pylint: disable=too-many-locals, too-many-statements
#                         reset_csr=None, minkowski_csr=None,
#                         minkowski_constraints_csr=None, minkowski_constraints_rhs=None, successor_has_inputs=False): 
# def add_curtime_constraints(lpi, csr, rhs_vec):
# def get_box_center(lpi):
# def make_direction_matrix(point, a_csr):
# def reorthogonalize_matrix(mat, dims):
# def is_feasible(csr, rhs):
# def is_point_in_lpi(point, orig_lpi):
# def compute_radius_inf(lpi):
# def minkowski_sum(lpi_list, mode):
# def from_input_constraints(b_mat, u_constraints, u_rhs, mode):


import numpy as np
import scipy as sp
from scipy.sparse import csr_matrix, csc_matrix
from hylaa.util import Freezable
from hylaa.time_elapse import TimeElapser
from hylaa import lputil
            rv = False
        elif not (self.csr.indptr == self.csr.indptr).all():
            rv = False
        else:
            for i in range(len(self.csr.data)):
                a = self.csr.data[i]
                b = other.csr.data[i]

                if abs(a - b) > tol:
                    rv = False
                    break

        return rv

    def is_true_for_point(self, point):
        'is the constraint true for the passed in point?'

        return self.csr * point <= self.rhs

    def negate(self):
        'return the negation of the condition'

        return LinearConstraint(-1 * self.csr, -self.rhs)

    def __str__(self):
        return '[LinearConstraint: {} * x <= {}]'.format(self.csr.toarray(), self.rhs)

    def __repr__(self):
        return 'LinearConstraint({}, {})'.format(repr(self.csr), repr(self.rhs))


# Path: hylaa/util.py
# class Freezable():
#     'a class where you can freeze the fields (prevent new fields from being created)'
# 
#     _frozen = False
# 
#     def freeze_attrs(self):
#         'prevents any new attributes from being created in the object'
#         self._frozen = True
# 
#     def __setattr__(self, key, value):
#         if self._frozen and not hasattr(self, key):
#             raise TypeError("{} does not contain attribute '{}' (object was frozen)".format(self, key))
# 
#         object.__setattr__(self, key, value)
# 
# Path: hylaa/time_elapse.py
# class TimeElapser(Freezable):
#     'Object which computes the time-elapse function for a single mode at multiples of the time step'
# 
#     def __init__(self, mode, step_size):
#         self.mode = mode
#         self.step_size = step_size
#         self.dims = self.mode.a_csr.shape[0]
#         self.inputs = 0 if self.mode.b_csr is None else self.mode.b_csr.shape[1]
# 
#         self.time_elapse_obj = None
# 
#         self.freeze_attrs()
# 
#     def get_basis_matrix(self, step_num):
#         '''perform the computation for the the basis matrix and input effects matrix at the passed-in step
# 
#         returns a tuple (basis_matrix, input_effects matrix)
#         '''
# 
#         if self.time_elapse_obj is None:
#             Timers.tic('init time_elapse_obj')
#             self.time_elapse_obj = TimeElapseExpmMult(self)
#             Timers.toc('init time_elapse_obj')
# 
#         Timers.tic('step')
#         self.time_elapse_obj.assign_basis_matrix(step_num)
#         Timers.toc('step')
# 
#         basis_mat = self.time_elapse_obj.cur_basis_matrix
#         input_effects_mat = self.time_elapse_obj.cur_input_effects_matrix
# 
#         # post-conditions check
#         assert isinstance(basis_mat, np.ndarray), "cur_basis_mat should be an np.array, " + \
#             "but it was {}".format(type(basis_mat))
# 
#         assert basis_mat.shape == (self.dims, self.dims), \
#             "cur_basis mat shape({}) should be {}".format(basis_mat.shape, (self.dims, self.dims))
# 
#         if self.inputs == 0 or step_num == 0: # 0-th step input should be null
#             assert input_effects_mat is None
#         else:
#             assert isinstance(input_effects_mat, np.ndarray)
# 
#             if self.time_elapse_obj.use_lgg:
#                 assert input_effects_mat.shape == (self.dims, self.inputs + self.dims)
#             else:
#                 assert input_effects_mat.shape == (self.dims, self.inputs)
# 
#         return basis_mat, input_effects_mat
# 
#     def use_lgg_approx(self):
#         '''
#         set this time elapse object to use lgg approximation model
#         '''
# 
#         self.time_elapse_obj.use_lgg_approx()
# 
# Path: hylaa/lputil.py
# def from_box(box_list, mode):
# def from_zonotope(center, generator_list, mode):
# def from_constraints(csr, rhs, mode, types=None, names=None, dims=None):
# def set_basis_matrix(lpi, basis_mat):
# def add_input_effects_matrix(lpi, input_mat, mode, lgg_beta=None):
# def check_intersection(lpi, lc, tol=1e-13):
# def add_init_constraint(lpi, vec, rhs, basis_matrix=None, input_effects_list=None, row_index=None):
# def try_replace_init_constraint(lpi, old_row_index, direction, rhs, basis_mat=None, input_effects_list=None):
# def aggregate_chull(lpi_list, mode):
# def _aggregate_chull_recursive(lpi_list, mode):
# def aggregate(lpi_list, direction_matrix, mode):
# def get_basis_matrix(lpi):
# def scale_with_bm(lpi, amount):
# def bloat(lpi, amount, var_name_prefix='bloat'):
# def add_reset_variables(lpi, mode_id, transition_index, # pylint: disable=too-many-locals, too-many-statements
#                         reset_csr=None, minkowski_csr=None,
#                         minkowski_constraints_csr=None, minkowski_constraints_rhs=None, successor_has_inputs=False): 
# def add_curtime_constraints(lpi, csr, rhs_vec):
# def get_box_center(lpi):
# def make_direction_matrix(point, a_csr):
# def reorthogonalize_matrix(mat, dims):
# def is_feasible(csr, rhs):
# def is_point_in_lpi(point, orig_lpi):
# def compute_radius_inf(lpi):
# def minkowski_sum(lpi_list, mode):
# def from_input_constraints(b_mat, u_constraints, u_rhs, mode):


# Path: hylaa/hybrid_automaton.py
import numpy as np
import scipy as sp
from scipy.sparse import csr_matrix, csc_matrix
from hylaa.util import Freezable
from hylaa.time_elapse import TimeElapser
from hylaa import lputil

            rv = False
        elif not (self.csr.indptr == self.csr.indptr).all():
            rv = False
        else:
            for i in range(len(self.csr.data)):
                a = self.csr.data[i]
                b = other.csr.data[i]

                if abs(a - b) > tol:
                    rv = False
                    break

        return rv

    def is_true_for_point(self, point):
        'is the constraint true for the passed in point?'

        return self.csr * point <= self.rhs

    def negate(self):
        'return the negation of the condition'

        return LinearConstraint(-1 * self.csr, -self.rhs)

    def __str__(self):
        return '[LinearConstraint: {} * x <= {}]'.format(self.csr.toarray(), self.rhs)

    def __repr__(self):
        return 'LinearConstraint({}, {})'.format(repr(self.csr), repr(self.rhs))


class Mode(Freezable):