Esempio n. 1
0
def calculate_accuracy(domain, target_formula, learned_formula):
    # from sys import path
    # path.insert(0, "/Users/samuelkolb/Documents/PhD/wmi-pa/experiments/client")
    # from run import compute_wmi
    print("Calculate accuracy:")
    # print(pretty_print(target_formula))
    # print(pretty_print(learned_formula))

    # r0, r1 = [smt.Symbol(n, smt.REAL) for n in ["r0", "r1"]]
    # b0, b1, b2, b3 = [smt.Symbol(n, smt.BOOL) for n in ["b0", "b1", "b2", "b3"]]
    # t1 = (~(1.0 <= 0.427230115861 * r0 + 1.02084935803 * r1) | ~(1.0 <= 1.59402729715 * r0 + 0.309004054118 * r1) | ~b1)
    # t2 = (b2 | (1.0 <= 1.59402729715 * r0 + 0.309004054118 * r1) | ~b0)

    # domain = problem.Domain(["x", "y"], {"x": smt.REAL, "y": smt.REAL}, {"x": (0, 1), "y": (0, 1)})
    # x, y = smt.Symbol("x", smt.REAL), smt.Symbol("y", smt.REAL)
    # t2 = (1.0 <= 1.5 * x + 0.5 * y)
    # t2 = (2 <= 3 * x + y)
    # f = (t1 & t2)

    flat = {
        "domain": problem.export_domain(domain, False),
        "query": parse.smt_to_nested(smt.Iff(target_formula, learned_formula))
    }

    print(domain)
    print(pretty_print(target_formula))
    print(pretty_print(learned_formula))
    # accuracy = list(compute_wmi(domain, [smt.Iff(target_formula, learned_formula)]))[0]

    output = str(subprocess.check_output(["/Users/samuelkolb/Documents/PhD/wmi-pa/env/bin/python",
                                     "/Users/samuelkolb/Documents/PhD/wmi-pa/experiments/client/run.py", "-s",
                                     json.dumps(flat)]))
    accuracy = float(output.split(": ")[1])
    print(accuracy)
    return accuracy
def test_xadd_iff_real():
    domain = Domain.make([], ["x", "y"], real_bounds=(-1, 1))
    x, y = domain.get_symbols()
    c = 0.00000001
    f1 = (x * c > 0) & (x * c <= y * c) & (y * c < c)
    f2 = normalize_formula(f1)

    xadd_vol1 = XaddEngine(domain, (f1 | f2) & (~f1 | ~f2),
                           smt.Real(1.0)).compute_volume()
    xadd_vol2 = XaddEngine(domain, smt.Iff(f1, ~f2),
                           smt.Real(1.0)).compute_volume()
    xadd_vol3 = XaddEngine(domain, ~smt.Iff(f1, f2),
                           smt.Real(1.0)).compute_volume()

    assert xadd_vol1 == pytest.approx(0, EXACT_REL_ERROR)
    assert xadd_vol2 == pytest.approx(0, EXACT_REL_ERROR)
    assert xadd_vol3 == pytest.approx(0, EXACT_REL_ERROR)
def test_rejection_iff_real():
    domain = Domain.make([], ["x", "y"], real_bounds=(-1, 1))
    x, y = domain.get_symbols()
    c = 0.00000001
    f1 = (x * c > 0) & (x * c <= y * c) & (y * c < c)
    f2 = normalize_formula(f1)

    rej_vol1 = RejectionEngine(domain, (f1 | f2) & (~f1 | ~f2), smt.Real(1.0),
                               100000).compute_volume()
    rej_vol2 = RejectionEngine(domain, smt.Iff(f1, ~f2), smt.Real(1.0),
                               100000).compute_volume()
    rej_vol3 = RejectionEngine(domain, ~smt.Iff(f1, f2), smt.Real(1.0),
                               100000).compute_volume()

    assert rej_vol1 == pytest.approx(0, REL_ERROR**3)
    assert rej_vol2 == pytest.approx(0, REL_ERROR**3)
    assert rej_vol3 == pytest.approx(0, REL_ERROR**3)
