def test_discrete_wrapper_smoke(): x = BV.uatom(3, 'x') circ = (x + 1).with_output('z') \ .aigbv func = from_aigbv(circ) assert func({'x': 6})[0] == {'z': 7} func2 = from_aigbv( circ, input_encodings={'x': NEG_ENC}, output_encodings={'z': NEG_ENC}, ) assert func2({'x': -2})[0] == {'z': -3} valid = (x <= 2).with_output('##valid') \ .aigbv func3 = from_aigbv( circ | valid, input_encodings={'x': NEG_ENC}, output_encodings={'z': NEG_ENC}, ) assert func3({'x': -2})[0] == {'z': -3} with pytest.raises(ValueError): func3({'x': -3}) assert func3.inputs == {'x'} assert func3.outputs == {'z'} assert func3.latches == set() assert func3.latch2init == {}
def test_parallel_composition(): x = BV.uatom(3, 'x') circ1 = (x + 1).with_output('y').aigbv \ | (x < 5).with_output('##valid').aigbv func1 = from_aigbv( circ1, input_encodings={'x': INT_ENC}, output_encodings={'y': INT_ENC}, ) circ2 = x.with_output('z').aigbv \ | (x > 2).with_output('##valid').aigbv func2 = from_aigbv( circ2, input_encodings={'x': INT_ENC}, output_encodings={'z': INT_ENC}, ) func12 = func1 | func2 assert func12({'x': 3})[0] == {'y': 4, 'z': 3} with pytest.raises(ValueError): func12({'x': 0}) with pytest.raises(ValueError): func12({'x': 7})
def register_distribution(self, probs): """ Register a distribution :param probs: The distribution as a list of probabilities :return: A pcirc that generates a binary-encoded output sel that corresponds to sampling from the distribution. """ dist = tuple(probs) if dist not in self._distributions: name = f"{self._aut_name}_c{len(self._distributions)}" sel = atom(len(probs), name).with_output("sel") lookup = bidict({idx: f'sel-{idx}' for idx in range(len(probs))}) encoder = D.Encoding(decode=lookup.get, encode=lookup.inv.get) func = D.from_aigbv(sel.aigbv, input_encodings={name: encoder}) name2prob = { name: {f"sel-{index}": prob for index, prob in enumerate(probs)} } coins_id = f"{self._aut_name}_c{len(self._distributions)}" self._distributions[dist] = C.pcirc(func) \ .randomize(name2prob) \ .with_coins_id(coins_id) return self._distributions[dist]
def test_readme(): # Will assume inputs are in 'A', 'B', 'C', 'D', or 'E'. ascii_encoder = Encoding( decode=lambda x: chr(x + ord('A')), # Make 'A' map to 0. encode=lambda x: ord(x) - ord('A'), ) # Create function which maps: A -> B, B -> C, C -> D, D -> E. x = BV.uatom(3, 'x') # Need 3 bits to capture 5 input types. update_expr = (x < 4).repeat(3) & (x + 1) # 0 if x < 4 else x + 1. circ = update_expr.with_output('y').aigbv # Need to assert that the inputs are less than 4. circ |= (x < 5).with_output('##valid').aigbv # Wrap using aiger_discrete. func = from_aigbv( circ, input_encodings={'x': ascii_encoder}, output_encodings={'y': ascii_encoder}, valid_id='##valid', ) assert func({'x': 'A'})[0] == {'y': 'B'} assert func({'x': 'B'})[0] == {'y': 'C'} assert func({'x': 'C'})[0] == {'y': 'D'} assert func({'x': 'D'})[0] == {'y': 'E'} assert func({'x': 'E'})[0] == {'y': 'A'}
def test_relabel(): x = BV.uatom(3, 'x') circ1 = (x + 1).with_output('y').aigbv \ | (x < 5).with_output('##valid').aigbv func1 = from_aigbv(circ1,) assert func1['i', {'x': 'z'}].inputs == {'z'} assert func1['o', {'y': 'z'}].outputs == {'z'} assert func1['i', {'x': 'z'}].valid_id == func1.valid_id
def test_seq_composition(): x = BV.atom(4, 'x') y = BV.atom(4, 'y') circ1 = (x + 1).with_output('y').aigbv \ | (x < 5).with_output('##valid').aigbv func1 = from_aigbv(circ1,) circ2 = (y - 1).with_output('z').aigbv \ | (y > 2).with_output('##valid').aigbv func2 = from_aigbv(circ2,) func12 = func1 >> func2 assert func12({'x': 4})[0] == {'z': 4} with pytest.raises(ValueError): func12({'x': 1}) func12 = func2 << func1 assert func12({'x': 4})[0] == {'z': 4} with pytest.raises(ValueError): func12({'x': 1})
def onehot_gadget(output: str): sat = BV.uatom(1, output) false, true = BV.uatom(2, 0b01), BV.uatom(2, 0b10) expr = BV.ite(sat, true, false) \ .with_output('sat') encoder = D.Encoding( encode=lambda x: 1 << int(x), decode=lambda x: bool((x >> 1) & 1), ) return D.from_aigbv( expr.aigbv, output_encodings={'sat': encoder}, )
def test_readme_mdd(): # Will assume inputs are in 'A', 'B', 'C', 'D', or 'E'. ascii_encoder = Encoding( decode=lambda x: chr(x + ord('A')), # Make 'A' map to 0. encode=lambda x: ord(x) - ord('A'), ) one_hot_ascii_encoder = Encoding( decode=lambda x: ascii_encoder.decode(ONE_HOT.inv[x]), encode=lambda x: ONE_HOT[ascii_encoder.encode(x)], ) # Create function which maps: A -> B, B -> C, C -> D, D -> E. x = BV.uatom(3, 'x') # Need 3 bits to capture 5 input types. update_expr = (x < 4).repeat(3) & (x + 1) # 0 if x < 4 else x + 1. circ = update_expr.with_output('y').aigbv circ |= (x < 5).with_output('##valid').aigbv one_hot_converter = BV.lookup(3, 5, ONE_HOT, 'y', 'y', in_signed=False, out_signed=False) circ >>= one_hot_converter func_circ = from_aigbv( circ, input_encodings={'x': ascii_encoder}, output_encodings={'y': one_hot_ascii_encoder}, valid_id='##valid', ) assert func_circ({'x': 'A'})[0] == {'y': 'B'} assert func_circ({'x': 'B'})[0] == {'y': 'C'} assert func_circ({'x': 'C'})[0] == {'y': 'D'} assert func_circ({'x': 'D'})[0] == {'y': 'E'} assert func_circ({'x': 'E'})[0] == {'y': 'A'} func_mdd = to_mdd(func_circ) assert func_mdd({'x': 'A'})[0] == 'B' assert func_mdd({'x': 'B'})[0] == 'C' assert func_mdd({'x': 'C'})[0] == 'D' assert func_mdd({'x': 'D'})[0] == 'E' assert func_mdd({'x': 'E'})[0] == 'A'
def test_loopback_and_unroll(): x = BV.uatom(3, 'x') y = BV.uatom(3, 'y') circ1 = (x + y).with_output('y').aigbv \ | (x < 7).with_output('##valid').aigbv func1 = from_aigbv(circ1,) func2 = func1.loopback({ 'input': 'x', 'output': 'y', 'keep_output': True, 'init': 0, }) assert func2.simulate([{'y': 1}, {'y': 1}])[-1][0] == {'y': 2} with pytest.raises(ValueError): assert func2.simulate([{'y': 1}]*10) func3 = func2.unroll(2, only_last_outputs=True) assert func3({'y##time_0': 0, 'y##time_1': 1})[0] == {'y##time_2': 1} with pytest.raises(ValueError): assert func3({'y##time_0': 7, 'y##time_1': 0})
def gridworld(n, start=(None, None), compressed_inputs=False): # Gridworld is 2 synchronized chains. circ = chain(n, 'x', 'ax', start[1]) | chain(n, 'y', 'ay', start[0]) circ <<= split_gate('a', 2, 'ay', 2, 'ax') # Combine inputs. x = BV.uatom(circ.omap['x'].size, 'x') y = BV.uatom(circ.omap['y'].size, 'y') circ >>= y.concat(x).with_output('state').aigbv # Combine outputs. if compressed_inputs: uncompress = lookup(2, 4, COMPRESSION_MAPPING, 'a', 'a', in_signed=False, out_signed=False) circ <<= uncompress # Wrap using aiger discrete add encoding + valid inputs. actions_map = ACTIONS_C if compressed_inputs else ACTIONS action_encoding = aiger_discrete.Encoding( encode=actions_map.get, decode=actions_map.inv.get, ) state_encoding = aiger_discrete.Encoding( encode=lambda s: s.yx, decode=lambda yx: G.GridState(yx, n), ) func = aiger_discrete.from_aigbv( circ, input_encodings={'a': action_encoding}, output_encodings={'state': state_encoding}, ) if not compressed_inputs: action = BV.uatom(4, 'a') is_1hot = (action != 0) & ((action & (action - 1)) == 0) func = func.assume(is_1hot) return func
def to_finite_func(circ) -> FiniteFunc: if isinstance(circ, FiniteFunc): return circ return aiger_discrete.from_aigbv(circ.aigbv)
def test_rename_valid(): func = from_aigbv(BV.uatom(3, 'x').aigbv).rename_valid('foo') assert 'foo' in func.circ.outputs assert 'foo' == func.valid_id