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 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 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_dynamic_regular(): mgr = BDD() x = DynamicCover(-2.0, 2.0) assert x.pt2index(-2.0, 3) == 0 assert x.pt2index( 2.0, 3 ) == 8 # TODO: 3 bits = 0-7 but need to return 8 b/c of the right/left align detection assert x.pt2index(1.99, 3) == 7 assert x.pt2index(-1, 1) == 0 assert x.pt2box(-.1, 3) == (approx(-.5), approx(0)) assert x.pt2box(.6, 3) == (approx(.5), approx(1.0)) assert x.pt2box(.6, nbits=4) == (approx(.5), approx(.75)) assert x.pt2box(.6, nbits=2) == (approx(0), approx(1.0)) assert x.pt2box(.6, nbits=1) == (approx(0), approx(2.0)) # No bits yields the entire interval assert x.pt2box(1, 0) == (approx(-2), approx(2)) with raises(OutOfDomainError): x.pt2bv(3, 1) # Inner approximation tests assert set(x.box2bvs((.4, .6), 2, innerapprox=True)) == set([]) assert set(x.box2bvs((-.4, 1.6), 2, innerapprox=True)) == {(True, False)} assert set(x.box2bvs((-.3, .3), 2, innerapprox=True)) == set([]) # Outer approximations tests assert set(x.box2bvs((.4, .6), 2, innerapprox=False)) == {(True, False)} assert set(x.box2bvs((.4, .6), 2, innerapprox=False)) == {(True, False)} assert set(x.box2bvs((.99, 1.01), 2, innerapprox=False)) == {(True, False), (True, True)} # Some numerical sensitivity tests assert x.box2indexwindow((-1.0, 1.0), 2, innerapprox=True) == (1, 2) assert x.box2indexwindow((-1.0, 1.0 - .00001), 2, innerapprox=True) == (1, 1) assert x.box2indexwindow((-1.0 - .00001, 1.0 + .00001), 2, innerapprox=True) == (1, 2) assert x.box2indexwindow((-1.0 - .00001, 1.0 + .00001), 2, innerapprox=False) == (0, 3) # BDD creation assert x.conc2pred(mgr, "x", (.4, .6), 1, True) == mgr.false args = [mgr, "x", (.4, .6), 1, True] args = [mgr, "x", (-.34, .65), 4, True] assert x.box2indexwindow(*args[2:]) == (7, 9) pspace = DynamicCover(-2, 2) assert pspace.box2indexwindow((0, .8), 6, False) == (32, 44) # Over and under approximations of boxes that align exactly with the grid are the same assert pspace.conc2pred(mgr, 'x', (-1, 1), 4, innerapprox=False) == pspace.conc2pred( mgr, 'x', (-1, 1), 4, innerapprox=True) assert pspace.conc2pred(mgr, 'x', (-1.5, 1.5), 4, innerapprox=False) == pspace.conc2pred( mgr, 'x', (-1.5, 1.5), 4, innerapprox=True)
def test_discrete(): mgr = BDD() x = DiscreteSet(5) x.conc2pred(mgr, 'x', 2) # Declares variables x_0, x_1, x_2 in manager mgr.declare("x_0", "x_1", "x_2") x0 = mgr.var("x_0") x1 = mgr.var("x_1") x2 = mgr.var("x_2") assert x.conc2pred(mgr, 'x', 2) == ~x0 & x1 & ~x2 assert x.abs_space(mgr, 'x') == (x0 & ~x1 & ~x2) | ~x0 with raises(AssertionError): x.conc2pred(mgr, 'x', -1)
def test_fixed_periodic(): mgr = BDD() y = FixedCover(0, 10, 5, periodic=True) # 5 bins assert set(y.box2bvs((3, 7), False)) == {(False, False, True), (False, True, False), (False, True, True)} assert set(y.box2bvs((3, 7), True)) == {(False, True, False)} # Inner-outer tests assert set(y.box2bvs((3, 3.5), True)) == set([]) assert set(y.box2bvs((3, 3.5), False)) == {(False, False, True)} assert set(y.box2bvs((3, 5), True)) == set([]) # Wrap around tests assert set(y.box2bvs((9, 1), True)) == set([]) assert set(y.box2bvs((9, 2.1), True)) == {(False, False, False)} assert set(y.box2bvs((9, 2.1), False)) == {(True, False, False), (False, False, False), (False, False, True)} z = FixedCover(0, 10, 5, periodic=True) mgr.declare("z_0", "z_1", "z_2") assert y == z assert z.box2indexwindow((9.9, .1), innerapprox=True) is None assert z.box2indexwindow((1.9, 2.1), innerapprox=True) is None assert z.box2indexwindow((9.9, .1), innerapprox=False) == (4, 0) assert z.box2indexwindow((4.4, 4.3), innerapprox=False) == (3, 2) assert z.box2indexwindow((9.9, 9.8), innerapprox=False) == (0, 4) z0 = mgr.var("z_0") z1 = mgr.var("z_1") z2 = mgr.var("z_2") assert z.conc2pred(mgr, 'z', (4.4, 4.3), innerapprox=False) == ~z0 | (z0 & ~z1 & ~z2) assert z.box2indexwindow((19.9, 19.8), innerapprox=False) == (0, 4) # Over and under approximations of boxes that align exactly with the grid are the same assert z.conc2pred(mgr, 'z', (0, 4), innerapprox=True) == z.conc2pred(mgr, 'z', (0, 4), innerapprox=False)
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 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 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_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_embedded_grid(): x = EmbeddedGrid(21, 10, 50) assert x.num_bits == 5 assert x.pt2index(24) == 7 # (24-10)/2 assert x.pt2index(22.9, snap=True) == 6 assert x.pt2index(25.1, snap=True) == 8 mgr = BDD() assert (mgr.count(x.abs_space(mgr, 'x'), 5)) == 21 with raises(ValueError): x.pt2index(23) with raises(ValueError): EmbeddedGrid(0, 40, 50) with raises(ValueError): EmbeddedGrid(3, 50, 40) with raises(ValueError): EmbeddedGrid(1, 40, 50) # Snapping to nearest from out of range assert x.pt2index(9, snap=True) == 0 assert x.pt2index(60, snap=True) == 20 EmbeddedGrid(1, 10, 10)
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
from pytest import approx import numpy as np import funcy as fn from redax.module import Interface, CompositeInterface from redax.spaces import DynamicCover from redax.synthesis import ControlPre, DecompCPre, ReachGame, SafetyGame, ReachAvoidGame from redax.visualizer import scatter2D, plot3D, plot3D_QT, pixel2D from redax.utils.overapprox import bloatbox from redax.predicates.dd import BDD mgr = BDD() mgr.configure(reordering=True) 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})
def test_dynamic_periodic(): mgr = BDD() x = DynamicCover(0, 20, periodic=True) # assert x.pt2bv(11, 4) == (True, True, False, False) # assert x.pt2bv(19+20, 4) == (True, False, False, False) # Wrap around assert set(x.box2bvs((17, 7), 2, innerapprox=True)) == {(False, False)} assert set(x.box2bvs((17, 7), 2, innerapprox=False)) == {(True, False), (False, False), (False, True)} mgr.declare("x_0", "x_1", "x_2") x0 = mgr.var("x_0") x1 = mgr.var("x_1") x2 = mgr.var("x_2") assert x.conc2pred(mgr, 'x', (1, 19), 3) == mgr.true # assert x.conc2pred(mgr, 'x', (19,1), 3, True) == mgr.false assert x.conc2pred(mgr, 'x', (0, 9.9), 3, False) == ~x0 assert x.conc2pred(mgr, 'x', (0, 9.9), 3, True) == (~x0 & ~x1) | (~x0 & x1 & x2) assert x.box2indexwindow((.1, 19), 3, False) == (0, 7) assert x.box2indexwindow((.1, 19), 3, True) == (1, 6) assert x.box2indexwindow((19, 1), 3, True) is None assert x.box2indexwindow((19, 1), 3, False) == (7, 0) assert x.box2indexwindow((.1, 1), 3, False) == (0, 0) assert x.box2indexwindow((.1, 1), 3, True) is None assert x.box2indexwindow((1, .1), 3, True) == (1, 7) assert x.box2indexwindow((9.7, 29.5), 3, True) == (4, 2) assert x.box2indexwindow((1.80, 21.1), 1, True) == (1, 1) assert x.box2indexwindow((16.2, 31), 3, True) == (7, 3) assert x.box2indexwindow((19.9, .1), 3, innerapprox=True) is None assert x.box2indexwindow((2.4, 2.6), 3, innerapprox=True) is None # wrap around with overapproximation assert x.box2indexwindow((19.9, .1), 3, innerapprox=False) == (7, 0) assert x.box2indexwindow((39.9, 20.1), 3, innerapprox=False) == (7, 0) assert x.box2indexwindow((9.9, 9.8), 3, innerapprox=False) == (4, 3) assert x.box2indexwindow((29.9, 29.8), 3, innerapprox=False) == (4, 3) assert x.box2indexwindow((29.9, 9.8), 3, innerapprox=False) == (4, 3) assert x.box2indexwindow((19.9, 19.8), 3, innerapprox=False) == (0, 7) # total cover assert x.box2indexwindow((39.9, 39.8), 3, innerapprox=False) == (0, 7) # total cover # Over and under approximations of boxes that align exactly with the grid are the same assert x.conc2pred(mgr, 'x', (5, 15), 4, innerapprox=True) == x.conc2pred(mgr, 'x', (5, 15), 4, innerapprox=False) assert x.conc2pred(mgr, 'x', (0, 5), 4, innerapprox=True) == x.conc2pred(mgr, 'x', (0, 5), 4, innerapprox=False) assert x.conc2pred(mgr, 'x', (15, 5), 4, innerapprox=True) == x.conc2pred(mgr, 'x', (15, 5), 4, innerapprox=False) assert x.conc2pred(mgr, 'x', (0, 20), 4, innerapprox=True) == x.conc2pred(mgr, 'x', (20, 40), 4, innerapprox=False) """
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_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()
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)