Esempio n. 4
0
def generate_click_graph(n):
    def t(c):
        return smt.Ite(c, one, zero)

    sim_n, cl_n, b_n, sim_x_n, b_x_n = "sim", "cl", "b", "sim_x", "b_x"
    domain = Domain.make(
        # Boolean
        ["{}_{}".format(sim_n, i) for i in range(n)] +
        ["{}_{}_{}".format(cl_n, i, j) for i in range(n) for j in (0, 1)] +
        ["{}_{}_{}".format(b_n, i, j) for i in range(n) for j in (0, 1)],
        # Real
        ["{}".format(sim_x_n)] +
        ["{}_{}_{}".format(b_x_n, i, j) for i in range(n) for j in (0, 1)],
        real_bounds=(0, 1))
    sim = [domain.get_symbol("{}_{}".format(sim_n, i)) for i in range(n)]
    cl = [[domain.get_symbol("{}_{}_{}".format(cl_n, i, j)) for j in (0, 1)]
          for i in range(n)]
    b = [[domain.get_symbol("{}_{}_{}".format(b_n, i, j)) for j in (0, 1)]
         for i in range(n)]
    sim_x = domain.get_symbol("{}".format(sim_x_n))
    b_x = [[domain.get_symbol("{}_{}_{}".format(b_x_n, i, j)) for j in (0, 1)]
           for i in range(n)]

    support = smt.And([
        smt.Iff(cl[i][0], b[i][0])
        & smt.Iff(cl[i][1], (sim[i] & b[i][0]) | (~sim[i] & b[i][1]))
        for i in range(n)
    ])

    one = smt.Real(1)
    zero = smt.Real(0)
    w_sim_x = t(sim_x >= 0) * t(sim_x <= 1)
    w_sim = [smt.Ite(s_i, sim_x, 1 - sim_x) for s_i in sim]
    w_b_x = [
        t(b_x[i][j] >= 0) * t(b_x[i][j] <= 1) for i in range(n) for j in (0, 1)
    ]
    w_b = [
        smt.Ite(b[i][j], b_x[i][j], 1 - b_x[i][j]) for i in range(n)
        for j in (0, 1)
    ]

    weight = smt.Times(*([w_sim_x] + w_sim + w_b_x + w_b))
    return Density(domain, support, weight)
def test_rejection_iff_bool():
    domain = Domain.make(["a", "b"])
    a, b = domain.get_symbols()

    print(domain)
    vol_t = RejectionEngine(domain, smt.TRUE(), smt.Real(1.0),
                            100000).compute_volume()
    vol1 = RejectionEngine(domain, (a | b) & (~a | ~b), smt.Real(1.0),
                           100000).compute_volume()
    vol2 = RejectionEngine(domain, smt.Iff(a, ~b), smt.Real(1.0),
                           100000).compute_volume()
    vol3 = RejectionEngine(domain, ~smt.Iff(a, b), smt.Real(1.0),
                           100000).compute_volume()

    print(vol1, vol2, vol3, vol_t)

    # print(PredicateAbstractionEngine(domain, a | b, smt.Real(1.0)).compute_volume())
    print(XaddEngine(domain, a | b, smt.Real(1.0)).compute_volume())

    quit()
Esempio n. 6
0
def test_pa_iff_real():
    pytest.skip("Bug fix requires changing PA solver")
    domain = Domain.make([], ["x", "y"], real_bounds=(-1, 1))
    x, y = domain.get_symbols()
    c = 0.00000001
    f1 = (x * c >= 0) & (x * c <= y * c) & (y * c < c)
    f2 = normalize_formula(f1)
    print(smt_to_nested(f2))

    pa_vol1 = PredicateAbstractionEngine(
        domain,
        domain.get_bounds() & (f1 | f2) & (~f1 | ~f2),
        smt.Real(1.0)).compute_volume()
    smt.write_smtlib(domain.get_bounds() & (f1 | f2) & (~f1 | ~f2),
                     "test_pa_iff_real.support")
    smt.write_smtlib(smt.Real(1.0), "test_pa_iff_real.weight")
    pa_vol2 = PredicateAbstractionEngine(domain, smt.Iff(f1, ~f2),
                                         smt.Real(1.0)).compute_volume()
    pa_vol3 = PredicateAbstractionEngine(domain, ~smt.Iff(f1, f2),
                                         smt.Real(1.0)).compute_volume()

    assert pa_vol1 == pytest.approx(0, REL_ERROR**3)
    assert pa_vol2 == pytest.approx(0, REL_ERROR**3)
    assert pa_vol3 == pytest.approx(0, REL_ERROR**3)
