def _basecase_msg(primal, x): """ Computes the piecewise integral for a leaf node. Parameters ---------- x : str Name of the variable in the primal graph """ assert (primal.G.degree[x] <= 1) intervals = domains_to_intervals(primal.get_univariate_formula(x)) # cacca one = Poly(1, symvar(x), domain="QQ") one = Poly(1, symvar(x), symvar("aux_y"), domain="QQ") return list(map(lambda i: (i[0], i[1], one), intervals))
def _parse_potentials(potentials, xvar, subs=None): msgs = [] symx = symvar(str(xvar)) # cacca one = Poly(1, symx, domain="QQ") one = Poly(1, symx, symvar("aux_y"), domain="QQ") for lit, f in potentials: msg = [] k, is_lower, _ = literal_to_bounds(lit)[xvar] if subs is not None: k = simplify(substitute(k, subs)) k = k.constant_value() if is_lower: msg.append((float('-inf'), k, one)) msg.append((k, float('inf'), f)) else: msg.append((float('-inf'), k, f)) msg.append((k, float('inf'), one)) msgs.append(msg) return msgs
def weight_to_lit_potentials(expr): """Converts a pysmt.FNode instance into the MPWMI data structure for weights if the input encodes potentials associated to literals, i.e.: Times(Ite(lit_1, w_1, Real(1)), ..., Ite(lit_n, w_n, Real(1))) Or the constant Real(1). Raises ValueError otherwise. Parameters ---------- expr : pysmt.FNode The pysmt expression """ def ite_to_potential(expr): if expr.is_ite(): cond, wpos, wneg = expr.args() if (is_literal(cond) and wneg.is_real_constant() and wneg.constant_value() == 1): return cond, wpos raise ValueError( "The expression is not Times(Ite(lit_1, w_1, Real(1)), ..., Ite(lit_n, w_n, Real(1)))" ) if expr.is_times(): wls = [ite_to_potential(a) for a in expr.args()] elif expr.is_real_constant() and expr.constant_value() == 1: return {} else: wls = [ite_to_potential(expr)] potentials = {} for lit, w in wls: variables = list(lit.get_free_variables()) v = tuple(sorted(map(lambda x: x.symbol_name(), variables))) assert (len(v) in [1, 2]), "Not implemented for ternary atoms" if v not in potentials: potentials[v] = [] symv = tuple([symvar(x) for x in v]) symw = Poly(to_sympy(w), symv, domain="RR") potentials[v].append((lit, symw)) return potentials
def _piecewise_symbolic_integral(cache, integrand, x, y=None): """ Computes the symbolic integral of 'x' of a piecewise polynomial 'integrand'. The result might be a sympy expression or a numerical value. Parameters ---------- integrand : list A list of (lower bound, upper bound, polynomial) x : object A string/sympy expression representing the integration variable """ cache_hit = [0, 0] if (cache is not None) else None res = 0 for l, u, p in integrand: symx = symvar(x) symy = symvar(y) if y else symvar("aux_y") syml = Poly(to_sympy(l), symy, domain="QQ") symu = Poly(to_sympy(u), symy, domain="QQ") if type(p) != Poly: symp = Poly(to_sympy(p), symx, domain="QQ") else: symp = Poly(p.as_expr(), symx, symy, domain="QQ") #print("integrating", symp.as_expr(), f"in d{symx} with bounds", [syml.as_expr(), symu.as_expr()]) if cache is not None: # for cache = True """ hierarchical cache, where we cache: - the anti-derivatives for integrands, retrieved by: (None, None, integrand key) - the partial integration term, retrieved by: (lower bound key, None, integrand key) (None, upper bound key, integrand key) - the whole integration, retrieved by: (lower bound key, upper bound key, integrand key) """ # cache keys for bounds k_lower = MP2WMI.sympy_to_tuple(syml) k_upper = MP2WMI.sympy_to_tuple(symu) k_poly = MP2WMI.sympy_to_tuple( symp) # cache key for integrand polynomial k_full = (k_lower, k_upper, k_poly) #print("========= KEYS =========") #print("lower:", syml.as_expr(), "-->", k_lower) #print("upper:", symu.as_expr(), "-->", k_upper) #print("poly:", symp.as_expr(), "-->", k_poly) #print("========================") if k_full in cache: # retrieve the whole integration cache_hit[True] += 1 symintegral = MP2WMI.tuple_to_sympy( cache[k_full], symx, symy) symintegral = symintegral.subs(symintegral.gens[0], symy) else: # retrieve partial integration terms terms = [None, None] k_part_l = (k_lower, k_poly) k_part_u = (k_upper, k_poly) if k_part_l in cache: partial_l = MP2WMI.tuple_to_sympy( cache[k_part_l], symx, symy) terms[0] = partial_l.subs(partial_l.gens[0], symy) if k_part_u in cache: partial_u = MP2WMI.tuple_to_sympy( cache[k_part_u], symx, symy) terms[1] = partial_u.subs(partial_u.gens[0], symy) if None not in terms: cache_hit[True] += 1 else: # retrieve anti-derivative k_anti = (k_poly, ) if k_anti in cache: cache_hit[True] += 1 antidrv = MP2WMI.tuple_to_sympy( cache[k_anti], symx, symy) else: cache_hit[False] += 1 antidrv = symp.integrate(symx) cache[k_anti] = MP2WMI.sympy_to_tuple(antidrv) # cache partial integration terms if terms[0] is None: terms[0] = Poly(antidrv.as_expr(), symx, domain=f'QQ[{symy}]').eval( {symx: syml.as_expr()}) terms[0] = Poly(terms[0].as_expr(), symx, symy, domain="QQ") cache[k_part_l] = MP2WMI.sympy_to_tuple(terms[0]) if terms[1] is None: terms[1] = Poly(antidrv.as_expr(), symx, domain=f'QQ[{symy}]').eval( {symx: symu.as_expr()}) terms[1] = Poly(terms[1].as_expr(), symx, symy, domain="QQ") cache[k_part_u] = MP2WMI.sympy_to_tuple(terms[1]) #print("subs: (", terms[1].as_expr(), ") - (", terms[0].as_expr(), ")") symintegral = terms[1] - terms[0] if not isinstance(symintegral, Poly): symintegral = Poly(symintegral, symx, symy, domain='QQ') cache[k_full] = MP2WMI.sympy_to_tuple(symintegral) else: # for cache = False antidrv = symp.integrate(symx) lower = Poly(antidrv.as_expr(), symx, domain=f'QQ[{symy}]').eval({symx: syml.as_expr()}) lower = Poly(lower.as_expr(), symx, symy, domain="QQ") upper = Poly(antidrv.as_expr(), symx, domain=f'QQ[{symy}]').eval({symx: symu.as_expr()}) upper = Poly(upper.as_expr(), symx, symy, domain="QQ") symintegral = upper - lower res += symintegral #print("integral:", symintegral.as_expr()) #print() #print("RESULT:", res) #print("**************************************************") return res, cache_hit
def piecewise_symbolic_integral(self, integrand, x, y=None): """ Computes the symbolic integral of 'x' of a piecewise polynomial 'integrand'. The result might be a sympy expression or a numerical value. Parameters ---------- integrand : list A list of (lower bound, upper bound, polynomial) x : object A string/sympy expression representing the integration variable """ res = 0 #logger.debug(f"\t\t\t\tpiecewise_symbolic_integral") #logger.debug(f"\t\t\t\tlen(integrand): {len(integrand)} --- y: {y}") for l, u, p in integrand: symx = symvar(x) symy = symvar(y) if y else symvar("aux_y") syml = Poly(to_sympy(l), symy, domain="QQ") symu = Poly(to_sympy(u), symy, domain="QQ") #logger.debug(f"\t\t\t\t\tl: {l} --- u: {u} --- p: {p}") if type(p) != Poly: symp = Poly(to_sympy(p), symx, domain="QQ") else: symp = Poly(p.as_expr(), symx, domain=f"QQ[{symy}]") if y else p if self.cache is not None: # for cache = True """ hierarchical cache, where we cache: - the anti-derivatives for integrands, retrieved by the same integrand key - the partial integration term, retrieved by the same (integrand key, lower / upper bound key) pair - the whole integration, retrieved by the same (integrand key, lower bound key, upper bound key) pair """ bds_ks = [MPWMI.cache_key(syml)[0], MPWMI.cache_key(symu)[0]] # cache keys for bounds bds = [syml.as_expr(), symu.as_expr()] p_ks = MPWMI.cache_key(symp) # cache key for integrand polynomial trm_ks = [(bds_ks[0], p_ks[0]), (bds_ks[1], p_ks[0])] if (bds_ks[0], bds_ks[1], p_ks[0]) in self.cache: # retrieve the whole integration self.cache_hit[True] += 1 symintegral = self.cache[(bds_ks[0], bds_ks[1], p_ks[0])] symintegral = symintegral.subs(symintegral.gens[0], symy) else: terms = [] for tk in trm_ks: # retrieve partial integration terms if tk in self.cache: trm = self.cache[tk] trm = trm.subs(trm.gens[0], symy) terms.append(trm) else: terms.append(None) if None not in terms: self.cache_hit[True] += 1 else: if p_ks[0] in self.cache: # retrieve anti-derivative self.cache_hit[True] += 1 antidrv = self.cache[p_ks[0]] antidrv_expr = antidrv.as_expr().subs(antidrv.gens[0], symx) antidrv = Poly(antidrv_expr, symx, domain=f"QQ[{symy}]") if y \ else Poly(antidrv_expr, symx, domain="QQ") else: self.cache_hit[False] += 1 antidrv = symp.integrate(symx) for k in p_ks: # cache anti-derivative self.cache[k] = antidrv for i in range(len(terms)): if terms[i] is not None: continue terms[i] = antidrv.eval({symx: bds[i]}) terms[i] = Poly(terms[i].as_expr(), symy, domain="QQ") for k in p_ks: # cache partial integration terms self.cache[(bds_ks[i], k)] = terms[i] symintegral = terms[1] - terms[0] for k in p_ks: # cache the whole integration self.cache[(bds_ks[0], bds_ks[1], k)] = symintegral else: # for cache = False antidrv = symp.integrate(symx) symintegral = antidrv.eval({symx: symu.as_expr()}) - \ antidrv.eval({symx: syml.as_expr()}) res += symintegral #logger.debug(f"\t\t\t\t\tsymintegral: {symintegral}") return res