Esempio n. 1
0
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
Esempio n. 2
0
    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)
Esempio n. 3
0
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()
Esempio n. 4
0
 def gen_lessthaneq(self, ident, val):
     return sym.Le(val.exp_a, val.exp_b)
Esempio n. 5
0

# 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]))
Esempio n. 7
0
 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
Esempio n. 8
0
    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)
Esempio n. 9
0
    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