Esempio n. 7
0
def test_convert_support():
    x, y = smt.Symbol("x", smt.REAL), smt.Symbol("y", smt.REAL)
    a = smt.Symbol("a", smt.BOOL)
    formula = (x < 0) | (~a & (x < -1)) | smt.Ite(a, x < 4, x < 8)
    # Convert formula into abstracted one (replacing inequalities)
    env, repl_formula, literal_info = extract_and_replace_literals(formula)
    result = compile_to_sdd(formula=repl_formula,
                            literals=literal_info,
                            vtree=None)
    recovered = recover_formula(sdd_node=result,
                                literals=literal_info,
                                env=env)
    # print(pretty_print(recovered))
    with smt.Solver() as solver:
        solver.add_assertion(~smt.Iff(formula, recovered))
        # print(pretty_print(formula))
        # print(pretty_print(recovered))
        assert not solver.solve(
        ), f"Expected UNSAT but found model {solver.get_model()}"
Esempio n. 8
0
 def accuracy_approx(experiment):
     key = "accuracy_approx:{}".format(experiment.imported_from_file)
     if Properties.db.exists(key):
         return Properties.db.get(key)
     else:
         pysmt.environment.push_env()
         pysmt.environment.get_env().enable_infix_notation = True
         if os.path.basename(experiment.imported_from_file).startswith("synthetic"):
             db = Properties.get_db_synthetic(experiment)
             name = Properties.to_synthetic_name(experiment.imported_from_file)
             entry = db.get(name)
             domain = import_domain(json.loads(entry["domain"]))
             true_formula = nested_to_smt(entry["formula"])
         else:
             density = Density.import_from(experiment.parameters.original_values["domain"])
             domain = Domain(density.domain.variables, density.domain.var_types, Properties.get_bound(experiment))
             true_formula = density.support
         learned_formula = nested_to_smt(experiment.results.formula)
         engine = RejectionEngine(domain, smt.TRUE(), smt.Real(1.0), 100000)
         accuracy = engine.compute_probability(smt.Iff(true_formula, learned_formula))
         pysmt.environment.pop_env()
         print(accuracy)
         Properties.db.set(key, accuracy)
     return accuracy
