def test_exactly_one_unpacking(self): s1,s2 = Symbol("x"), Symbol("y") f1 = ExactlyOne((s for s in [s1,s2])) f2 = ExactlyOne([s1,s2]) f3 = ExactlyOne(s1,s2) self.assertEqual(f1,f2) self.assertEqual(f2,f3)
def test_exactlyone_w_generator(self): x, y = Symbol("x"), Symbol("y") elems = [x,y] f1 = ExactlyOne(elems) f2 = ExactlyOne(e for e in elems) self.assertEqual(f1, f2)
def test_smart_serialize(self): x, y = Symbol("x"), Symbol("y") f1 = And(x,y) f = Implies(x, f1) substitutions = {f1: "f1"} # Mapping FNode -> String res = smart_serialize(f, subs=substitutions) self.assertEquals("(x -> f1)", res) # If no smarties are provided, the printing is compatible # with standard one res = smart_serialize(f) self.assertIsNotNone(res) self.assertEquals(str(f), res) fvars = [Symbol("x%d" % i) for i in xrange(5)] ex = ExactlyOne(fvars) substitutions = {ex: "ExactlyOne(%s)" % ",".join(str(v) for v in fvars)} old_str = ex.serialize() smart_str = smart_serialize(ex, subs=substitutions) self.assertTrue(len(old_str) > len(smart_str)) self.assertEquals("ExactlyOne(x0,x1,x2,x3,x4)", smart_str)
def test_smart_serialize(self): x, y = Symbol("x"), Symbol("y") f1 = And(x, y) f = Implies(x, f1) substitutions = {f1: "f1"} # Mapping FNode -> String res = smart_serialize(f, subs=substitutions) self.assertEqual("(x -> f1)", res) # If no smarties are provided, the printing is compatible # with standard one res = smart_serialize(f) self.assertIsNotNone(res) self.assertEqual(str(f), res) fvars = [Symbol("x%d" % i) for i in range(5)] ex = ExactlyOne(fvars) substitutions = { ex: "ExactlyOne(%s)" % ",".join(str(v) for v in fvars) } old_str = ex.serialize() smart_str = smart_serialize(ex, subs=substitutions) self.assertTrue(len(old_str) > len(smart_str)) self.assertEqual("ExactlyOne(x0,x1,x2,x3,x4)", smart_str)
def create_replication(replicas, microservice, nodes): """ :param replicas: the total number of replicas of the current microservice :param microservice: the microservice that is allocated to the network :param nodes: a dictionary where a key represents a microservice having the value a list of possible mapping nodes :return: an encoding to map exactly one replica on a node and a list of replicas """ replicas_list = list() encoding = list() for i in range(replicas): replicas_list.append(replica(microservice, i)) replica_len = len(replicas_list) for n in nodes[microservice]: for i in range(replica_len): encoding.append( Equals(replicas_list[i], Int(int(n))).Implies( Not( Or( Equals(replicas_list[j], Int(int(n))) for j in range(i + 1, replica_len))))) micro_constraint = And( ExactlyOne(Equals(r, Int(int(n))) for n in nodes[microservice]) for r in replicas_list) return And(encoding), replicas_list, micro_constraint
def encode(self): """ Do the job. """ self.enc = [] # getting a tree ensemble self.ensemble = TreeEnsemble( self.model, self.xgb.extended_feature_names_as_array_strings, nb_classes=self.nofcl) # introducing class score variables csum = [] for j in range(self.nofcl): cvar = Symbol('class{0}_score'.format(j), typename=REAL) csum.append(tuple([cvar, []])) # if targeting interval-based encoding, # traverse all trees and extract all possible intervals # for each feature if self.optns.encode == 'smtbool': self.compute_intervals() # traversing and encoding each tree for i, tree in enumerate(self.ensemble.trees): # getting class id clid = i % self.nofcl # encoding the tree tvar = Symbol('tr{0}_score'.format(i + 1), typename=REAL) self.traverse(tree, tvar, prefix=[]) # this tree contributes to class with clid csum[clid][1].append(tvar) # encoding the sums for pair in csum: cvar, tvars = pair self.enc.append(Equals(cvar, Plus(tvars))) # enforce exactly one of the feature values to be chosen # (for categorical features) categories = collections.defaultdict(lambda: []) for f in self.xgb.extended_feature_names_as_array_strings: if '_' in f: categories[f.split('_')[0]].append( Symbol(name=f, typename=BOOL)) for c, feats in six.iteritems(categories): self.enc.append(ExactlyOne(feats)) # number of assertions nof_asserts = len(self.enc) # making conjunction self.enc = And(self.enc) # number of variables nof_vars = len(self.enc.get_free_variables()) if self.optns.verb: print('encoding vars:', nof_vars) print('encoding asserts:', nof_asserts) return self.enc, self.intvs, self.imaps, self.ivars
# The Norwegian lives next to the blue house. # Careful with this!!! And( Iff(nat(i, "norwegian"), Or(color(i - 1, "blue"), color(i + 1, "blue"))) for i in Houses), # The owner who smokes Blends lives next to the one who drinks water. And( Iff(smoke(i, "blends"), Or(drink(i - 1, "water"), drink(i + 1, "water"))) for i in Houses)) domain = And( And(ExactlyOne(color(i, c) for i in Houses) for c in Color), And(ExactlyOne(nat(i, c) for i in Houses) for c in Nat), And(ExactlyOne(pet(i, c) for i in Houses) for c in Pet), And(ExactlyOne(drink(i, c) for i in Houses) for c in Drink), And(ExactlyOne(smoke(i, c) for i in Houses) for c in Smoke), # And(ExactlyOne(color(i, c) for c in Color) for i in Houses), And(ExactlyOne(nat(i, c) for c in Nat) for i in Houses), And(ExactlyOne(pet(i, c) for c in Pet) for i in Houses), And(ExactlyOne(drink(i, c) for c in Drink) for i in Houses), And(ExactlyOne(smoke(i, c) for c in Smoke) for i in Houses), ) problem = And(domain, facts) model = get_model(problem)
def build_formula(self): t_list = [] l_facts = [] domain = [] latencies = [] task_constraints = [] offers = dict() # the SLA of our application SLA = int(self.app_dict["IoTapplication"]["SLA"]) # find the dependencies between tasks depend_list, tasks, prob = self.find_dependencies(self.app_dict) # encodes the domain constraints in the SAT formula # checks on what nodes every two tasks are mapped and save their # latency ln = len(self.node_list) for i in range(ln): d = {} for j in range(i, ln): for t in depend_list: if i == j: d[str(t[0]) + "_" + str(t[1])] = And( Equals(self.task(t[0]), Int(i)), Equals(self.task(t[1]), Int(j))) else: d[str(t[0]) + "_" + str(t[1])] = Or( And(Equals(self.task(t[0]), Int(i)), Equals(self.task(t[1]), Int(j))), And(Equals(self.task(t[0]), Int(j)), Equals(self.task(t[1]), Int(i)))) for k, v in d.items(): t_tasks = k.split("_") domain.append( v.Implies( self.get_latency(t_tasks[0], t_tasks[1]).Equals( Int(self.getLatency(i, j))))) # find the offers for each individual task # RETURN: a dictionary containing as a key the tasks and the value a # list of nodes which sent a bid for that particular task for k in self.node_offers: ln = len(self.node_offers[k]) for i in range(ln): aTask = self.node_offers[k][i] offerSize = len(aTask) for j in range(offerSize): theTask = aTask[j] if theTask in offers: offers[str(theTask)].add(int(k)) else: aSet = set() offers[theTask] = aSet aSet.add(int(k)) # check if all tasks have received an offer, if not the tasks will receive an offer from cloud, i.e., node 0 if len(offers) != len(tasks): for t in tasks: if str(t) in offers: continue else: aSet = set() offers[str(t)] = aSet aSet.add(0) """creates a list of constraints that ensure a solution found by SMT does not exceed the available resources of a node. It is dependent on the bids received from each participant""" for node, bid in self.node_offers.items(): bid_len = len(bid) task_c = [] tasks_pos = [] task_r = [] neg_dict = {} diff_dict = {} for t in range(bid_len): tasks_cst = set() tmp_list = set() diff_list = [] for n in bid[t]: diff_list.append(n) diff_dict[t] = diff_list if len(bid[t]) == 0: continue else: tasks_pos.append( Or( Equals(self.task(tn), Int(int(node))) for tn in bid[t])) for j in range(bid_len): if j == t: continue else: for tsk in bid[j]: tasks_cst.add(tsk) common_elem = list(set(bid[t]) & set(bid[j])) if len(common_elem) != 0: tt = set(bid[j]) - set(bid[t]) for et in tt: tmp_list.add(et) neg_dict[t] = list((tasks_cst - set(bid[t]))) for e in common_elem: if e in diff_dict[t]: diff_dict[t].remove(e) temp_list = list((tasks_cst - set(bid[t])) - tmp_list) task_c.append( Or(Equals(self.task(tn), Int(int(node))) for tn in bid[t]).Implies( Not( Or( Equals(self.task(temp_list[tn]), Int(int(node))) for tn in range(len(temp_list)))))) for t in range(bid_len): if t in neg_dict and t in diff_dict: if len(diff_dict[t]) > 0: task_c.append( Or( Equals(self.task(tn), Int(int(node))) for tn in diff_dict[t]).Implies( Not( Or( Equals(self.task(neg_dict[t][tn]), Int(int(node))) for tn in range(len(neg_dict[t])))))) task_r.append(Or(tasks_pos)) task_r.append(And(task_c)) task_constraints.append(And(task_r)) # creates the encoding for the tasks facts, i.e., where a task can be mapped task_facts = And( ExactlyOne(Equals(self.task(t), Int(n)) for n in offers[t]) for t in offers.keys()) # encodes the facts related to the latency between nodes latency_domain = And(domain) # encodes the domain # encode the latency constraint for our problem problem = Plus(p for p in prob) # ensure that the solution does not exceed the available resources of a participant tasks_const = And(task_constraints) # put everything together into one sat formula facts = And(task_facts, tasks_const) f1 = facts.And(latency_domain) formula = f1.And(LE(problem, Int(SLA))) return formula, tasks, prob, latencies, task_facts, tasks_const, latency_domain
def encode_shape(self): #[@TODO]If p in {Operators} support to perform selective formula creation. logging.debug('Encoding (Prop) Shape Rules...') #a) Each node is labeled with exactly (At Least, At Most) one label (Operator or variable). c_1_a = [] # At least constraints logging.debug('c_1_a: Each node is labeled with at least one label:') for i in range(1, self.d + 1): c_1_a.append(And([Or(self.x[i, label] for label in self.L)])) logging.debug('c_1_a:%s'%(c_1_a)) c_1_b = [] # At Most Constraints #product of label, not reflex and not commutative prod_labels = list(filter(lambda t: t[0] < t[1], product(self.L,self.L))) logging.debug('c_1_b: Each node is labeled with at most one label:') for i in range(1, self.d + 1): or_cls = [] for (l,l1) in prod_labels: or_cls.append(Or(Not(self.x[i,l]), Not(self.x[i,l1]))) c_1_b.append(And(or_cls)) logging.debug('c_1_b:%s'%(c_1_b)) c_2 = [] logging.debug('c_2: Each node is labeled with a binary operator has always (exactly) two outgoing edges:') for i in range(2, self.d + 1): out_edges = self.aux_out_egdes(i) and_cls = [] for label in self.binaryOp: or_cls = [] for (u,v) in out_edges: # logging.debug('%s => %s and %s '%(self.x[i, label], self.l[u], self.r[v])) or_cls.append(And(self.l[u], self.r[v])) and_cls.append(Implies(self.x[i, label], ExactlyOne(or_cls))) c_2.append(And(and_cls)) logging.debug('c_2:%s'%(c_2)) c_3 = [] logging.debug('c_3: Each node is labeled with a unary operator has exactly one outgoing edge (Default: Left edge) and no right edge:') # At most constraint for i in range(2, self.d + 1): out_edges = self.aux_out_egdes(i) and_cls = [] for label in self.unaryOp: or_cls = [] right_edge = [] for (u,v) in out_edges: if not self.l[u] in or_cls: or_cls.append(self.l[u]) right_edge.append(Not(self.r[u])) and_cls.append(Implies(self.x[i, label], ExactlyOne(or_cls))) and_cls.append(Implies(self.x[i, label], And(right_edge))) # logging.debug('%s => ExactlyOne(%s) '%(self.x[i, label], ExactlyOne(or_cls))) c_3.append(And(and_cls)) logging.debug('c_3:%s'%(c_3)) #d) each node labeled with AP has no outgoing edge (neither left, nor right) # print('****:',self.x) logging.debug('c_4: Each node is labeled from AP has no outgoing edge:') c_4 = [] for i in range(2, self.d + 1): out_edges = self.aux_out_egdes(i) and_cls = [] for label in self.AP: or_cls = [] for (u,v) in out_edges: # logging.debug('%s => %s and %s '%(self.x[i, label], self.l[u], self.r[v])) or_cls.append(Not(self.l[u])) or_cls.append(Not(self.r[v])) and_cls.append(Implies(self.x[i, label], And(or_cls))) c_4.append(And(and_cls)) logging.debug('c_4:%s'%(c_4)) #e) node 1 is always labeled with AP c_5_a = [] logging.debug('c_5: node 1 is always labeled with at least one label from AP:') for label in self.AP: c_5_a.append(self.x[1, label]) logging.debug('c_5_a:%s'%(c_5_a)) # print('++++++++++++++:',c_5_a) c_5_b = [] logging.debug('c_5: node 1 is always labeled with at most one label from AP:') prod_labels = list(filter(lambda t: t[0] < t[1], product(self.AP,self.AP))) for (u,v) in prod_labels: c_5_b.append(Or(Not(self.x[1, u]), Not(self.x[1,v]))) logging.debug('c_5_b:%s'%(c_5_b)) c_6 = [] #Quite strong replace it with simpler one. logging.debug('c_6: Each node labeled with operator has a parent also labeled with the operator:') for i in range(2, self.d + 1): if i + 1 < self.d+1: c_6.append(Implies(self.aux_or_cls(i), self.aux_or_cls(i + 1))) logging.debug('c_6: %s'%(c_6)) # logging('Left', And(c_6)) c_7 = [] logging.debug('c_7: Each node has exactly at least incoming edge (except root node):') for i in range(1, self.d+1): or_cls = [] for j in range(i + 1, self.d+1): or_cls.append(self.l[j,i]) or_cls.append(self.r[j,i]) if len(or_cls) != 0: c_7.append(Or(or_cls)) logging.debug('c_7: %s'%(c_7)) #k) Once and Hence rule doesn't come in same succession? logging.debug('All rules encoding well-defined pLTL in shape of a (DAG)') c_8 = [self.x[self.d,'G']] for i in range(1, self.d): c_8.append(Not(self.x[i, 'G'])) #Verifying particular solution. # c_9 = [self.x[1,'q'], self.x[2,'p'], self.x[3,'Y'], self.x[4,'|'], self.x[5,'O']] #Models of ast_enc are pLTL ASTs ast_enc = And(c_1_a) & And(c_1_b) & And(c_2) & And(c_3) & And(c_4) & Or(c_5_a) & And(c_5_b) & And(c_6) & And(c_7) & And(c_8) #& And(c_9) # ast_enc = ast_enc & self.x[1, 'Top'] return ast_enc