Esempio n. 1
0
    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]
Esempio n. 2
0
def test_never_false_redemption():
    spec = LTL.atom('x').historically()
    monitor = BV.aig2aigbv(spec.aig)
    assert len(monitor.latches) == 1
    monitor = monitor['l', {fn.first(monitor.latches): 'z'}]

    # Environment can save you.
    x, y = BV.uatom(1, 'x'), BV.uatom(1, 'y')
    xy = (x | y).with_output('x')  # env y can override x.


    dyn = C.pcirc(xy.aigbv) \
           .randomize({'y': {0: 0.75, 1: 0.25}})

    horizon = 3
    model = from_pcirc(dyn, monitor, steps=horizon)

    coeff = np.log(2)  # Special coeff to make LSE visit powers of 2.
    actor = improviser(model, coeff)

    v8 = coeff
    v7 = 0
    v6 = coeff / 4
    v5 = np.logaddexp(v6, coeff)
    v4 = (np.log(8) + v5) / 4
    v3 = np.logaddexp(v4, v5)
    v2 = (3 * np.log(4) + v3) / 4
    v1 = np.logaddexp(v2, v3)

    expected = sorted([v8, v7, v6, v5, v4, v3, v2, v1])
    expected = fn.lmap(pytest.approx, expected)

    vals = sorted(list(actor.node2val.values()))
    assert all(x == y for x, y in zip(vals, expected))

    def lprob(elems):
        return actor.prob(elems, log=True)

    assert lprob([]) == 0
    assert lprob([1]) == pytest.approx(v3 - v1)

    for prefix in [[1], [1, 0, 1], [1, 1, 1], [1, 1, 1, 1, 1]]:
        for bit in [0, 1]:
            expected = pytest.approx(-np.log(4) + bit * np.log(3))
        assert lprob(prefix + [bit]) - lprob(prefix) == expected

    ctrl = actor.policy()
    example = []
    for env in [None, 0, 0]:
        example.append(ctrl.send(env))
    assert -float('inf') < lprob(example)

    ctrl = actor.policy(observe_states=True)
    example = []
    for env in [None, ({'x': 1}, None), ({'x': 1}, None)]:
        example.append(ctrl.send(env))
    assert -float('inf') < lprob(example)
Esempio n. 3
0
def test_never_false():
    spec = LTL.atom('x').historically()
    monitor = BV.aig2aigbv(spec.aig)
    dyn = C.pcirc(BV.identity_gate(1, 'x'))

    horizon = 3
    model = from_pcirc(dyn, monitor, steps=horizon)
    graph = model.graph()
    assert len(graph.nodes) == horizon + 2
    assert len(graph.edges) == 2 * horizon
Esempio n. 4
0
def test_never_false_redemption():
    spec = LTL.atom('x').historically()
    monitor = BV.aig2aigbv(spec.aig)

    # Environment can save you.
    x, y = BV.uatom(1, 'x'), BV.uatom(1, 'y')
    xy = (x | y).with_output('x')  # env y can override x.

    dyn = C.pcirc(xy.aigbv) \
           .randomize({'y': {0: 0.4, 1: 0.6}})

    horizon = 3
    model = from_pcirc(dyn, monitor, steps=horizon)
    graph = model.graph()
    assert len(graph.nodes) == 2 * horizon + 2
    assert len(graph.edges) == 4 * horizon
Esempio n. 5
0
def test_never_false1():
    spec = LTL.atom('x').historically()
    monitor = BV.aig2aigbv(spec.aig)
    dyn = C.pcirc(BV.identity_gate(1, 'x'))

    horizon = 4
    model = from_pcirc(dyn, monitor, steps=horizon)

    coeff = np.log(2)
    actor = model.improviser(rationality=coeff)

    expected = [
        0,
        coeff,
        np.log(1 + 2),
        np.log(3 + 2),
        np.log(7 + 2),
        np.log(15 + 2),
    ]
    # LSE visits powers of 2 for the special coeff.
    expected = fn.lmap(pytest.approx, expected)

    vals = sorted(list(actor.node2val.values()))
    assert all(x == y for x, y in zip(expected, vals))

    expected = sorted([
        np.log(9) - np.log(17),
        np.log(8) - np.log(17),
    ])
    expected = fn.lmap(pytest.approx, expected)

    def lprob(elems):
        return actor.prob(elems, log=True)

    assert lprob([]) == 0
    assert lprob([1]) == pytest.approx(np.log(9) - np.log(17))
    assert lprob([1, 1]) == pytest.approx(np.log(5) - np.log(17))
    assert lprob([1, 1, 1]) == pytest.approx(np.log(3) - np.log(17))
    assert lprob([1, 1, 1, 1]) == pytest.approx(coeff - np.log(17))

    # ----------- Fail on first state ---------------------
    base = np.log(8) - np.log(17)
    assert lprob([0]) == pytest.approx(base)
    # Uniform after failing.
    assert lprob([0, 1]) == pytest.approx(base - np.log(2))
    assert lprob([0, 0]) == pytest.approx(base - np.log(2))
    assert lprob([0, 0, 0]) == pytest.approx(base - np.log(4))
    assert lprob([0, 0, 1]) == pytest.approx(base - np.log(4))

    # ----------- Fail on second state ---------------------
    base = np.log(4) - np.log(17)
    assert lprob([1, 0]) == pytest.approx(base)
    assert lprob([1, 0, 0]) == pytest.approx(base - np.log(2))
    assert lprob([1, 0, 1]) == pytest.approx(base - np.log(2))
    assert lprob([1, 0, 0, 0]) == pytest.approx(base - np.log(4))
    assert lprob([1, 1, 0, 1]) == pytest.approx(base - np.log(4))

    with pytest.raises(ValueError):
        lprob([1, 1, 1, 1, 1, 1])

    example = list(actor.sample())
    assert -float('inf') < lprob(example)

    ctrl = actor.policy()
    example = []
    for env in [None, 0, 0, 0]:
        example.append(ctrl.send(env))
    assert -float('inf') < lprob(example)

    actor = model.improviser(psat=0.7)
    assert actor.sat_prob() == pytest.approx(0.7)