Esempio n. 9
0
def find_cnf(data, domain, active_indices, solver, n_c, n_h):
    # Constants
    n_b_original = len(domain.bool_vars)
    n_b = n_b_original * 2
    n_r = len(domain.real_vars)
    n_d = len(data)

    real_features = [[row[v] for v in domain.real_vars] for row, _ in data]
    bool_features = [[row[v] for v in domain.bool_vars] for row, _ in data]
    labels = [row[1] for row in data]

    # Variables
    a_hr = [[
        smt.Symbol("a_hr[{}][{}]".format(h, r), REAL) for r in range(n_r)
    ] for h in range(n_h)]
    b_h = [smt.Symbol("b_h[{}]".format(h), REAL) for h in range(n_h)]
    s_ch = [[smt.Symbol("s_ch[{}][{}]".format(c, h)) for h in range(n_h)]
            for c in range(n_c)]
    s_cb = [[smt.Symbol("s_cb[{}][{}]".format(c, b)) for b in range(n_b)]
            for c in range(n_c)]

    # Aux variables
    s_ih = [[smt.Symbol("s_ih[{}][{}]".format(i, h)) for h in range(n_h)]
            for i in range(n_d)]
    s_ic = [[smt.Symbol("s_ic[{}][{}]".format(i, c)) for c in range(n_c)]
            for i in range(n_d)]

    # Constraints
    for i in active_indices:
        x_r, x_b, label = real_features[i], bool_features[i], labels[i]

        for h in range(n_h):
            sum_coefficients = smt.Plus(
                [a_hr[h][r] * smt.Real(x_r[r]) for r in range(n_r)])
            if label:
                solver.add_assertion(
                    smt.Iff(s_ih[i][h], sum_coefficients + DELTA <= b_h[h]))
            else:
                solver.add_assertion(
                    smt.Iff(s_ih[i][h], sum_coefficients - DELTA <= b_h[h]))

        for c in range(n_c):
            solver.add_assertion(
                smt.Iff(
                    s_ic[i][c],
                    smt.Or([smt.FALSE()] + [(s_ch[c][h] & s_ih[i][h])
                                            for h in range(n_h)] +
                           [s_cb[c][b]
                            for b in range(n_b_original) if x_b[b]] + [
                                s_cb[c][b] for b in range(n_b_original, n_b)
                                if not x_b[b - n_b_original]
                            ])))

        if label:
            solver.add_assertion(smt.And([s_ic[i][c] for c in range(n_c)]))
        else:
            solver.add_assertion(smt.Or([~s_ic[i][c] for c in range(n_c)]))

    if not solver.solve():
        return None
    model = solver.get_model()

    x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)]
    half_spaces = [
        smt.Plus([model.get_value(a_hr[h][r]) * x_vars[r]
                  for r in range(n_r)]) <= model.get_value(b_h[h])
        for h in range(n_h)
    ]

    b_vars = [
        domain.get_symbol(domain.bool_vars[b]) for b in range(n_b_original)
    ]
    bool_literals = [b_vars[b] for b in range(n_b_original)]
    bool_literals += [~b_vars[b] for b in range(n_b - n_b_original)]

    conjunctions = [
        [half_spaces[h]
         for h in range(n_h) if model.get_py_value(s_ch[c][h])] + [
             bool_literals[b]
             for b in range(n_b) if model.get_py_value(s_cb[c][b])
         ] for c in range(n_c)
    ]

    return smt.And([smt.Or(conjunction) for conjunction in conjunctions])
Esempio n. 10
0
    def learn_partial(self, solver, domain, data, new_active_indices):
        # Constants
        n_b_original = len(domain.bool_vars)
        n_b = n_b_original * 2
        n_r = len(domain.real_vars)

        n_h_original = self.half_space_count if n_r > 0 else 0
        n_h = n_h_original * 2 if self.allow_negations else n_h_original

        n_c = self.conjunction_count
        n_d = len(data)

        real_features = [[row[v] for v in domain.real_vars] for row, _ in data]
        bool_features = [[row[v] for v in domain.bool_vars] for row, _ in data]
        labels = [row[1] for row in data]

        # Variables
        a_hr = [[
            smt.Symbol("a_hr[{}][{}]".format(h, r), REAL) for r in range(n_r)
        ] for h in range(n_h_original)]
        b_h = [
            smt.Symbol("b_h[{}]".format(h), REAL) for h in range(n_h_original)
        ]
        s_ch = [[smt.Symbol("s_ch[{}][{}]".format(c, h)) for h in range(n_h)]
                for c in range(n_c)]
        s_cb = [[smt.Symbol("s_cb[{}][{}]".format(c, b)) for b in range(n_b)]
                for c in range(n_c)]

        # Aux variables
        s_ih = [[smt.Symbol("s_ih[{}][{}]".format(i, h)) for h in range(n_h)]
                for i in range(n_d)]
        s_ic = [[smt.Symbol("s_ic[{}][{}]".format(i, c)) for c in range(n_c)]
                for i in range(n_d)]

        # Constraints
        for i in new_active_indices:
            x_r, x_b, label = real_features[i], bool_features[i], labels[i]

            for h in range(n_h_original):
                sum_coefficients = smt.Plus(
                    [a_hr[h][r] * smt.Real(x_r[r]) for r in range(n_r)])
                solver.add_assertion(
                    smt.Iff(s_ih[i][h], sum_coefficients <= b_h[h]))

            for h in range(n_h_original, n_h):
                solver.add_assertion(
                    smt.Iff(s_ih[i][h], ~s_ih[i][h - n_h_original]))

            for c in range(n_c):
                solver.add_assertion(
                    smt.Iff(
                        s_ic[i][c],
                        smt.And([smt.TRUE()] + [(~s_ch[c][h] | s_ih[i][h])
                                                for h in range(n_h)] +
                                [
                                    ~s_cb[c][b]
                                    for b in range(n_b_original) if not x_b[b]
                                ] + [
                                    ~s_cb[c][b]
                                    for b in range(n_b_original, n_b)
                                    if x_b[b - n_b_original]
                                ])))

            if label:
                solver.add_assertion(smt.Or([s_ic[i][c] for c in range(n_c)]))
            else:
                solver.add_assertion(smt.And([~s_ic[i][c]
                                              for c in range(n_c)]))

        solver.solve()
        model = solver.get_model()

        x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)]
        half_spaces = [
            smt.Plus(
                [model.get_value(a_hr[h][r]) * x_vars[r]
                 for r in range(n_r)]) <= model.get_value(b_h[h])
            for h in range(n_h_original)
        ] + [
            smt.Plus(
                [model.get_value(a_hr[h][r]) * x_vars[r]
                 for r in range(n_r)]) > model.get_value(b_h[h])
            for h in range(n_h - n_h_original)
        ]

        b_vars = [
            domain.get_symbol(domain.bool_vars[b]) for b in range(n_b_original)
        ]
        bool_literals = [b_vars[b] for b in range(n_b_original)]
        bool_literals += [~b_vars[b] for b in range(n_b - n_b_original)]

        conjunctions = [[
            half_spaces[h]
            for h in range(n_h) if model.get_py_value(s_ch[c][h])
        ] + [
            bool_literals[b]
            for b in range(n_b) if model.get_py_value(s_cb[c][b])
        ] for c in range(n_c)]

        return smt.Or([smt.And(conjunction) for conjunction in conjunctions])
