def test_composite_module_topology(): mgr = BDD() x = DynamicCover(0, 10) m1 = Interface(mgr, {'a': x}, {'b': x, 'i': x}) m2 = Interface(mgr, {'i': x, 'j': x}, {'k': x}) m12 = CompositeInterface((m1, m2)) assert m12.sorted_mods() == ((m1, ), (m2, )) assert set(m12.outputs) == {'k', 'b', 'i'} assert set(m12.inputs) == {'a', 'j'} assert set(m12.latent) == {'i'} m3 = Interface(mgr, {'k': x, 'b': x}, {}) m123 = CompositeInterface([m1, m2, m3]) assert m123.sorted_mods() == ((m1, ), (m2, ), (m3, )) assert set(m123.outputs) == {'b', 'i', 'k'} assert set(m123.inputs) == {'a', 'j'} assert set(m123.vars) == {'a', 'j', 'b', 'i', 'k'} # Renaming m123renamed = m123.renamed(b='r', a='q') assert set(m123renamed.outputs) == {'r', 'i', 'k'} assert set(m123renamed.inputs) == {'q', 'j'}
def setup(): """ Initialize binary circuit manager """ mgr = BDD() mgr.configure(reordering=False) """ Declare spaces and types """ # Declare continuous state spaces pspace = DynamicCover(-2, 2) anglespace = DynamicCover(-np.pi, np.pi, periodic=True) # Declare discrete control spaces vspace = EmbeddedGrid(2, vmax / 2, vmax) angaccspace = EmbeddedGrid(3, -1.5, 1.5) """ Declare interfaces """ dubins_x = Interface(mgr, { 'x': pspace, 'theta': anglespace, 'v': vspace }, {'xnext': pspace}) dubins_y = Interface(mgr, { 'y': pspace, 'theta': anglespace, 'v': vspace }, {'ynext': pspace}) dubins_theta = Interface(mgr, { 'theta': anglespace, 'v': vspace, 'omega': angaccspace }, {'thetanext': anglespace}) return mgr, dubins_x, dubins_y, dubins_theta
def test_series_comp(): mgr = BDD() inputs = { 'x': DynamicCover(0, 4), 'y': DynamicCover(0, 4), } output = {'z': DynamicCover(0, 4)} h = Interface(mgr, inputs, output) g = ('j', 'y') >> h >> ('z', 'r') precision = {'j': 4, 'x': 3, 'r': 3} g = g.io_refined({ 'j': (.75, 2.5), 'x': (2.5, 3.8), 'r': (2.1, 3.1) }, nbits=precision) h = ('r', 'x') >> h h = h.io_refined({ 'r': (1.3, 3.8), 'y': (1.0, 2.0), 'z': (.9, 3.1) }, nbits={ 'r': 3, 'y': 3, 'z': 4 }) assert (g >> h) == g.composed_with(h) assert (g >> h) == h.composed_with(g) assert (g >> h).nonblock() != mgr.false assert (g >> h).count_nb(bits=10) == approx(28) # 7 * 2 * 2
def run(self, steps: Optional[int] = None, winning: Optional[Interface] = None, verbose: bool = False): """ Run a reachability game until reaching a fixed point or a maximum number of steps. Parameters ---------- steps: int Maximum number of game steps to run winning: int Currently winning region verbose: bool If True (not default), then print out intermediate statistics. Returns ------- Interface: Backward reachable set int: Number of game steps run MemorylessController: Controller for the reach game """ if steps: assert steps >= 0 state_control = self.cpre.prestate.copy() state_control.update(self.cpre.control) C = Interface(self.cpre.mgr, state_control, {}) z = self.target if winning is None else winning zz = Interface(self.cpre.mgr, {}, {}) # Defaults to false interface. i = 0 while (z != zz): if steps and i == steps: break zz = z step_start = time.time() z = self.cpre(zz, verbose=verbose) # state-input pairs # TODO: Change this to shared refinement? C._assum = C.assum | (z.assum & ~self.cpre.elimcontrol(C.assum) ) # Add new state-input pairs to controller if verbose: print("Eliminating control") z = ihide(z, self.cpre.control) z = z + self.target i += 1 if verbose: print("Step #: ", i, "Step Time (s): ", time.time() - step_start, "Size: ", self.cpre.mgr.count(z.assum, len(z.assum.support)), "Winning nodes:", len(z.assum)) return z, i, MemorylessController(self.cpre, C)
def test_reachavoid(): composite = CompositeInterface((pcomp, vcomp)) dcpre = DecompCPre(composite, (('p', 'pnext'), ('v', 'vnext')), ('a')) target = pspace.conc2pred(mgr, 'p', [-3, 3], 6, innerapprox=True) target &= vspace.conc2pred(mgr, 'v', [0, 2], 6, innerapprox=True) targetint = Interface(mgr, { 'p': pspace, 'v': vspace }, {}, guar=mgr.true, assum=target) safe = pspace.conc2pred(mgr, 'p', [-8, 8], 6, innerapprox=True) safe &= ~pspace.conc2pred(mgr, 'p', [-.4, .4], 6, innerapprox=True) safe &= vspace.conc2pred(mgr, 'v', [-4, 4], 6, innerapprox=True) safeint = Interface(mgr, { 'p': pspace, 'v': vspace }, {}, guar=mgr.true, assum=safe) game = ReachAvoidGame(dcpre, safeint, targetint) basin, _, _ = game.run() # scatter2D(mgr, ('p', pspace), ('v', vspace), # basin.pred, # fname = "reachavoid_doubleint.png" # )
def coarse_abstract(f: Interface, iter_coarseness, save_precision, shift_frac=0.0): """ iter_coarseness: How many bits along each input dimension save_precision: Bits to record in the underlying grid shift: Shift boxes along each dimension by this fraction. Useful for the offset grid iterator. """ default_boxes = { 'x': (-.01, .01), 'y': (.49, .51), 'vx': (-.01, .01), 'vy': (-.01, .01), 'theta': (-.01, .01), 'omega': (-.01, .01), 't': -.01, 's': .25 } iter = f.input_iter(precision=iter_coarseness) for iobox in iter: # Lift missing arguments to a full dimension with default_boxes. for arg in default_boxes: if arg not in iobox: iobox[arg] = default_boxes[arg] # Bloat inputs for numerical precision, just in case. for k, v in iobox.items(): if isinstance(v, tuple): iobox[k] = bloatbox(shiftbox(v, shift_frac), .001) # Simulate and overapproximate outputs stateboxes = {k: v for k, v in iobox.items() if k not in ['s', 't']} s = lander_box_dynamics(steps=T, a=(iobox['t'], iobox['s']), **stateboxes, discrete=False) out = {i: bloatbox(j, factor=-0.01) for i, j in zip(nextstates, s) } # Shrink output box ever so slightly. iobox.update(out) # Refine iobox = {k: v for k, v in iobox.items() if k in f.vars} # Filter unnecessary input/output slices. f = f.io_refined(iobox, nbits=save_precision) f.check() print("Done coarse abs: ", f.outputs) return f
def heuristic(iface: Interface) -> Interface: """ Coarsens sink interface along the dimension that shrinks the set the least until a certain size met. """ assert iface.is_sink() while (len(iface.pred) > maxnodes): granularity = { k: len(v) for k, v in iface.pred_bitvars.items() if k in ['x', 'y', 'theta', 'xnext', 'ynext', 'thetanext'] } statebits = len(iface.pred.support) # List of (varname, # of coarsened interface nonblock input assignments) coarsened_ifaces = [(k, coarsen(iface, bits={ k: v - 1 }).count_nb(statebits)) for k, v in granularity.items()] coarsened_ifaces.sort(key=lambda x: x[1], reverse=True) best_var = coarsened_ifaces[0][0] # print(coarsened_ifaces) # print("Coarsening along dimension {}".format(best_var)) iface = coarsen(iface, bits={best_var: granularity[best_var] - 1}) return iface
def test_embeddedgrid_module(): mgr = BDD() inputs = {'x': EmbeddedGrid(4, 0, 3)} outputs = {'y': EmbeddedGrid(8, 4, 11)} m = Interface(mgr, inputs, outputs) mgr.declare("x_0", "x_1", "y_0", "y_1", "y_2") x0 = mgr.var("x_0") x1 = mgr.var("x_1") y0 = mgr.var("y_0") y1 = mgr.var("y_1") y2 = mgr.var("y_2") assert m.io_refined({ 'x': 2, 'y': 4 }).pred == (x0 & ~x1) & (~(x0 & ~x1) | (~y0 & ~y1 & ~y2)) assert len(mgr.vars) > 0
def make_target(mgr, composite: CompositeInterface): """ Controller Synthesis with a reach objective """ pspace = composite['x'] anglespace = composite['theta'] # Declare reach set as [0.8] x [-.8, 0] box in the x-y space. target = pspace.conc2pred(mgr, 'x', [1.0, 1.5], 5, innerapprox=False) target &= pspace.conc2pred(mgr, 'y', [1.0, 1.5], 5, innerapprox=False) targetmod = Interface(mgr, { 'x': pspace, 'y': pspace, 'theta': anglespace }, {}, guar=mgr.true, assum=target) targetmod.check() return targetmod
def test_sinks(): mgr = BDD() x = DynamicCover(0, 10) x1 = x.conc2pred(mgr, 'x', [1, 4], 5, innerapprox=True) x2 = x.conc2pred(mgr, 'x', [5, 8], 5, innerapprox=True) x3 = x.conc2pred(mgr, 'x', [0.0001, 5.001], 5, innerapprox=False) x4 = x.conc2pred(mgr, 'x', [4.9999, 9.999], 5, innerapprox=False) m1 = Interface(mgr, {'x': x}, {}, assum=x1) m2 = Interface(mgr, {'x': x}, {}, assum=x2) m3 = Interface(mgr, {'x': x}, {}, assum=x3) m4 = Interface(mgr, {'x': x}, {}, assum=x4) assert (m1 * m2).assum == mgr.false assert (m1 + m2).assum != mgr.false assert (m3 + m4).assum == mgr.true assert (m3 + m1) == m3 assert (m3 * m1) == m1 assert (m4 * m1) <= m4
def test_module_composition(): mgr = BDD() x = DynamicCover(0, 10) m1 = Interface(mgr, {'a': x}, {'b': x, 'c': x}) m2 = Interface(mgr, {'i': x, 'j': x}, {'k': x}) m12 = (m1 >> m2.renamed(i='c')) assert set(m12.inputs) == set(['a', 'j']) assert set(m12.outputs) == set(['c', 'b', 'k']) assert m12 == m2.renamed(i='c').composed_with(m1) assert m12 == (('c', 'i') >> m2).composed_with(m1) # Renaming is left associative assert m12 == m1 >> (('c', 'i') >> m2) assert m12 != (m1 >> ('c', 'i')) >> m2 assert m12 == ((m1.renamed(c='i') >> m2)).renamed(i='c') assert m12 == ((m1 >> ('c', 'i') >> m2)).renamed(i='c') assert m12 == m1.renamed(c='i').composed_with(m2).renamed(i='c') assert m12 == m1.renamed(c='i').composed_with(m2) >> ('i', 'c')
def __call__(self, Z: Interface, no_inputs=False, verbose=False) -> Interface: """ One step decomposed control predecessor Coarsens Z interface whenever its complexity grows too large. """ assert Z.is_sink() Z = rename(Z, self.pre_to_post) # See if the user has provided a pre-determined order to compose interfaces. if self.elimorder is not None: to_elim_post = list(self.elimorder) else: to_elim_post = list(self.poststate) if self.condition(Z): Z = self.heuristic(Z) # Eliminate each interface output # FIXME: This code assumes that each module only has a single output. # Should instead iterate over modules. Find a better way to specify the # module to be outputted. Can't hash them unfortunately. # Also doesn't take into account arbitrary DAG topologies and control inputs. while (len(to_elim_post) > 0): var = to_elim_post.pop() # Partition into modules that do/don't depend on var dep_mods = tuple(mod for mod in self.sys.children if var in mod.outputs) if len(dep_mods) == 0: continue # Find Z bit precision. Z_var_bits = len( [b for b in Z.assum.support if bv_var_name(b) == var]) assert Z_var_bits == len(Z.pred_bitvars[var]) for mod in dep_mods: Z = sinkprepend(coarsen(mod, **{var: Z_var_bits}), Z) if no_inputs: return ihide(Z, self.control) else: return Z
def __call__(self, Z: Interface, no_inputs: bool = False, verbose=False) -> Interface: """One step control predecessor""" assert Z.is_sink() if len(Z.outputs) > 0: raise ValueError("Only accept sink modules as inputs.") # Rename inputs from pre variables to post. Z = rename(Z, self.pre_to_post) # Compute robust state-input pairs xu = sinkprepend(self.sys, Z) # Return state-input pairs or only states if no_inputs: return ihide(xu, self.control) else: return xu
def test_reach_control(): composite = CompositeInterface((pcomp, vcomp)) dcpre = DecompCPre(composite, (('p', 'pnext'), ('v', 'vnext')), ('a')) target = pspace.conc2pred(mgr, 'p', [-2, 2], 6, innerapprox=True) targetint = Interface(mgr, { 'p': pspace, 'v': vspace }, {}, guar=mgr.true, assum=target) # Solve game and plot 2D invariant region game = ReachGame(dcpre, targetint) dbasin, _, _ = game.run() system = pcomp * vcomp cpre = ControlPre(system, (('p', 'pnext'), ('v', 'vnext')), ('a')) game = ReachGame(cpre, targetint) basin, _, _ = game.run() assert dbasin == basin
def test_safe_control(): composite = CompositeInterface((pcomp, vcomp)) dcpre = DecompCPre(composite, (('p', 'pnext'), ('v', 'vnext')), ('a')) safe = pspace.conc2pred(mgr, 'p', [-8, 8], 6, innerapprox=True) safesink = Interface(mgr, { 'p': pspace, 'v': vspace }, {}, guar=mgr.true, assum=safe) # Solve game and plot 2D invariant region game = SafetyGame(dcpre, safesink) dinv, _, controller = game.run() system = pcomp * vcomp cpre = ControlPre(system, (('p', 'pnext'), ('v', 'vnext')), ('a')) game = SafetyGame(cpre, safesink) inv, _, _ = game.run(verbose=True) assert dinv == inv # assert dinv.count_nb(p_precision + v_precision) == approx(5988) # Simulate for initial states state_box = fn.first(controller.winning_states()) assert state_box is not None state = {k: .5 * (v[0] + v[1]) for k, v in state_box.items()} for step in range(30): u = fn.first(controller.allows(state)) assert u is not None picked_u = { 'a': u['a'][0] } # Pick lower bound of first allowed control voxel state.update(picked_u) nextstate = dynamics(**state) state = {'p': nextstate[0], 'v': nextstate[1]}
def test_refinement_and_coarsening(): mgr = BDD() def conc(x): return -3 * x x = DynamicCover(-10, 10) y = DynamicCover(20, 20) linmod = Interface(mgr, {'x': x}, {'y': y}) width = 15 for _ in range(50): # Generate random input windows f_width = {'x': np.random.rand() * width} f_left = {'x': -10 + np.random.rand() * (20 - f_width['x'])} f_right = {k: f_width[k] + f_left[k] for k in f_width} iobox = {k: (f_left[k], f_right[k]) for k in f_width} # Generate output overapproximation ur = conc(**f_left) ll = conc(**f_right) iobox['y'] = (ll, ur) # Refine and check abstract relation newmod = linmod.io_refined(iobox, nbits={'x': 8, 'y': 8}) assert linmod <= newmod linmod = newmod # Check abstract relation relative to coarsened module assert linmod.coarsened(x=5, y=5) <= linmod assert linmod.coarsened(x=5) <= linmod assert linmod.coarsened(y=5) <= linmod # Coarsen should do nothing because it keeps many bits assert linmod.coarsened({'x': 10}, y=10) == linmod
def test_mixed_module(): from redax.module import Interface from redax.spaces import DynamicCover, FixedCover mgr = BDD() inputs = { 'x': DynamicCover(0, 16), 'y': FixedCover(-10, 10, 10), 'theta': DynamicCover(-np.pi, np.pi, periodic=True), 'v': FixedCover(0, 5, 5), 'omega': FixedCover(-2, 2, 4) } outputs = { 'xnext': DynamicCover(0, 4), 'ynext': FixedCover(-10, 10, 10), 'thetanext': DynamicCover(-np.pi, np.pi, periodic=True) } dubins = Interface(mgr, inputs, outputs) # Underspecified input-output with raises(AssertionError): dubins.io_refined( { 'v': (3.6, 3.7), 'theta': (6, -6), 'y': (2, 3), 'ynext': (2.1, 3.1) }, nbits={'theta': 3}) # Test that fixed covers yield correct space cardinality assert mgr.count(dubins.inspace(), 4 + 4 + 4 + 3 + 2) == 16 * 10 * 16 * 5 * 4 assert mgr.count(dubins.outspace(), 2 + 4 + 4) == 4 * 10 * 16
ts = .2 k = .1 g = 9.8 def dynamics(p, v, a): vsign = 1 if v > 0 else -1 return p + v * ts, v + a * ts - vsign * k * (v**2) * ts - g * ts pspace = DynamicCover(-10, 10) vspace = DynamicCover(-16, 16) aspace = DynamicCover(0, 20) # Smaller component modules pcomp = Interface(mgr, {'p': pspace, 'v': vspace}, {'pnext': pspace}) vcomp = Interface(mgr, {'v': vspace, 'a': aspace}, {'vnext': vspace}) # Declare grid precision p_precision = 7 v_precision = 7 precision = { 'p': p_precision, 'v': v_precision, 'a': 7, 'pnext': p_precision, 'vnext': v_precision } bittotal = sum(precision.values()) outorder = {0: 'pnext', 1: 'vnext'} possible_transitions = (pcomp * vcomp).count_io_space(bittotal)
def run(self, steps: Optional[int] = None, winning: Optional[Interface] = None, verbose: bool = False): """ Run a reach-avoid game until reaching a fixed point or a maximum number of steps. Solves for the temporal logic formula "safe UNTIL target" Parameters ---------- steps: int Maximum number of game steps to run Returns ------- Interface: Safe backward reachable set int: Number of game steps run MemorylessController: Controller for the reach-avoid game """ if steps: assert steps >= 0 state_control_vars = self.cpre.prestate.copy() state_control_vars.update(self.cpre.control) C = Interface(self.cpre.mgr, state_control_vars, {}) z = self.target if winning is None else winning zz = Interface(self.cpre.mgr, {}, {}) # Defaults to false interface. i = 0 while (z != zz): if steps and i == steps: print("Reached step limit") break zz = z step_start = time.time() z = self.cpre(zz, verbose=verbose) # state-input pairs # z = (self.cpre(zz, verbose=verbose) * self.safe) + self.target # state-input pairs # C = C | (z.assum & ~self.cpre.elimcontrol(C)) # Add new state-input pairs to controller C._assum = C.assum | (z.assum & ~self.cpre.elimcontrol(C.assum) ) # Add new state-input pairs to controller C._assum &= self.safe.assum if verbose: print("Eliminating control") z = ihide(z, self.cpre.control) z = (z * self.safe) + self.target i += 1 if verbose: print("Step #: ", i, "Step Time (s): ", time.time() - step_start, "Size: ", self.cpre.mgr.count(z.assum, len(z.assum.support)), "Winning nodes:", len(z.assum)) return z, i, MemorylessController(self.cpre, C)
# Reach Game if True: f = CompositeInterface(tuple(subsys[i] for i in states)) controller = None target = f['x'].conc2pred(mgr, 'x', (-.1, .1), 7, innerapprox=True) target &= f['y'].conc2pred(mgr, 'y', (.05, .15), 7, innerapprox=False) target &= f['theta'].conc2pred(mgr, 'theta', (-.15, .15), 5, innerapprox=True) target &= f['vy'].conc2pred(mgr, 'vy', (-.2, .3), 6, innerapprox=True) print("Target Size:", mgr.count(target, statebits)) target = Interface(mgr, {k: v for k, v in f.inputs.items() if k in states}, {}, guar=mgr.true, assum=target) w = target c = mgr.false iterreach_start = time.time() for gameiter in range(5): print("\nStep:", gameiter) # Monitoring the set of states in the lunar lander reach basin elimvars = [ v for v in w.pred.support if _name(v) not in ['x', 'y'] ]
g = 9.8 init_time = time.time() def dynamics(p, v, a): vsign = 1 if v > 0 else -1 return p + v * ts, v + a * ts - vsign * k * (v**2) * ts - g * ts pspace = DynamicCover(-10, 10) vspace = DynamicCover(-16, 16) aspace = DynamicCover(0, 20) # Smaller component modules pcomp = Interface(mgr, {'p': pspace, 'v': vspace}, {'pnext': pspace}) vcomp = Interface(mgr, {'v': vspace, 'a': aspace}, {'vnext': vspace}) bounds = {'p': [-10, 10], 'v': [-16, 16]} # Monolithic system system = pcomp * vcomp # Composite system composite = CompositeInterface((pcomp, vcomp)) # Declare grid precision p_precision = 7 v_precision = 7 precision = { 'p': p_precision,
def setup(init=False): mgr = BDD() mgr.configure( reordering=False ) # Reordering causes too much time overhead for minimal gain. subsys = {} subsys['x'] = Interface(mgr, { 'x': xspace, 'vx': vxspace, 'theta': thetaspace, 't': thrust }, {'xnext': xspace}) subsys['y'] = Interface(mgr, { 'y': yspace, 'vy': vyspace, 'theta': thetaspace, 't': thrust }, {'ynext': yspace}) subsys['vx'] = Interface( mgr, { 'vx': vxspace, 'theta': thetaspace, 'omega': omegaspace, 't': thrust, 's': side }, {'vxnext': vxspace}) subsys['vy'] = Interface( mgr, { 'vy': vyspace, 'theta': thetaspace, 'omega': omegaspace, 't': thrust, 's': side }, {'vynext': vyspace}) subsys['theta'] = Interface(mgr, { 'theta': thetaspace, 'omega': omegaspace, 's': side }, {'thetanext': thetaspace}) subsys['omega'] = Interface(mgr, { 'omega': omegaspace, 's': side }, {'omeganext': omegaspace}) # Coarse, but exhaustive abstraction of submodules. if init: iter_coarseness = { 'x': { 'x': 7, 'vx': 5, 'theta': 2 }, 'y': { 'y': 7, 'vy': 5, 'theta': 2 }, 'vx': { 'vx': 5, 'theta': 4, 'omega': 3 }, 'vy': { 'vy': 5, 'theta': 4, 'omega': 3 }, 'theta': { 'theta': 5, 'omega': 4 }, 'omega': { 'omega': 4 } } # iter_coarseness = {k: {k_: v_ + 1 for k_, v_ in v.items()} for k, v in iter_coarseness.items()} # iter_coarseness = sysview initabs_start = time.time() for i in states: print("Refining", i, "module") print("Iter Coarseness:", iter_coarseness[i]) print("Recording Coarseness:", {k: v for k, v in sysview[i].items() if k in subsys[i].vars}) subsys[i] = coarse_abstract(subsys[i], iter_coarseness[i], sysview[i]) print(len(mgr), "manager nodes after first pass") subsys[i].check() print("Subsys", i, "ND ratio:", nd_ratio(subsys[i])) subsys[i] = coarse_abstract(subsys[i], iter_coarseness[i], sysview[i], shift_frac=0.5) print(len(mgr), "manager nodes after second pass") subsys[i].check() print("Subsys", i, "ND ratio:", nd_ratio(subsys[i]), "\n") mgr.reorder( order_heuristic(mgr, [ 'x', 'y', 'vx', 'vy', 'theta', 'omega', 't', 's', 'xnext', 'ynext', 'vxnext', 'vynext', 'thetanext', 'omeganext' ])) print("Coarse Initial Abstraction Time (s): ", time.time() - initabs_start) return mgr, subsys
def test_dynamic_module(): mgr = BDD() inputs = { 'x': DynamicCover(0, 16), 'y': DynamicCover(0, 4), } output = {'z': DynamicCover(0, 4)} h = Interface(mgr, inputs, output) # Rename inputs assert set(h.inputs) == {'x', 'y'} assert set(h.outputs) == {'z'} g = ('j', 'x') >> h >> ('z', 'r') assert set(g.inputs) == {'j', 'y'} assert set(g.outputs) == {'r'} assert g == h.renamed(x='j', z='r') precision = {'j': 4, 'y': 3, 'r': 3} bittotal = sum(precision.values()) assert g.count_io_space(bittotal) == approx(1024) assert g.count_io(bittotal) == approx(0) g = g.io_refined({ 'j': (2.9, 10.1), 'y': (2.4, 3.8), 'r': (2.1, 3.1) }, nbits=precision) assert g.count_io(bittotal) == approx(42) # = 7 * 2 * 3 # Adding approximately the same transitions twice does nothing due to quantization oldpred = g.pred g = g.io_refined({ 'j': (3., 10.), 'y': (2.5, 3.8), 'r': (2.1, 3.1) }, nbits=precision) assert g.pred == oldpred assert (g).pred.support == { 'j_0', 'j_1', 'j_2', 'j_3', 'y_0', 'y_1', 'y_2', 'r_0', 'r_1', 'r_2' } assert (g >> ('r', 'z')).pred.support == { 'j_0', 'j_1', 'j_2', 'j_3', 'y_0', 'y_1', 'y_2', 'z_0', 'z_1', 'z_2' } assert g.nonblock() == g.input_to_abs({ 'j': (3., 10.), 'y': (2.5, 3.8) }, nbits=precision) # No inputs block assert g.nonblock() == (g.ohidden(g.outputs).pred) # Identity test for input and output renaming assert ((g >> ('r', 'z')) >> ('z', 'r')) == g assert (('j', 'w') >> (('w', 'j') >> g)) == g # Parallel composition assert set((g * h).outputs) == {'z', 'r'} assert set((g * h).inputs) == {'x', 'y', 'j'} # Series composition with disjoint I/O yields parallel composition assert (g >> h) == (g * h) assert (g >> h) == g.composed_with(h) assert (g >> h) == h.composed_with(g) assert (g * h) == h.composed_with(g) # Out of bounds errors with raises(OutOfDomainError): g = g.io_refined({ 'j': (3., 10.), 'y': (2.5, 3.8), 'r': (2.1, 4.6) }, silent=False, nbits=precision)
def run(self, steps: Optional[int] = None, winning: Optional[Interface] = None, verbose=False, winningonly=False): """ Run a safety game until reaching a fixed point or a maximum number of steps. Parameters ---------- steps: int Maximum number of game steps to run winning: Interface or None Currently winning region verbose: bool, False If True (not default), then print out intermediate statistics. winningonly: bool, False If true, output safety controller that only stores the invariant region. Returns ------- Interface: Safe invariant region int: Actualy number of game steps run. MemorylessController: Controller that maps state dictionary to safe input dictionary """ if steps is not None: assert steps >= 0 z = self.safe if winning is None else winning zz = Interface(self.cpre.mgr, {}, {}) # Defaults to false interface. # C = self.cpre.mgr.false state_control = self.cpre.prestate.copy() state_control.update(self.cpre.control) # C = Interface(self.cpre.mgr, state_control, {}) i = 0 while (z != zz): if steps and i == steps: break step_start = time.time() zz = z z = self.cpre(zz, verbose=verbose) C = z if verbose: print("Eliminating control") z = ihide(z, self.cpre.control) z = z * self.safe i = i + 1 if verbose: print( "Step #: ", i, "Step Time (s): {0:.3f}".format(time.time() - step_start), "Winning Size:", self.cpre.mgr.count(z.assum, len(z.assum.support)), "Winning nodes:", len(z.assum)) return z, i, MemorylessController(self.cpre, C)
Declare modules """ mgr = aigerwrapper() # Declare continuous state spaces pspace = DynamicCover(-2, 2) anglespace = DynamicCover(-np.pi, np.pi, periodic=True) # Declare discrete control spaces vspace = EmbeddedGrid(2, vmax / 2, vmax) angaccspace = EmbeddedGrid(3, -1.5, 1.5) # Declare modules dubins_x = Interface(mgr, { 'x': pspace, 'theta': anglespace, 'v': vspace }, {'xnext': pspace}) dubins_y = Interface(mgr, { 'y': pspace, 'theta': anglespace, 'v': vspace }, {'ynext': pspace}) dubins_theta = Interface(mgr, { 'theta': anglespace, 'v': vspace, 'omega': angaccspace }, {'thetanext': anglespace}) bits = 7 precision = {
def test_sin_sqrt_comp(): def containszero(left, right): """Determine if 0 is contained in a periodic interval [left,right]. Possible that right < left.""" # Map to interval [-pi,pi] left = ((left + np.pi) % (np.pi * 2)) - np.pi left = left - 2 * np.pi if left > np.pi else left right = ((right + np.pi) % (np.pi * 2)) - np.pi right = right - 2 * np.pi if right > np.pi else right if right < left: if left <= 0: return True else: if left <= 0 and right >= 0: return True return False def maxmincos(left, right): """Compute the maximum and minimum values of cos in an interval.""" if containszero(left, right) is True: maxval = 1 else: maxval = max([np.cos(left), np.cos(right)]) if containszero(left + np.pi, right + np.pi) is True: minval = -1 else: minval = min([np.cos(left), np.cos(right)]) return (minval, maxval) def maxminsin(left, right): """Compute the maximum and minimum values of sin in an interval.""" return maxmincos(left - np.pi / 2, right - np.pi / 2) mgr = BDD() sinout = DynamicCover(-1.2, 1.2) sinin = DynamicCover(-2 * np.pi, 2 * np.pi, periodic=True) # Sin module sinmod = Interface(mgr, {'sin': sinin}, {'sout': sinout}) # Sqrt module sqrtout = DynamicCover(0, 1.2) sqrtmod = Interface(mgr, {'sout': sinout}, {'sqrt': sqrtout}) comp = CompositeInterface([sinmod, sqrtmod]) def random_input_gen(module: Interface, scale: float) -> dict: iobox = dict() for invar, space in module.inputs.items(): if isinstance(space, ContinuousCover): width = scale * space.width() if space.periodic: left = space.lb + np.random.rand() * space.width() else: left = space.lb + np.random.rand() * (space.width() - width) right = left + width iobox.update({invar: (left, right)}) return iobox precision = {'sin': 9, 'sout': 9, 'sqrt': 9} # Learn sin module from redax.visualizer import scatter2D for i in range(200): iobox = random_input_gen(sinmod, scale=.01) out = maxminsin(iobox['sin'][0], iobox['sin'][1]) iobox.update({'sout': (out[0], out[1])}) # No errors should be raised sinmod = sinmod.io_refined(iobox, silent=False, nbits=precision) comp = comp.io_refined(iobox, nbits=precision) assert sinmod == comp.children[0] # Learn sqrt module for i in range(200): iobox = random_input_gen(sqrtmod, scale=.1) if iobox['sout'][0] < 0 or iobox['sout'][1] < 0: continue out = (math.sqrt(iobox['sout'][0]), math.sqrt(iobox['sout'][1])) iobox.update({'sqrt': out}) sqrtmod = sqrtmod.io_refined(iobox, silent=True, nbits=precision) comp = comp.io_refined(iobox, nbits=precision) assert sqrtmod == comp.children[1] # sinroot = (sinmod >> sqrtmod).ohidden(['sout']) sinroot = (comp.children[0] >> comp.children[1]).ohidden(['sout']) assert set(sinroot.vars) == {'sin', 'sqrt'} assert sinroot.pred != mgr.false sinroot.check()