def search_in_node(data, domain, node, h): if node.solvable == False: return None if node.literals is None: neg_data_gen = node.to_gen() possible_pos_lits = domain.bool_vars.copy() possible_neg_lits = domain.bool_vars.copy() for i in neg_data_gen: assert(not data[i][1]) possible_pos_lits = [l for l in possible_pos_lits if not data[i][0][l]] possible_neg_lits = [l for l in possible_neg_lits if data[i][0][l]] if not possible_neg_lits and not possible_pos_lits: break node.literals = [domain.get_symbol(b) for b in possible_pos_lits] + [~domain.get_symbol(b) for b in possible_neg_lits] pos_data = [i for i in range(len(data)) if data[i][1]] for l in possible_pos_lits: pos_data = [i for i in pos_data if not data[i][0][l]] for l in possible_neg_lits: pos_data = [i for i in pos_data if data[i][0][l]] if pos_data: node.next_level_node = build_hierarchy(data, domain, pos_data, False) #else it stays None if node.next_level_node is None: return smt.Or(node.literals) q = PriorityQueue() q.put(node.next_level_node) solutions = [] neg_idx = list(node.to_gen()) while not q.empty(): if len(solutions) + len(q.queue) > h: return None n = q.get() if n.clause is not None: solutions += [n.clause] continue if n.hl_min > 1: if n.left is None: node.solvable = False return None q.put(n.left) q.put(n.right) continue with smt.Solver() as solver: solution = find_iter(data, domain, list(n.to_gen()) + neg_idx,find_hl, solver) if solution is None: n.hl_min = 2 if n.left is None: #print("unsatisfiable node!") return None q.put(n.left) q.put(n.right) else: solutions += [solution] n.clause = solution saturate(data, domain, n) return smt.Or(solutions + node.literals)
def generate_strict_cnf(domain, and_count, or_count, half_space_count): half_spaces = [generate_half_space_sample(domain, len(domain.real_vars)) for _ in range(half_space_count)] candidates = [domain.get_symbol(v) for v in domain.bool_vars] + half_spaces candidates += [smt.Not(c) for c in candidates] formulas = [] iteration = 0 max_iterations = 100 * and_count while len(formulas) < and_count: if iteration >= max_iterations: return generate_strict_cnf(domain, and_count, or_count, half_space_count) iteration += 1 formula_candidates = [c for c in candidates] random.shuffle(formula_candidates) formula = [] try: while len(formula) < or_count: next_term = formula_candidates.pop(0) if len(formula) == 0 or smt.is_sat(~smt.Or(*formula) & next_term): formula.append(next_term) except IndexError: continue if len(formulas) == 0 or smt.is_sat(~smt.And(*[smt.Or(*f) for f in formulas]) & smt.Or(*formula)): formulas.append(formula) return smt.And(*[smt.Or(*f) for f in formulas])
def get_term(self, literal_pool): from bitarray import bitarray print("Generate term: ", end="") for i in range(self.max_tries): literals = random.sample(literal_pool, self.l) term = smt.Or(*list(zip(*literals))[0]) covered = bitarray(self.sample_count) covered.setall(False) significant_literals = 0 for _, bits in literals: prev_size = covered.count() covered |= bits if (covered.count() - prev_size) / self.sample_count >= 0.05: significant_literals += 1 if significant_literals == self.l: # & test_ratio(covered): print("y", end="") print() return term, covered else: print("x", end="") print(" Failed after {} tries".format(self.max_tries)) raise GeneratorError()
def ast_to_smt(self, node): """ :type node: Node """ def convert_children(number=None): if number is not None and len(node.children) != number: raise Exception( "The number of children ({}) differed from {}".format( len(node.children), number)) return [self.ast_to_smt(child) for child in node.children] if node.name == "ite": return smt.Ite(*convert_children(3)) elif node.name == "~": return smt.Not(*convert_children(1)) elif node.name == "^": return smt.Pow(*convert_children(2)) elif node.name == "&": return smt.And(*convert_children()) elif node.name == "|": return smt.Or(*convert_children()) elif node.name == "*": return smt.Times(*convert_children()) elif node.name == "+": return smt.Plus(*convert_children()) elif node.name == "-": return smt.Minus(*convert_children(2)) elif node.name == "<=": return smt.LE(*convert_children(2)) elif node.name == ">=": return smt.GE(*convert_children(2)) elif node.name == "<": return smt.LT(*convert_children(2)) elif node.name == ">": return smt.GT(*convert_children(2)) elif node.name == "=": return smt.Equals(*convert_children(2)) elif node.name == "const": c_type, c_value = [child.name for child in node.children] if c_type == "bool": return smt.Bool(bool(c_value)) elif c_type == "real": return smt.Real(float(c_value)) else: raise Exception("Unknown constant type {}".format(c_type)) elif node.name == "var": v_type, v_name = [child.name for child in node.children] if v_type == "bool": v_smt_type = smt.BOOL elif v_type == "real": v_smt_type = smt.REAL else: raise Exception("Unknown variable type {}".format(v_type)) return smt.Symbol(v_name, v_smt_type) else: raise RuntimeError("Unrecognized node type '{}'".format(node.name))
def dual(n): n = 2*n domain = Domain.make([], ["x{}".format(i) for i in range(n)], real_bounds=(0, 1)) x_vars = domain.get_symbols() terms = [x_vars[2 * i] <= x_vars[2 * i + 1] for i in range(int(n / 2))] disjunction = smt.Or(*terms) for i in range(len(terms)): for j in range(i + 1, len(terms)): disjunction &= ~terms[i] | ~terms[j] return FileDensity(domain, disjunction & domain.get_bounds(), smt.Real(1))
def check_example(domain, example_features, dnf_list): x_vars = [domain.get_symbol(var) for var in domain.real_vars] b_vars = [domain.get_symbol(var) for var in domain.bool_vars] formula = smt.Or( [smt.And(hyperplane_conjunct) for hyperplane_conjunct in dnf_list]) substitution = { var: example_features[str(var)] for var in x_vars + b_vars } return formula.substitute(substitution).simplify().is_true()
def generate_mutual_exclusive(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] disjunction = smt.Or(*terms) for i in range(n): for j in range(i + 1, n): disjunction &= ~terms[i] | ~terms[j] return Density(flip_domain(domain), bounds & disjunction, smt.Real(1.0))
def dual_paths(n): booleans = [] # ["A{}".format(i) for i in range(n)] domain = Domain.make(booleans, ["x{}".format(i) for i in range(n)], real_bounds=(0, 1)) bool_vars = domain.get_bool_symbols() real_vars = domain.get_real_symbols() terms = [] for i in range(n): v1, v2 = random.sample(real_vars, 2) terms.append(v1 * random.random() <= v2 * random.random()) paths = [] for i in range(n): paths.append(smt.And(*random.sample(bool_vars + terms, n))) return Density(domain, domain.get_bounds() & smt.Or(*paths), smt.Real(1))
def mutual_exclusive(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] disjunction = smt.TRUE() for i in range(n): for j in range(i + 1, n): disjunction &= ~terms[i] | ~terms[j] disjunction = smt.simplify(disjunction) & smt.Or(*terms) flipped_domain = Domain(list(reversed([v for v in domain.variables if v != "x"])) + ["x"], domain.var_types, domain.var_domains) return FileDensity(flipped_domain, disjunction & bounds, smt.Real(1.0))
def get_term(self, literal_pool): print("Generate term: ", end="") for i in range(self.max_tries): literals = random.sample(literal_pool, self.l) term = smt.Or(*list(zip(*literals))[0]) covered = np.zeros(self.sample_count) significant_literals = 0 for _, labels in literals: prev_size = sum(covered) covered = np.logical_or(covered, labels) if (sum(covered) - prev_size) / self.sample_count >= 0.05: significant_literals += 1 if significant_literals == self.l: # & test_ratio(covered): print("y", end="") print() return term, covered else: print("x", end="") print(" Failed after {} tries".format(self.max_tries)) raise GeneratorError()
def compute_volume_from_pieces( self, base_support, piecewise_function, labeling_dict ): # Prepare the support for each piece (not compiled yet) term_supports = multimap() for piece_weight, piece_support in piecewise_function.pieces.items(): logger.debug( "piece with weight %s and support %s", piece_weight, piece_support ) for term in piece_weight.get_terms(): term_supports[term].add(piece_support) terms_dict = { term: smt.Or(*supports) & base_support for term, supports in term_supports.items() } volume = self.algebra.zero() for i, (term, support) in enumerate(terms_dict.items()): logger.debug("----- Term %s -----", term) repl_env, logic_support, literals = extract_and_replace_literals(support) literals.labels = labeling_dict vtree = self.get_vtree(support, literals) support_sdd = self.get_sdd(logic_support, literals, vtree) # TODO # if logger.getEffectiveLevel() == logging.DEBUG: # filename = f"sdd_{i}.dot" # sdd_to_dot_file(support_sdd, literals, filename, node_to_groups) # logger.debug(f"saved SDD of piece {i} to {filename}") subvolume = self.compute_volume_for_piece(term, literals, support_sdd) volume = self.algebra.plus(volume, subvolume) return volume
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])
def walk_implies(self, left, right): return self.walk_smt(smt.Or(smt.Not(left), right))
def walk_or(self, args): return smt.Or(*self.walk_smt_multiple(args))
def generate_synthetic_data_sampling(data_sets_per_setting, bool_count, real_count, cnf_or_dnf, k, l_per_term, h, sample_count, max_ratio, seed, prefix="synthetics"): def test_ratio(_indices): return (1 - max_ratio) * sample_count <= len(_indices) <= max_ratio * sample_count data_set_count = 0 domain = generate_domain(bool_count, real_count) while data_set_count < data_sets_per_setting: name = get_synthetic_problem_name(prefix, bool_count, real_count, cnf_or_dnf, k, l_per_term, h, sample_count, seed, max_ratio * 100, data_set_count) samples = [get_sample(domain) for _ in range(sample_count)] half_spaces = [] print("Generating half spaces: ", end="") if real_count > 0: while len(half_spaces) < h: half_space = generate_half_space_sample(domain, real_count) indices = {i for i in range(sample_count) if smt_test(half_space, samples[i])} if True or test_ratio(indices): half_spaces.append((half_space, indices)) print("y", end="") else: print("x", end="") print() bool_literals = [(domain.get_symbol(v), {i for i in range(sample_count) if samples[i][v]}) for v in domain.bool_vars] literal_pool = half_spaces + bool_literals literal_pool += [(smt.Not(l), {i for i in range(sample_count)} - indices) for l, indices in literal_pool] random.shuffle(literal_pool) term_pool = [] print("Generating terms: ", end="") for literals in itertools.combinations(literal_pool, l_per_term): term = smt.Or(*[t[0] for t in literals]) covered = set() all_matter = True for _, indices in literals: prev_size = len(covered) covered |= indices if (len(covered) - prev_size) / sample_count < 0.05: all_matter = False if all_matter: # & test_ratio(covered): term_pool.append((term, covered)) print("y", end="") else: print("x", end="") print() print("Generating formulas: ", end="") random.shuffle(term_pool) counter = 0 max_tries = 10000 for terms in itertools.combinations(term_pool, k): if counter >= max_tries: print("Restart") break formula = smt.And(*[t[0] for t in terms]) covered = {i for i in range(sample_count)} all_matter = True for _, indices in terms: prev_size = len(covered) covered &= indices if prev_size == len(covered): all_matter = False if all_matter & test_ratio(covered): print("y({:.2f})".format(len(covered) / sample_count), end="") synthetic_problem = SyntheticProblem(problem.Problem(domain, formula, name), "cnf", k, l_per_term, h) data_set = synthetic_problem.get_data(sample_count, 1) new_sample_positives = [sample for sample in data_set.samples if sample[1]] if test_ratio(new_sample_positives): print("c({:.2f})".format(len(new_sample_positives) / sample_count)) data_set_count += 1 yield data_set break else: print("r({:.2f})".format(len(new_sample_positives) / sample_count), end="") else: print("x", end="") print(" ", end="") counter += 1 print()
def prove_properties(junctions): width = max(max(y1 for _, y1, _ in junctions), max(y2 for _, _, y2 in junctions)) + 1 length = max(x for x, _, _ in junctions) formula, belts = balancer_flow_formula(junctions, width, length) supply = s.Plus(beltway[0].rho for beltway in belts) demand = s.Plus(beltway[-1].v for beltway in belts) max_theoretical_throughput = s.Min(supply, demand) actual_throughput = s.Plus(beltway[-1].flux for beltway in belts) fully_balanced_output = [] max_flux = s.Max(b[-1].flux for b in belts) for i, b1 in enumerate(belts): fully_balanced_output.append( s.Or(s.Equals(b1[-1].flux, max_flux), s.Equals(b1[-1].flux, b1[-1].v))) fully_balanced_output = s.And(fully_balanced_output) fully_balanced_input = [] max_flux = s.Max(b[0].flux for b in belts) for i, b1 in enumerate(belts): fully_balanced_input.append( s.Or(s.Equals(b1[0].flux, max_flux), s.Equals(b1[0].flux, b1[0].rho))) fully_balanced_input = s.And(fully_balanced_input) with s.Solver() as solver: solver.add_assertion(s.And(formula)) solver.push() solver.add_assertion( s.Not(s.Equals(max_theoretical_throughput, actual_throughput))) if not solver.solve(): print("It's throughput-unlimited!") else: print("It's throughput-limited; here's an example:") m = solver.get_model() inputs = tuple(m.get_py_value(beltway[0].rho) for beltway in belts) outputs = tuple(m.get_py_value(beltway[-1].v) for beltway in belts) print(f'Input lane densities: ({", ".join(map(str, inputs))})') print(f'Output lane velocities: ({", ".join(map(str, outputs))})') check_balancer_flow(junctions, inputs, outputs) solver.pop() solver.push() solver.add_assertion(s.Not(fully_balanced_input)) if not solver.solve(): print("It's input-balanced!") else: print("It's input-imbalanced; here's an example:") m = solver.get_model() inputs = tuple(m.get_py_value(beltway[0].rho) for beltway in belts) outputs = tuple(m.get_py_value(beltway[-1].v) for beltway in belts) print(f'Input lane densities: ({", ".join(map(str, inputs))})') print(f'Output lane velocities: ({", ".join(map(str, outputs))})') check_balancer_flow(junctions, inputs, outputs) solver.pop() solver.push() solver.add_assertion(s.Not(fully_balanced_output)) if solver.solve: print("It's output-balanced!") else: print("It's output-imbalanced; here's an example:") m = solver.get_model() inputs = tuple(m.get_py_value(beltway[0].rho) for beltway in belts) outputs = tuple(m.get_py_value(beltway[-1].v) for beltway in belts) print(f'Input lane densities: ({", ".join(map(str, inputs))})') print(f'Output lane velocities: ({", ".join(map(str, outputs))})') check_balancer_flow(junctions, inputs, outputs)
def generate_dnf(domain, or_count, and_count, half_space_count): formulas = get_formulas(domain, and_count, or_count, half_space_count) return smt.Or(smt.And(*formula) for formula in formulas)
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.")
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])
def __getExpressionTree(symbolicExpression): # TODO LATER: take into account bitwise shift operations args = [] castType = None if len(symbolicExpression.args) > 0: for symbolicArg in symbolicExpression.args: arg, type = Solver.__getExpressionTree(symbolicArg) args.append(arg) if castType is None: castType = type else: if castType.literal == 'Integer': if type.literal == 'Real': castType = type # TODO LATER: consider other possible castings if castType.literal == 'Real': for i in range(len(args)): args[i] = pysmt.ToReal(args[i]) if isinstance(symbolicExpression, sympy.Not): if castType.literal == 'Integer': return pysmt.Equals(args[0], pysmt.Int(0)), Type('Bool') elif castType.literal == 'Real': return pysmt.Equals(args[0], pysmt.Real(0)), Type('Bool') elif castType.literal == 'Bool': return pysmt.Not(args[0]), Type('Bool') else: # castType.literal == 'BitVector' return pysmt.BVNot(args[0]), Type('BitVector') elif isinstance(symbolicExpression, sympy.Lt): return pysmt.LT(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Gt): return pysmt.GT(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Ge): return pysmt.GE(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Le): return pysmt.LE(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Eq): return pysmt.Equals(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Ne): return pysmt.NotEquals(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.And): if castType.literal == 'Bool': return pysmt.And(args[0], args[1]), Type('Bool') else: # type.literal == 'BitVector' return pysmt.BVAnd(args[0], args[1]), castType elif isinstance(symbolicExpression, sympy.Or): if castType.literal == 'Bool': return pysmt.Or(args[0], args[1]), Type('Bool') else: # type.literal == 'BitVector' return pysmt.BVOr(args[0], args[1]), castType elif isinstance(symbolicExpression, sympy.Xor): return pysmt.BVXor(args[0], args[1]), castType elif isinstance(symbolicExpression, sympy.Add): return pysmt.Plus(args), castType elif isinstance(symbolicExpression, sympy.Mul): return pysmt.Times(args), castType elif isinstance(symbolicExpression, sympy.Pow): return pysmt.Pow(args[0], args[1]), castType # TODO LATER: deal with missing modulo operator from pysmt else: if isinstance(symbolicExpression, sympy.Symbol): symbolType = Variable.symbolTypes[symbolicExpression.name] literal = symbolType.getTypeForSolver() designator = symbolType.designatorExpr1 type = Type(literal, designator) return Solver.__encodeTerminal(symbolicExpression, type), type elif isinstance(symbolicExpression, sympy.Integer): type = Type('Integer') return Solver.__encodeTerminal(symbolicExpression, type), type elif isinstance(symbolicExpression, sympy.Rational): type = Type('Real') return Solver.__encodeTerminal(symbolicExpression, type), type elif isinstance(symbolicExpression, sympy.Float): type = Type('Real') return Solver.__encodeTerminal(symbolicExpression, type), type else: type = Type('Real') return Solver.__encodeTerminal(symbolicExpression, type), type
def to_smt(self): return sc.Or(self._folA.to_smt(), self._folB.to_smt())
def __or__(self, other: 'SMTBit') -> 'SMTBit': return type(self)(smt.Or(self.value, other.value))
print(variables) constraints = [] for var in variables: string = var.__repr__() # add requirement that all a_i, c_i > -1 if string[0] == "a" or string[0] == "c": constraints.append(SMT.GT(var, SMT.Int(-1))) # add requirement that b_i > 0 else: constraints.append(SMT.GT(var, SMT.Int(0))) for i in range(0, 3 * cap, 3): # add requirement that either a_i =0 or c_i = 0 constraints.append( SMT.Or(SMT.Equals(variables[i], SMT.Int(0)), SMT.Equals(variables[i + 2], SMT.Int(0)))) # add requirement that if a_i = 0 and c_i = 0 then b_i = 1 constraints.append( SMT.Or( SMT.Or(SMT.GT(variables[i], SMT.Int(0)), SMT.GT(variables[i + 2], SMT.Int(0))), SMT.Equals(variables[i + 1], SMT.Int(1)))) solver = SMT.Solver(name="z3") print("constraints:") for c in constraints: print(c) solver.add_assertion(c) # add equations
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])
def smt_to_nested(expression): # type: (FNode) -> str """ Converts an smt expression to a nested formula :param expression: An SMT expression (FNode) :return: A string representation (lisp-style) Functional operators can be: &, |, *, +, <=, <, ^ Variables are represented as (var type name) Constants are represented as (const type name) Types can be: real, int, bool """ def convert_children(op): return "({} {})".format( op, " ".join(smt_to_nested(arg) for arg in expression.args())) def format_type(smt_type): if smt_type == smt.REAL: return "real" if smt_type == smt.INT: return "int" if smt_type == smt.BOOL: return "bool" raise RuntimeError("No type corresponding to {}".format(smt_type)) if expression.is_and(): return convert_children("&") if expression.is_or(): return convert_children("|") if expression.node_type() == IMPLIES: return smt_to_nested( smt.Or(smt.Not(expression.args()[0]), expression.args()[1])) if expression.is_iff(): return smt_to_nested( smt.And(smt.Implies(expression.args()[0], expression.args()[1]), smt.Implies(expression.args()[1], expression.args()[0]))) if expression.is_not(): return convert_children("~") if expression.is_times(): return convert_children("*") if expression.is_plus(): return convert_children("+") if expression.is_minus(): return convert_children("-") if expression.is_ite(): return convert_children("ite") if expression.node_type() == POW: exponent = expression.args()[1] if exponent.is_constant() and exponent.constant_value() == 1: return smt_to_nested(expression.args()[0]) else: return convert_children("^") if expression.is_le(): return convert_children("<=") if expression.is_lt(): return convert_children("<") if expression.is_equals(): return convert_children("=") if expression.is_symbol(): return "(var {} {})".format(format_type(expression.symbol_type()), expression.symbol_name()) if expression.is_constant(): value = expression.constant_value() if isinstance(value, fractions.Fraction): value = float(value) return "(const {} {})".format(format_type(expression.constant_type()), value) raise RuntimeError("Cannot convert {} (of type {})".format( expression, expression.node_type()))