Esempio n. 11
0
 def __ne__(self, other: 'SMTBit') -> 'SMTBit':
     return type(self)(smt.Not(smt.Iff(self.value, other.value)))
Esempio n. 12
0
 def order_equal(pair1, pair2):
     x_t, x_f, y_t, y_f = pair1 + pair2
     return smt.Iff(x_t, y_t) & smt.Iff(x_f, y_f)
Esempio n. 13
0
    def learn_partial(self, solver, domain: Domain, data: np.ndarray,
                      labels: np.ndarray, new_active_indices: Set):

        # Constants
        n_b_original = len(domain.bool_vars)
        n_b = n_b_original * 2
        n_r = len(domain.real_vars)

        n_h_original = self.half_space_count if n_r > 0 else 0
        n_h = n_h_original * 2 if self.allow_negations else n_h_original

        n_c = self.conjunction_count
        n_d = data.shape[0]

        real_indices = np.array(
            [domain.var_types[v] == smt.REAL for v in domain.variables])
        real_features = data[:, real_indices]
        bool_features = data[:, np.logical_not(real_indices)]

        # Variables
        a_hr = [[
            smt.Symbol("a_hr[{}][{}]".format(h, r), REAL) for r in range(n_r)
        ] for h in range(n_h_original)]
        b_h = [
            smt.Symbol("b_h[{}]".format(h), REAL) for h in range(n_h_original)
        ]
        s_ch = [[smt.Symbol("s_ch[{}][{}]".format(c, h)) for h in range(n_h)]
                for c in range(n_c)]
        s_cb = [[smt.Symbol("s_cb[{}][{}]".format(c, b)) for b in range(n_b)]
                for c in range(n_c)]

        # Aux variables
        s_ih = [[smt.Symbol("s_ih[{}][{}]".format(i, h)) for h in range(n_h)]
                for i in range(n_d)]
        s_ic = [[smt.Symbol("s_ic[{}][{}]".format(i, c)) for c in range(n_c)]
                for i in range(n_d)]

        def pair(real: bool, c: int, index: int) -> Tuple[FNode, FNode]:
            if real:
                return s_ch[c][index], s_ch[c][index + n_h_original]
            else:
                return s_cb[c][index], s_cb[c][index + n_b_original]

        def order_equal(pair1, pair2):
            x_t, x_f, y_t, y_f = pair1 + pair2
            return smt.Iff(x_t, y_t) & smt.Iff(x_f, y_f)

        def order_geq(pair1, pair2):
            x_t, x_f, y_t, y_f = pair1 + pair2
            return x_t | y_f | ((~x_f) & (~y_t))

        def pairs(c: int) -> List[Tuple[FNode, FNode]]:
            return [pair(True, c, i) for i in range(n_h_original)
                    ] + [pair(False, c, i) for i in range(n_b_original)]

        def order_geq_lex(c1: int, c2: int):
            pairs_c1, pairs_c2 = pairs(c1), pairs(c2)
            assert len(pairs_c1) == len(pairs_c2)
            constraints = smt.TRUE()
            for j in range(len(pairs_c1)):
                condition = smt.TRUE()
                for i in range(j):
                    condition &= order_equal(pairs_c1[i], pairs_c2[i])
                constraints &= smt.Implies(condition,
                                           order_geq(pairs_c1[j], pairs_c2[j]))
            return constraints

        # Constraints
        for i in new_active_indices:
            x_r, x_b, label = [float(val) for val in real_features[i]
                               ], bool_features[i], labels[i]

            for h in range(n_h_original):
                sum_coefficients = smt.Plus(
                    [a_hr[h][r] * smt.Real(x_r[r]) for r in range(n_r)])
                solver.add_assertion(
                    smt.Iff(s_ih[i][h], sum_coefficients <= b_h[h]))

            for h in range(n_h_original, n_h):
                solver.add_assertion(
                    smt.Iff(s_ih[i][h], ~s_ih[i][h - n_h_original]))

            for c in range(n_c):
                solver.add_assertion(
                    smt.Iff(
                        s_ic[i][c],
                        smt.
                        Or([smt.FALSE()] + [(s_ch[c][h] & s_ih[i][h])
                                            for h in range(n_h)] +
                           [s_cb[c][b]
                            for b in range(n_b_original) if x_b[b]] + [
                                s_cb[c][b] for b in range(n_b_original, n_b)
                                if not x_b[b - n_b_original]
                            ])))

            # --- [start] symmetry breaking ---
            # Mutually exclusive
            if "m" in self.symmetries:
                for c in range(n_c):
                    for h in range(n_h_original):
                        solver.add_assertion(~(s_ch[c][h]
                                               & s_ch[c][h + n_h_original]))
                    for b in range(n_b_original):
                        solver.add_assertion(~(s_cb[c][b]
                                               & s_cb[c][b + n_b_original]))

            # Normalized
            if "n" in self.symmetries:
                for h in range(n_h_original):
                    solver.add_assertion(
                        smt.Equals(b_h[h], smt.Real(1.0))
                        | smt.Equals(b_h[h], smt.Real(0.0)))

            # Vertical symmetries
            if "v" in self.symmetries:
                for c in range(n_c - 1):
                    solver.add_assertion(order_geq_lex(c, c + 1))

            # Horizontal symmetries
            if "h" in self.symmetries:
                for h in range(n_h_original - 1):
                    solver.add_assertion(a_hr[h][0] >= a_hr[h + 1][0])
            # --- [end] symmetry breaking ---

            if label:
                solver.add_assertion(smt.And([s_ic[i][c] for c in range(n_c)]))
            else:
                solver.add_assertion(smt.Or([~s_ic[i][c] for c in range(n_c)]))

        solver.solve()
        model = solver.get_model()

        x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)]
        half_spaces = [
            smt.Plus(
                [model.get_value(a_hr[h][r]) * x_vars[r]
                 for r in range(n_r)]) <= model.get_value(b_h[h])
            for h in range(n_h_original)
        ] + [
            smt.Plus(
                [model.get_value(a_hr[h][r]) * x_vars[r]
                 for r in range(n_r)]) > model.get_value(b_h[h])
            for h in range(n_h - n_h_original)
        ]

        b_vars = [
            domain.get_symbol(domain.bool_vars[b]) for b in range(n_b_original)
        ]
        bool_literals = [b_vars[b] for b in range(n_b_original)]
        bool_literals += [~b_vars[b] for b in range(n_b - n_b_original)]

        conjunctions = [[
            half_spaces[h]
            for h in range(n_h) if model.get_py_value(s_ch[c][h])
        ] + [
            bool_literals[b]
            for b in range(n_b) if model.get_py_value(s_cb[c][b])
        ] for c in range(n_c)]

        return smt.And([smt.Or(conjunction) for conjunction in conjunctions])
