def test_simplify(): x = sympy.Symbol('x') y = sympy.Symbol('y') p1 = Parameter('x', 3) p2 = Parameter('y', 9, fix=True) pset = Parameters([p1, p2]) assert pset.simplify(x * y) == 9.0 * x p1 = Parameter('x', 3, lower=0.001) p2 = Parameter('y', 9) pset = Parameters([p1, p2]) assert pset.simplify(abs(x)) == x p1 = Parameter('x', 3, lower=0) p2 = Parameter('y', 9) pset = Parameters([p1, p2]) assert pset.simplify(sympy.Piecewise((2, sympy.Ge(x, 0)), (56, True))) == 2 p1 = Parameter('x', -3, upper=-1) p2 = Parameter('y', 9) pset = Parameters([p1, p2]) assert pset.simplify(abs(x)) == -x p1 = Parameter('x', -3, upper=0) p2 = Parameter('y', 9) pset = Parameters([p1, p2]) assert pset.simplify(sympy.Piecewise((2, sympy.Le(x, 0)), (56, True))) == 2 p1 = Parameter('x', 3) p2 = Parameter('y', 9) pset = Parameters([p1, p2]) assert pset.simplify(x * y) == x * y
def __init__(self, entries: Dict[ChannelID, Sequence[EntryInInit]], identifier: Optional[str] = None, *, parameter_constraints: Optional[List[Union[ str, ParameterConstraint]]] = None, measurements: Optional[List[MeasurementDeclaration]] = None, consistency_check: bool = True, registry: PulseRegistryType = None) -> None: """ Construct a `TablePulseTemplate` from a dict which maps channels to their entries. By default the consistency of the provided entries is checked. There are two static functions for convenience construction: from_array and from_entry_list. Args: entries: A dictionary that maps channel ids to a list of entries. An entry is a (time, voltage[, interpolation strategy]) tuple or a TableEntry identifier: Used for serialization parameter_constraints: Constraint list that is forwarded to the ParameterConstrainer superclass measurements: Measurement declaration list that is forwarded to the MeasurementDefiner superclass consistency_check: If True the consistency of the times will be checked on construction as far as possible """ AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements) ParameterConstrainer.__init__( self, parameter_constraints=parameter_constraints) if not entries: raise ValueError( "Cannot construct an empty TablePulseTemplate (no entries given). There is currently no " "specific reason for this. Please submit an issue if you need this 'feature'." ) self._entries = dict((ch, list()) for ch in entries.keys()) for channel, channel_entries in entries.items(): if len(channel_entries) == 0: raise ValueError('Channel {} is empty'.format(channel)) for entry in channel_entries: self._add_entry(channel, TableEntry(*entry)) self._duration = self.calculate_duration() self._table_parameters = set( var for channel_entries in self.entries.values() for entry in channel_entries for var in itertools.chain(entry.t.variables, entry.v.variables )) | self.constrained_parameters if self.duration == 0: warnings.warn( 'Table pulse template with duration 0 on construction.', category=ZeroDurationTablePulseTemplate) if consistency_check: # perform a simple consistency check. All inequalities with more than one free variable are ignored as the # sympy solver does not support them # collect all conditions inequalities = [eq.sympified_expression for eq in self._parameter_constraints] +\ [sympy.Le(previous_entry.t.underlying_expression, entry.t.underlying_expression) for channel_entries in self._entries.values() for previous_entry, entry in zip(channel_entries, channel_entries[1:])] # test if any condition is already dissatisfied if any( isinstance(eq, BooleanAtom) and bool(eq) is False for eq in inequalities): raise ValueError( 'Table pulse template has impossible parametrization') # filter conditions that are inequalities with one free variable and test if the solution set is empty inequalities = [ eq for eq in inequalities if isinstance(eq, sympy.Rel) and len(eq.free_symbols) == 1 ] if not sympy.reduce_inequalities(inequalities): raise ValueError( 'Table pulse template has impossible parametrization') self._register(registry=registry)
def sum_over_piecewise(expr: sympy.Piecewise, sum_var: sympy.Symbol, sum_start: typing.Union[sympy.Basic, int], sum_end: sympy.Basic, extra_condition: sympy.Basic = True) -> sympy.Expr: """ :return: equivalent to Sum(expr, (sum_var, sum_start, sum_end)), but we try to remove the piecewise. We assume that the piecewise conditions also depend on sum_var. """ assert sum_var.is_Integer or sum_var.assumptions0["integer"] assert isinstance(expr, sympy.Piecewise) res = sympy.sympify(0) cond_start = sympy.Ge(sum_var, sum_start) cond_end = sympy.Le(sum_var, sum_end) prev_ranges = [(None, sum_start - 1), (sum_end + 1, None)] def check(cond__): false_cond = simplify_and(sympy.And(sympy.Not(cond__), extra_condition)) if false_cond == sympy.sympify(False): return True true_cond = simplify_and(sympy.And(cond__, extra_condition)) if true_cond == sympy.sympify(False): return False return None for value, cond in expr.args: j = 0 while j < len(prev_ranges) - 1: cond_r_start = sympy.Ge(sum_var, prev_ranges[j][1] + 1) cond_r_end = sympy.Le(sum_var, prev_ranges[j + 1][0] - 1) cond = sympy.And(cond, cond_start, cond_end, cond_r_start, cond_r_end) cond_ = simplify_and(cond, sum_var, extra_conditions=extra_condition) # print(cond, "->", cond_) if cond_ == sympy.sympify(False): j += 1 continue if isinstance(cond_, sympy.And): if any( [sum_var not in part.free_symbols for part in cond_.args]): new_extra_conditions = [ part for part in cond_.args if sum_var not in part.free_symbols ] new_extra_condition = sympy.And(*new_extra_conditions) if check(new_extra_condition) is False: j += 1 continue assert check(new_extra_condition) cond_ = sympy.And(*[ part for part in cond_.args if sum_var in part.free_symbols ]) r = range_from_relationals(cond_, sum_var) if r[0] is None: # e.g. if cond_start == True because sum_var is >= 0 always r = (sum_start, r[1]) if sympy.Eq(r[0], r[1]) == sympy.sympify(True): res += value.subs(sum_var, r[0]) else: res += sympy.Sum(value, (sum_var, r[0], r[1])).doit() for i in range(1, len(prev_ranges) + 1): assert i < len(prev_ranges), "where to insert %r?" % ( r, ) # should not get past here assert check(sympy.Gt(r[0], prev_ranges[i - 1][1])) if check(sympy.Eq(r[0] - 1, prev_ranges[i - 1][1])): prev_ranges[i - 1] = (prev_ranges[i - 1][0], r[1]) break if check(sympy.Lt(r[0], prev_ranges[i][0])) or check( sympy.Lt(r[1], prev_ranges[i][0])): if check(sympy.Eq(r[1] + 1, prev_ranges[i][0])): prev_ranges[i] = (r[0], prev_ranges[i][1]) else: prev_ranges.insert(i, r) break # print("prev ranges:", prev_ranges) j = 0 # start over... if len(prev_ranges) == 2 and sympy.Eq( prev_ranges[0][1], prev_ranges[1][0]) == sympy.sympify(True): # We have the full range covered. break return res.simplify()
def gen_lessthaneq(self, ident, val): return sym.Le(val.exp_a, val.exp_b)
# Methods that have a `__foo__` as well as `__rfoo__` reflectable_magic_methods = { 'add': lambda a, b: a + b, 'sub': lambda a, b: a - b, 'mul': lambda a, b: a * b, 'mod': lambda a, b: a % b, } magic_methods = { **reflectable_magic_methods, 'eq': lambda a, b: sympy.Eq(a, b), 'gt': lambda a, b: sympy.Gt(a, b), 'lt': lambda a, b: sympy.Lt(a, b), 'le': lambda a, b: sympy.Le(a, b), 'ge': lambda a, b: sympy.Ge(a, b), } for method, _func in magic_methods.items(): method_name = f'{method}' def _create_magic_impl(func): def magic_impl(self, other): if isinstance(other, PySymInt): other = other.expr return PySymInt(func(self.expr, other), self.shape_env) return magic_impl # this should be wrapped transparently into torch._C.SymIntNode
def _ex_less_equal(self, e): return sp.Le(self.ex(e[0]), self.ex(e[1]))
def __kkts(self): alpha_st_kkts = sp.Matrix( np.diag(np.array(self.alphas.T * self.__subject_to_func().T))) alpha_kkts = sp.Matrix([sp.Ge(alpha, 0) for alpha in self.alphas]) st_kkts = sp.Matrix([sp.Le(st, 0) for st in self.__subject_to_func()]) return alpha_st_kkts, alpha_kkts, st_kkts
def test_reader_writer(self): # Test using the proper reader/writer try: import sympy as sp except ImportError: print('Sympy not found, skipping test.') return # Create writer and reader w = mypy.SymPyExpressionWriter() r = mypy.SymPyExpressionReader(self._model) # Name a = self._a ca = sp.Symbol('c.a') self.assertEqual(w.ex(a), ca) self.assertEqual(r.ex(ca), a) # Number with unit b = myokit.Number('12', 'pF') cb = sp.Float(12) self.assertEqual(w.ex(b), cb) # Note: Units are lost in sympy im/ex-port! #self.assertEqual(r.ex(cb), b) # Number without unit b = myokit.Number('12') cb = sp.Float(12) self.assertEqual(w.ex(b), cb) self.assertEqual(r.ex(cb), b) # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), cb) # Note: Sympy doesn't seem to have a prefix plus self.assertEqual(r.ex(cb), b) # Prefix minus # Note: SymPy treats -x as Mul(NegativeOne, x) # But for numbers just returns a number with a negative value x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), -cb) self.assertEqual(float(r.ex(-cb)), float(x)) # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), ca + cb) # Note: SymPy likes to re-order the operands... self.assertEqual(float(r.ex(ca + cb)), float(x)) # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), ca - cb) self.assertEqual(float(r.ex(ca - cb)), float(x)) # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), ca * cb) self.assertEqual(float(r.ex(ca * cb)), float(x)) # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), ca / cb) self.assertEqual(float(r.ex(ca / cb)), float(x)) # Quotient x = myokit.Quotient(a, b) self.assertEqual(w.ex(x), ca // cb) self.assertEqual(float(r.ex(ca // cb)), float(x)) # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), ca % cb) self.assertEqual(float(r.ex(ca % cb)), float(x)) # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), ca**cb) self.assertEqual(float(r.ex(ca**cb)), float(x)) # Sqrt x = myokit.Sqrt(a) cx = sp.sqrt(ca) self.assertEqual(w.ex(x), cx) # Note: SymPy converts sqrt to power self.assertEqual(float(r.ex(cx)), float(x)) # Exp x = myokit.Exp(a) cx = sp.exp(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Log(a) x = myokit.Log(a) cx = sp.log(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Log(a, b) x = myokit.Log(a, b) cx = sp.log(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(float(r.ex(cx)), float(x)) # Log10 x = myokit.Log10(b) cx = sp.log(cb, 10) self.assertEqual(w.ex(x), cx) self.assertAlmostEqual(float(r.ex(cx)), float(x)) # Sin x = myokit.Sin(a) cx = sp.sin(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Cos x = myokit.Cos(a) cx = sp.cos(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Tan x = myokit.Tan(a) cx = sp.tan(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # ASin x = myokit.ASin(a) cx = sp.asin(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # ACos x = myokit.ACos(a) cx = sp.acos(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # ATan x = myokit.ATan(a) cx = sp.atan(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Floor x = myokit.Floor(a) cx = sp.floor(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Ceil x = myokit.Ceil(a) cx = sp.ceiling(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Abs x = myokit.Abs(a) cx = sp.Abs(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Equal x = myokit.Equal(a, b) cx = sp.Eq(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # NotEqual x = myokit.NotEqual(a, b) cx = sp.Ne(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # More x = myokit.More(a, b) cx = sp.Gt(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Less x = myokit.Less(a, b) cx = sp.Lt(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # MoreEqual x = myokit.MoreEqual(a, b) cx = sp.Ge(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # LessEqual x = myokit.LessEqual(a, b) cx = sp.Le(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Not x = myokit.Not(a) cx = sp.Not(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # And cond1 = myokit.More(a, b) cond2 = myokit.Less(a, b) c1 = sp.Gt(ca, cb) c2 = sp.Lt(ca, cb) x = myokit.And(cond1, cond2) cx = sp.And(c1, c2) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Or x = myokit.Or(cond1, cond2) cx = sp.Or(c1, c2) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # If # Note: sympy only does piecewise, not if x = myokit.If(cond1, a, b) cx = sp.Piecewise((ca, c1), (cb, True)) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x.piecewise()) # Piecewise c = myokit.Number(1) cc = sp.Float(1) x = myokit.Piecewise(cond1, a, cond2, b, c) cx = sp.Piecewise((ca, c1), (cb, c2), (cc, True)) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Myokit piecewise's (like CellML's) always have a final True # condition (i.e. an 'else'). SymPy doesn't require this, so test if # we can import this --> It will add an "else 0" x = myokit.Piecewise(cond1, a, myokit.Number(0)) cx = sp.Piecewise((ca, c1)) self.assertEqual(r.ex(cx), x) # SymPy function without Myokit equivalent --> Should raise exception cu = sp.principal_branch(cx, cc) self.assertRaisesRegex(ValueError, 'Unsupported type', r.ex, cu) # Derivative m = self._model.clone() avar = m.get('c.a') r = mypy.SymPyExpressionReader(self._model) avar.promote(4) x = myokit.Derivative(self._a) cx = sp.symbols('dot(c.a)') self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Equation e = myokit.Equation(a, b) ce = sp.Eq(ca, cb) self.assertEqual(w.eq(e), ce) # There's no backwards equivalent for this! # The ereader can handle it, but it becomes and Equals expression. # Test sympy division del (m, avar, x, cx, e, ce) a = self._model.get('c.a') b = self._model.get('c').add_variable('bbb') b.set_rhs('1 / a') e = b.rhs() ce = w.ex(b.rhs()) e = r.ex(ce) self.assertEqual( e, myokit.Multiply(myokit.Number(1), myokit.Power(myokit.Name(a), myokit.Number(-1)))) # Test sympy negative numbers a = self._model.get('c.a') e1 = myokit.PrefixMinus(myokit.Name(a)) ce = w.ex(e1) e2 = r.ex(ce) self.assertEqual(e1, e2)
def xml_to_sympy_expr(self, xml_node): if isTag(xml_node, '/MathML}cn'): return float(xml_node.text) elif isTag(xml_node, '/MathML}ci'): if xml_node.text not in self.symbol_table: self.symbol_table[xml_node.text] = sp.Symbol(xml_node.text) self.extra_assignments[self.symbol_table[xml_node.text]] = None return self.symbol_table[xml_node.text] elif not isTag(xml_node, '/MathML}apply'): raise ValueError("Do not know hot to parse node %s" % xml_node) try: func, *arg_children = filterTags(xml_node.getchildren()) except ValueError: raise processed_args = [] for child in arg_children: processed_args.append(self.xml_to_sympy_expr(child)) if isTag(func, '/MathML}plus'): out_expr = sp.Add(*processed_args) elif isTag(func, '/MathML}times'): out_expr = sp.Mul(*processed_args) elif isTag(func, '/MathML}power'): if len(processed_args) != 2: raise ValueError("Power is a binary operation!") out_expr = sp.Pow(*processed_args) elif isTag(func, '/MathML}divide'): if len(processed_args) != 2: raise ValueError("Division is a binary operation!") out_expr = sp.Mul(processed_args[0], sp.Pow(processed_args[1], -1)) elif isTag(func, '/MathML}minus'): if (len(processed_args) > 2) or (len(processed_args) == 0): raise ValueError("Subtraction is a unitary or binary operation!") elif len(processed_args) == 2: out_expr = sp.Add(processed_args[0], sp.Mul(processed_args[1], -1)) elif len(processed_args) == 1: out_expr = sp.Mul(processed_args[0], -1) elif isTag(func, '/MathML}abs'): if len(processed_args) != 1: raise ValueError("Absolute value is a unitary operation!") out_expr = sp.Abs(processed_args[0]) elif isTag(func, '/MathML}cos'): if len(processed_args) != 1: raise ValueError("cos is a unitary operation!") out_expr = sp.cos(processed_args[0]) elif isTag(func, '/MathML}sin'): if len(processed_args) != 1: raise ValueError("sin is a unitary operation!") out_expr = sp.sin(processed_args[0]) elif isTag(func, '/MathML}csymbol'): if func.text == 'atan2': if len(processed_args) != 2: raise ValueError("atan2 is a binary operation!") out_expr = sp.atan2(*processed_args) else: raise ValueError("Unknown csymbol %s" % func.text) elif isTag(func, '/MathML}piecewise'): *pieces, otherwise = func.getchildren() otherwise_children = filterTags(otherwise.getchildren()) conditions = [] values = [] if len(otherwise_children) != 1: raise ValueError("Too many `otherwise` children") for piece in filterTags(pieces): value_child, condition_child = filterTags(piece.getchildren()) values.append(self.xml_to_sympy_expr(value_child)) conditions.append(self.xml_to_sympy_expr(condition_child)) values.append(self.xml_to_sympy_expr(otherwise_children[0])) conditions.append(True) out_expr = sp.Piecewise(*[(value, condition) for value, condition in zip(values, conditions)]) elif isTag(func, '/MathML}lt'): if len(processed_args) != 2: raise ValueError("Less than is a binary operation!") out_expr = sp.Le(*processed_args) elif isTag(func, '/MathML}gt'): if len(processed_args) != 2: raise ValueError("Greater than is a binary operation!") out_expr = sp.Gt(*processed_args) elif isTag(func, '/MathML}leq'): if len(processed_args) != 2: raise ValueError("Less than equal to is a binary operation!") out_expr = sp.Le(*processed_args) elif isTag(func, '/MathML}geq'): if len(processed_args) != 2: raise ValueError("Greater than equal to is a binary operation!") out_expr = sp.Ge(*processed_args) else: raise ValueError("Unknown func tag, %s" % func.tag) if out_expr is None: raise ValueError("Calculated None") else: return out_expr
def construct_objective_function(window_length, step_length, n_classes): p = [sympy.Symbol("P_" + str(i)) for i in range(n_classes)] c = [sympy.Symbol("C_" + str(i)) for i in range(n_classes)] m = sympy.Symbol("P(M)") p_given_m = [ sympy.Symbol("P(P_" + str(i) + "|M)") for i in range(n_classes) ] p_given_c = [[ sympy.Symbol("P(P_" + str(i) + "|C_" + str(j) + ")") for j in range(n_classes) ] for i in range(n_classes)] c_given_m = [ sympy.Symbol("P(C_" + str(j) + "|M)") for j in range(n_classes) ] m_given_c = [ sympy.Symbol("P(M|C_" + str(j) + ")") for j in range(n_classes) ] p_given_cm = [[ sympy.Symbol("P(P_" + str(i) + "|C_" + str(j) + ",M)") for j in range(n_classes) ] for i in range(n_classes)] f_given_c = [[ sympy.Function("F_" + str(i) + "C_" + str(j) + "") for j in range(n_classes) ] for i in range(n_classes)] t = [sympy.Symbol("t_" + str(i)) for i in range(n_classes)] itr = sympy.Add(*[ p_given_cm[i][j] * c_given_m[j] * sympy.Piecewise( (0, sympy.Le(p_given_m[i], 0)), (0, sympy.Le(p_given_cm[i][j], 0)), (sympy.log(p_given_cm[i][j] / p_given_m[i], 2), True)) for i in range(n_classes) for j in range(n_classes) ]) # itr = sympy.Add(*[p_given_cm[i][j]*c_given_m[j]*sympy.log(p_given_cm[i][j]/p_given_m[i], 2) for i in range(n_classes) for j in range(n_classes)]) mdt = window_length + (1 / m - 1) * step_length unfolded_itr = itr * 60 / mdt unfolded_itr = unfolded_itr.subs({ p_given_cm[i][j]: p_given_c[i][j] / m_given_c[j] for i in range(n_classes) for j in range(n_classes) }) unfolded_itr = unfolded_itr.subs({ m_given_c[j]: sympy.Add(*[p_given_c[i][j] for i in range(n_classes)]) for j in range(n_classes) }) unfolded_itr = unfolded_itr.subs({ c_given_m[j]: sympy.Add(*[p_given_c[i][j] * c[j] / m for i in range(n_classes)]) for j in range(n_classes) }) unfolded_itr = unfolded_itr.subs( {p_given_m[i]: p[i] / m for i in range(n_classes)}) unfolded_itr = unfolded_itr.subs( {m: sympy.Add(*[p[i] for i in range(n_classes)])}) unfolded_itr = unfolded_itr.subs({ p[i]: sympy.Add(*[p_given_c[i][j] * c[j] for j in range(n_classes)]) for i in range(n_classes) }) unfolded_itr = unfolded_itr.subs( {c[j]: sympy.Integer(1) / n_classes for j in range(n_classes)}) unfolded_itr = unfolded_itr.subs({ p_given_c[i][k]: (1 - f_given_c[i][k](t[i])) * sympy.Mul(*[f_given_c[j][k](t[j]) for j in range(n_classes) if j != i]) for i in range(n_classes) for k in range(n_classes) }) itr_gradient = [unfolded_itr.diff(t[i]) for i in range(n_classes)] itr_function = sympy.lambdify( [e(t[i]) for i, l in enumerate(f_given_c) for e in l], unfolded_itr, "numpy") gradient_arguments = [ sympy.Derivative(f_given_c[i][j](t[i]), t[i]) for i in range(n_classes) for j in range(n_classes) ] gradient_arguments += [ f_given_c[i][j](t[i]) for i in range(n_classes) for j in range(n_classes) ] gradient_function = sympy.lambdify(gradient_arguments, itr_gradient, "numpy") return itr_function, gradient_function