def get_const_valuation_with_v(v, variables, is_log): """ return a constant valuation with scope v, (all ones, all zero) """ for var in variables: if var.label == v: if is_log: # transform back to log scale # return Valuation(Factor({var}, ONE), Factor({var}, ONE)).log() return Valuation(Factor({var}, ONE), Factor({var}, ZERO)).log() else: # return Valuation(Factor({var}, ONE), Factor({var}, ONE)) return Valuation(Factor({var}, ONE), Factor({var}, ZERO))
def get_const_with_vars(var_labels, variables, is_valuation, is_log): var_set = {var for var in variables if var.label in var_labels} if is_valuation: const_linear = Valuation(Factor(var_set.copy(), ONE), Factor(var_set.copy(), ZERO)) else: const_linear = Factor(var_set.copy(), ONE) if is_log: return const_linear.log() else: return const_linear
def eu_from_bounds(self, bounds, scale_prob_max=True): temp = Valuation(1.0, 0.0) # bounds[0].copy() # first valuation for el in bounds: temp = temp * el # combine bounds if scale_prob_max: return temp.util * self.prob_const # result is in linear scale else: return temp.util
def _array_to_val(self, var_obj, x, edges): prob_tables, util_tables = np.split(x.copy(), 2) valuations = [] for p, u in zip(np.split(prob_tables, len(edges)), np.split(util_tables, len(edges))): valuations.append(Valuation(Factor(var_obj, p), Factor(var_obj, u))) return valuations
def _set_cost(self, node_from, node_to, prob_gradient, util_gradient, step): prob_shift = -step*prob_gradient util_shift = -step*util_gradient va_shift = Valuation(prob_shift.exp(), util_shift) np.clip(va_shift.prob.t, a_min=TOL, a_max=None, out=va_shift.prob.t) # fix numerical error cannot div by zero va_from = self.mg.message_graph.node[node_from]['fs'][0] va_to = self.mg.message_graph.node[node_to]['fs'][0] self.mg.message_graph.node[node_from]['fs_old'] = va_from self.mg.message_graph.node[node_to]['fs_old'] = va_to self.mg.set_factor(node_from, va_from/va_shift) self.mg.set_factor(node_to, va_to*va_shift)
def __init__(self, verbose_level, message_graph, elim_order, weights, is_log, epsilon, log_file_name, ibound, mini_buckets, gm_variables): super(WeightedMBE4, self).__init__(verbose_level, message_graph, elim_order, weights, is_log, log_file_name) self.epsilon = epsilon # WEPS smallest weight self.ibound = ibound self.mini_buckets = mini_buckets # defaultdict(SortedSet) mini_buckets[var] : SortedSet( [(var, m_id), ...] ) self.prob_const_per_layer = {v: 1.0 for v in self.elim_order} self.global_meu_bound = 1.0 # meu is in linear scale self.gm_variables = gm_variables # list of Var objects from gm class Var0 at index 0 of list self.processed_nodes = set() self.const_msg_at = {v: Valuation(1.0, 0.0) for v in self.elim_order}
def _set_util_const(self, node, util_gradient, step): util_const_shift = -step * util_gradient self.mg.message_graph.node[node]['util_const_old'] = self.mg.message_graph.node[node]['util_const'] self.mg.message_graph.node[node]['util_const'] += util_const_shift self.mg.message_graph.node[node]['fs_old'] = self.mg.message_graph.node[node]['fs'][0] p = self.mg.message_graph.node[node]['fs'][0].prob eu = self.mg.message_graph.node[node]['fs'][0].util u_shifted = eu/p + util_const_shift # creates a new object eu_shifted = p*u_shifted # creates a new object self.mg.set_factor(node, Valuation(p, eu_shifted)) self.util_const += util_const_shift
def read_decomposed_bounds(self, nodes=[]): if not nodes: nodes = self.mg.message_graph.nodes() temp = Valuation(1.0, 0.0) for node in nodes: #self.mg.message_graph.nodes_iter(): if node in self.processed_nodes: continue val = self.mg.message_graph.node[node]['bound'] temp = temp * val # combine bounds assert not np.isnan(temp.util) return temp.util
def _eval_pseudo_belief_node(self, node): pr = self.mg.message_graph.node[node]['fs'][0].prob pr = pr.log() mu_p = self._eval_pseudo_belief(node, pr) eu = self.mg.message_graph.node[node]['fs'][0].util.copy() np.clip(eu.t, a_min=ZERO, a_max=None, out=eu.t) eu = eu.log() mu_u = self._eval_pseudo_belief(node, eu) if self.mg.message_graph.node[node]['pseudo_belief'] is None: self.mg.message_graph.node[node]['pseudo_belief'] = Valuation(mu_p, mu_u) else: self.mg.message_graph.node[node]['pseudo_belief'].prob = mu_p self.mg.message_graph.node[node]['pseudo_belief'].util = mu_u
def _construct_tree(self): def add_vertex(stage): vertex = self._graph.add_vertex() index = self._graph.vertex_index[vertex] self._vertices[index] = stage return index root_formula = Formula() root_formula.assert_some_states_present(self._protocol.initial_states) root_formula.assert_all_states_absent(self._protocol.states - self._protocol.initial_states) root_stage = Stage(root_formula, Valuation(), set(), speed=Speed.ZERO) root_index = add_vertex(root_stage) unexplored = [(root_index, root_stage)] mem = {} # for memoization in computation of J while len(unexplored) > 0: index, stage = unexplored.pop() valuations = stage.formula.solutions() has_children = False for val in valuations: child_stage = new_stage(self._protocol, stage, val, mem) if not child_stage.is_redundant(): child_index = add_vertex(child_stage) self._graph.add_edge(index, child_index) unexplored.append((child_index, child_stage)) has_children = True if not has_children: self._leaves.add(index) self._speed = max(self._speed, stage.speed)
def solutions(self): solver = z3.Solver() solver.add(self._consistency_constraints()) solver.add(self._assertions) sol = [] while (solver.check() == z3.sat): model = solver.model() valuation = Valuation() for var in self: valuation[var] = z3.is_true(model[Formula._id(var)]) sol.append(valuation) # Forbid solution in future checks solver.add( z3.Or([ z3.Not(Formula._id(v)) if valuation[v] else Formula._id(v) for v in valuation ])) return sol
def _propagate_one_pass(self, time_limit, iter_limit, optimize_weight, optimize_cost): """ wmbe_id: initialize weights by 1 iteration of gdd -> inherit weights """ const_valuations = [] for ord_ind, var in enumerate(self.elim_order): var_obj = self.gm_variables[var] nodes_at_the_current_layer = self.mini_buckets[var] # pull message and combine before cost shifting self.print_log("process var:{}\nmini_buckets:{}".format(var, nodes_at_the_current_layer)) for node in nodes_at_the_current_layer: if debug: assert node[0] == var for n_from, n_cur in self.mg.message_graph.in_edges_iter([node]): if debug: assert n_cur == node if self.elim_order.index( n_from[0] ) < self.elim_order.index( n_cur[0] ): in_msgs = self.mg.pull_msg_from(n_from, node) combined_factor = self.mg.combine_factors(node, in_msgs, self.is_log) self.mg.set_factor(node, combined_factor) # optimize bound time_0, iter, cost_updated, weight_updated = time.time(), 1, False, False while len(nodes_at_the_current_layer) > 1 and iter <= iter_limit and (time.time() - time_0) < time_limit: if optimize_cost: cost_updated = self._optimize_costs(var_obj, nodes_at_the_current_layer) if optimize_weight: weight_updated = self._optimize_weights(var_obj, nodes_at_the_current_layer) if not cost_updated and not weight_updated: # both not updated within iter limit escape self.print_log("\toptimizing mini_buckets iter:{} no improvement".format(iter)) break else: self.print_log("\toptimizing mini_buckets iter:{}\tbound:{}".format(iter, self.read_decomposed_bounds())) self.print_log("\t\tc_updated:{}\tw_updated:{}".format(cost_updated, weight_updated)) iter += 1 # push message downward along the edge nodes_changed = [] for node in nodes_at_the_current_layer: # if node == (20,0): # print("watch") first mini bucket split in mdp1 w = self.mg.message_graph.node[node]['w'][var] w_inv = np.inf if w <= self.epsilon else ONE/w F = self.mg.message_graph.node[node]['fs'][0].abs() lnF = F.log() lnmsg = lnF.lsePower(elim={var_obj}, power=w_inv) msg = lnmsg.exp() next_node = self._get_next_node(node) if next_node: nodes_changed.append(next_node) self.print_log("\tsend msg from {} to {}".format(node, next_node)) self.mg.push_msg(node, next_node, msg) ### inherit weights, recompute bounds, pseudo belief at receiving nodes sc_from = self.mg.message_graph.node[node]['sc'] sc_to = self.mg.message_graph.node[next_node]['sc'] w_from = self.mg.message_graph.node[node]['w'] w_to = self.mg.message_graph.node[next_node]['w'] if debug: sc_msg = sc_from - {var} assert sc_msg.issubset(sc_to) for vv in sc_from: if vv == var: # skip weight for the current layer continue # inherit weigths self.mg.message_graph.node[next_node]['w'][vv] += self.mg.message_graph.node[node]['w'][vv] else: self.print_log("\tconstant msg from {} with valuation:{}".format(node, msg)) self.mg.push_msg(node, None, msg) const_valuations.append(msg) if nodes_changed: self._reset_node_bounds(nodes_changed) self._reset_pseudo_beliefs(nodes_changed) self.processed_nodes.update(nodes_at_the_current_layer) # if var == 19: for mdp1 problem debugging first decision # exit() bound_valuation = Valuation(1.0, 0.0) print(const_valuations) for val in const_valuations: bound_valuation = bound_valuation * val return float(bound_valuation.util), float(bound_valuation.prob)
def new_stage_valuation(protocol, valuation, disabled): def f(M, N): M_, N_ = set(M), set(N) def cannot_occur(t): return (len(t.preset & M) > 0 or (t.pre in disabled) or (len(t.preset) == 1 and t.pre.some() in N)) for q in M: T = {t for t in protocol.transitions if q in t.post} to_remove = any(not cannot_occur(t) for t in T) if to_remove: M_.remove(q) for q in N: S = { t for t in protocol.transitions if (q in t.pre) and ( t.pre.other(q) != q) and (not t.unchanged(q)) } T = {t for t in protocol.transitions if q not in t.pre} to_remove = any(not ((t.pre.other(q) in M) or (t.pre in disabled)) for t in S) to_remove = to_remove or any(not cannot_occur(t) for t in T) if to_remove: N_.remove(q) return (M_, N_) # Compute greatest fixed point (M, N) of f M, N = valuation.absent_states(), valuation.unique_states() M_, N_ = f(M, N) while (M, N) != (M_, N_): M, N = M_, N_ M_, N_ = f(M, N) # Compute E E = set() P = valuation.present_states() for q in P: T = { t for t in protocol.transitions if (q in t.pre and q not in t.post) } if all((t.pre.other(q) in M) or (t.pre in disabled) or ( t.pre.other(q) == q and q in N) for t in T): E.add(q) # Construct new valuation new_valuation = Valuation() for q in M: new_valuation[Var(q)] = False for q in E: new_valuation[Var(q)] = True for q in N: new_valuation[Var(q, True)] = True return new_valuation
def _propagate_one_pass(self, time_limit, iter_limit, optimize_weight, optimize_cost): """ wmbe_id: initialize weight uniformly -> reset all remaining weights uniformly after passing messages """ const_valuations = [ ] # todo this constant should be multiplied per layer to be a heuristic for var in self.elim_order: var_obj = self.gm_variables[var] nodes_at_the_current_layer = self.mini_buckets[var] self._reset_uniform_weights() self._reset_node_bounds() self._reset_pseudo_beliefs() # pull message and combine before cost shifting self.print_log("process var:{}\nmini_buckets:{}".format( var, nodes_at_the_current_layer)) for node in nodes_at_the_current_layer: if debug: assert node[0] == var for n_from, n_cur in self.mg.message_graph.in_edges_iter( [node]): if debug: assert n_cur == node if self.elim_order.index( n_from[0]) < self.elim_order.index(n_cur[0]): in_msgs = self.mg.pull_msg_from(n_from, node) combined_factor = self.mg.combine_factors( node, in_msgs, self.is_log) self.mg.set_factor(node, combined_factor) # optimize bound time_0, iter, cost_updated, weight_updated = time.time( ), 1, False, False while len(nodes_at_the_current_layer ) > 1 and iter <= iter_limit and (time.time() - time_0) < time_limit: if optimize_cost: cost_updated = self._optimize_costs( var_obj, nodes_at_the_current_layer) if optimize_weight: weight_updated = self._optimize_weights( var_obj, nodes_at_the_current_layer) if not cost_updated and not weight_updated: # both not updated within iter limit escape self.print_log( "\toptimizing mini_buckets iter:{} no improvement". format(iter)) break else: self.print_log( "\toptimizing mini_buckets iter:{}\tbound:{}".format( iter, self.read_decomposed_bounds())) self.print_log("\t\tc_updated:{}\tw_updated:{}".format( cost_updated, weight_updated)) iter += 1 # push message downward along the edge; uniform weight for node in nodes_at_the_current_layer: # if node == (20,0): # print("watch") first mini bucket split in mdp1 w = self.mg.message_graph.node[node]['w'][var] w_inv = np.inf if w <= self.epsilon else ONE / w F = self.mg.message_graph.node[node]['fs'][0].abs() lnF = F.log() lnmsg = lnF.lsePower(elim={var_obj}, power=w_inv) msg = lnmsg.exp() next_node = self._get_next_node(node) if next_node: self.print_log("\tsend msg from {} to {}".format( node, next_node)) self.mg.push_msg(node, next_node, msg) else: self.print_log( "\tconstant msg from {} with valuation:{}".format( node, msg)) self.mg.push_msg(node, None, msg) const_valuations.append(msg) self.processed_nodes.update(nodes_at_the_current_layer) # if var == 19: for mdp1 problem debugging first decision # exit() bound_valuation = Valuation(1.0, 0.0) print(const_valuations) for val in const_valuations: bound_valuation = bound_valuation * val return float(bound_valuation.util), float(bound_valuation.prob)