Esempio n. 14
0
    def initialize(self, mdp, colors, hole_options, reward_name, okay_states,
                   target_states, threshold, relation):
        logger.warning("This approach has been tested sparsely.")
        prob0E, prob1A = stormpy.compute_prob01min_states(
            mdp, okay_states, target_states)
        sink_states = ~okay_states

        assert len(mdp.initial_states) == 1
        self.state_vars = [
            smt.Symbol("p_{}".format(i), smt.REAL)
            for i in range(mdp.nr_states)
        ]
        self.state_prob1_vars = [
            smt.Symbol("asure_{}".format(i)) for i in range(mdp.nr_states)
        ]
        self.state_probpos_vars = [
            smt.Symbol("x_{}".format(i)) for i in range(mdp.nr_states)
        ]
        self.state_order_vars = [
            smt.Symbol("r_{}".format(i), smt.REAL)
            for i in range(mdp.nr_states)
        ]
        self.option_vars = dict()
        for h, opts in hole_options.items():
            self.option_vars[h] = {
                index: smt.Symbol("h_{}_{}".format(h, opt))
                for index, opt in enumerate(opts)
            }
        self.transition_system = []
        logger.debug("Obtain rewards if necessary")

        rewards = mdp.reward_models[reward_name] if reward_name else None
        if rewards:
            assert not rewards.has_transition_rewards
            state_rewards = rewards.has_state_rewards
            action_rewards = rewards.has_state_action_rewards
            logger.debug(
                "Model has state rewards: {}, has state/action rewards {}".
                format(state_rewards, action_rewards))

            self.transition_system.append(
                self.state_prob1_vars[mdp.initial_states[0]])

        threshold_inequality, action_constraint_inequality = self._to_smt_relation(
            relation)  # TODO or GE
        self.transition_system.append(
            threshold_inequality(self.state_vars[mdp.initial_states[0]],
                                 smt.Real(float(threshold))))

        state_order_domain_constraint = smt.And([
            smt.And(smt.GE(var, smt.Real(0)), smt.LE(var, smt.Real(1)))
            for var in self.state_order_vars
        ])
        self.transition_system.append(state_order_domain_constraint)
        #TODO how to ensure that prob is zero if there is no path.

        select_parameter_value_constraints = []
        for h, opts in self.option_vars.items():
            select_parameter_value_constraints.append(
                smt.Or(ov for ov in opts.values()))
            for i, opt1 in enumerate(opts.values()):
                for opt2 in list(opts.values())[i + 1:]:
                    select_parameter_value_constraints.append(
                        smt.Not(smt.And(opt1, opt2)))
        #logger.debug("Consistency: {}".format(smt.And(select_parameter_value_constraints)))
        self.transition_system.append(
            smt.And(select_parameter_value_constraints))

        for state in mdp.states:
            if sink_states.get(state.id):
                assert rewards is None
                self.transition_system.append(
                    smt.Equals(self.state_vars[state.id], smt.REAL(0)))
                #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                self.transition_system.append(
                    smt.Not(self.state_prob1_vars[state.id]))
                #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                self.transition_system.append(
                    smt.Equals(self.state_order_vars[state.id], smt.Real(0)))
                #logger.debug("Constraint: {}".format(self.transition_system[-1]))
            elif target_states.get(state.id):
                self.transition_system.append(
                    smt.Equals(self.state_order_vars[state.id], smt.Real(1)))
                #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                self.transition_system.append(self.state_prob1_vars[state.id])
                #logger.debug("Constraint: {}".format(self.transition_system[-1]))

                if rewards is None:
                    self.transition_system.append(
                        smt.Equals(self.state_vars[state.id], smt.Real(1)))
                    #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                else:
                    self.transition_system.append(
                        self.state_probpos_vars[state.id])
                    #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                    self.transition_system.append(
                        smt.Equals(self.state_vars[state.id], smt.Real(0)))
                    #logger.debug("Constraint: {}".format(self.transition_system[-1]))
            else:
                state_in_prob1A = False
                state_in_prob0E = False
                if prob0E.get(state.id):
                    state_in_prob0E = True
                else:
                    self.transition_system.append(
                        smt.Equals(self.state_order_vars[state.id],
                                   smt.Real(1)))
                    #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                    self.transition_system.append(
                        self.state_probpos_vars[state.id])
                    #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                if rewards and not state_in_prob0E:
                    if prob1A.get(state.id):
                        self.transition_system.append(
                            self.state_prob1_vars[state.id])
                        logger.debug("Constraint: {}".format(
                            self.transition_system[-1]))
                        state_in_prob1A = True

                for action in state.actions:
                    action_index = mdp.nondeterministic_choice_indices[
                        state.id] + action.id
                    #logger.debug("Action index: {}".format(action_index))
                    precondition = smt.And([
                        self.option_vars[hole][list(option)[0]] for hole,
                        option in colors.get(action_index, dict()).items()
                    ])
                    reward_value = None
                    if rewards:
                        reward_const = (rewards.get_state_reward(
                            state.id) if state_rewards else 0.0) + (
                                rewards.get_state_action_reward(action_index)
                                if action_rewards else 0.0)
                        reward_value = smt.Real(reward_const)
                    act_constraint = action_constraint_inequality(
                        self.state_vars[state.id],
                        smt.Plus([
                            smt.Times(smt.Real(t.value()), self.
                                      state_vars[t.column])
                            for t in action.transitions
                        ] + [reward_value] if reward_value else []))
                    full_act_constraint = act_constraint
                    if state_in_prob0E:
                        if not rewards:
                            full_act_constraint = smt.And(
                                smt.Implies(self.state_probpos_vars[state.id],
                                            act_constraint),
                                smt.Implies(
                                    smt.Not(self.state_probpos_vars),
                                    smt.Equals(self.state_vars[state.id],
                                               smt.Real(0))))
                        self.transition_system.append(
                            smt.Implies(
                                precondition,
                                smt.Iff(
                                    self.state_probpos_vars[state.id],
                                    smt.Or([
                                        smt.And(
                                            self.state_probpos_vars[t.column],
                                            smt.LT(
                                                self.state_order_vars[
                                                    state.id],
                                                self.state_order_vars[
                                                    t.column]))
                                        for t in action.transitions
                                    ]))))
                        #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                    if rewards and not state_in_prob1A:
                        # prob_one(state) <-> probpos AND for all succ prob_one(succ)
                        self.transition_system.append(
                            smt.Implies(
                                precondition,
                                smt.Iff(
                                    self.state_prob1_vars[state.id],
                                    smt.And([
                                        self.state_prob1_vars[t.column]
                                        for t in action.transitions
                                    ] + [self.state_probpos_vars[state.id]]))))
                        #logger.debug("Constraint: {}".format(self.transition_system[-1]))
                    self.transition_system.append(
                        smt.Implies(precondition, full_act_constraint))
                    #logger.debug("Constraint: {}".format(self.transition_system[-1]))

        if rewards:
            self.transition_system.append(
                smt.And([smt.GE(sv, smt.Real(0)) for sv in self.state_vars]))
        else:
            self.transition_system.append(
                smt.And([
                    smt.And(smt.GE(sv, smt.Real(0)), smt.LE(sv, smt.Real(1)))
                    for sv in self.state_vars
                ]))

        #print(self.transition_system)
        formula = smt.And(self.transition_system)
        logger.info("Start SMT solver")
        model = smt.get_model(formula)

        if model:
            logger.info("SAT: Found {}".format(model))
        else:
            logger.info("UNSAT.")