Esempio n. 6
0
def _translate_destinations(data: dict, ctx: AutomatonContext) -> set[str]:
    """

    :param data: Describes the destinations.
    :param ctx:
    :return:
    """
    # Create coin flipping part.
    if len(data) == 1:
        pass
    else:
        probs = []
        for d in data:
            if "probability" not in d:
                probs.append(1)
            else:
                probability = d["probability"]["exp"]
                probs.append(_parse_prob(probability))
        prob_input = ctx.register_distribution(probs)
        # TODO consider what to do with the additional output of prob_input

    vars_written_to = set()
    for d in data:
        for a in d["assignments"]:
            vars_written_to.add(a["ref"])

    destinations = []
    for index, d in enumerate(data):
        assert d["location"] == "l"
        updates = {}
        # TODO add location handling
        for assignment in d["assignments"]:
            var_name = assignment["ref"]
            var_primed = var_name
            if len(data) > 1:  # TODO: why make this case special?
                var_primed += f"-{index}"

            val = assignment["value"]
            updates[var_primed] = _translate_expression(val, ctx.scope) \
                .resize(ctx.scope.get_aig_variable(var_name).size) \
                .with_output(var_primed) \
                .aigbv

        for var in vars_written_to:
            var_primed = var
            if len(data) > 1:
                var_primed += f"-{index}"  # TODO: why make this case special.
            if var_primed not in updates:
                updates[var_primed] = ctx.scope \
                                         .get_aig_variable(var) \
                                         .with_output(var_primed) \
                                         .aigbv

        update = par_compose(updates.values())
        destinations.append(update)

    edge_circuit = par_compose(destinations)

    vars_written_to = list(vars_written_to)
    if len(destinations) > 1:
        indices = range(len(destinations))

        def selectors():
            for var in vars_written_to:
                size = ctx.scope.get_aig_variable(var).size
                outputs = [BV.uatom(size, f"{var}-{idx}") for idx in indices]
                yield mux(outputs, key_name='sel').with_output(var).aigbv

        edge_circuit >>= par_compose(selectors())
        edge_circuit <<= prob_input
    else:
        edge_circuit = C.pcirc(edge_circuit)  # Deterministic pcirc.
    return edge_circuit, vars_written_to
Esempio n. 7
0
def test_from_pcirc_smoke():
    sys = BV.uatom(3, 'sys')
    env = BV.uatom(3, 'env')
    out = BV.uatom(3, 'out')

    monitor = out < 4

    dyn = (sys + env).with_output('out')
    dyn = C.pcirc(dyn) \
           .randomize({'env': {0: 1/3, 1: 1/3, 5: 1/3}}) \
           .with_coins_id('coins')

    model = from_pcirc(dyn, monitor, steps=2)
    assert model.coin_biases('sys##time_0') is None

    coins0 = f'coins##time_0'
    assert model.coin_biases(coins0) == dyn.coin_biases

    assert model.mdd({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 0,
        'coins##time_1': 0b11,  # <- env == 0
    }) == 1

    assert model.mdd({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 5,
        'coins##time_1': 0b11,  # <- env == 0
    }) == 0

    sum_zero0 = BV.uatom(3, f'out##time_1') == 0b000
    expr2 = model.preimage(sum_zero0)

    assert expr2({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 0,
        'coins##time_1': 0b11,  # <- env == 0
    })[0]

    assert expr2({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 0,
        'coins##time_1': 0b00,  # <- env != 0
    })[0]

    assert not expr2({
        'sys##time_0': 0,
        'coins##time_0': 0b00,  # <- env != 0
        'sys##time_1': 0,
        'coins##time_1': 0b00,  # <- env != 0
    })[0]

    model2 = model.override(sum_zero0, False)

    assert model2.mdd({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 0,
        'coins##time_1': 0b11,  # <- env == 0
    }) == 0

    assert model2.mdd({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 3,
        'coins##time_1': 0b11,  # <- env == 0
    }) == 0

    assert model2.mdd({
        'sys##time_0': 0,
        'coins##time_0': 0b11,  # <- env == 0
        'sys##time_1': 5,
        'coins##time_1': 0b11,  # <- env == 0
    }) == 0

    graph = model.graph()
    graph2 = model2.graph()
    assert len(graph2) >= len(graph)  # Now need to case split at 0.

    assert not model.is_random('sys##time_0')
    assert model.is_random('coins##time_0')

    assert model.time_step('foo##time_0') == 0
    assert model.time_step('foo##time_1') == 1
    assert model.time_step(False) == 2

    guard = fn.first(graph.edges(data=True))[-1]['label']
    assert model.size(guard) > 0

    expr = model.mdd.io.inputs[1].expr() == 0
    assert model.prob(expr) == pytest.approx(1 / 6)