def test_multidimensional_derivatives(self):
        exprs = [ex('x**2 + y'), ex('x*y + 5')]

        der1 = multidimensional_derivative(exprs, ['x', 'y'])
        der2 = multidimensional_derivative(der1, ['x', 'y'])

        func1 = multidimensional_lambdify(['x', 'y'], der1)
        func2 = multidimensional_lambdify(['x', 'y'], der2)

        val1 = func1(3, 4)
        val2 = func2(3, 4)

        self.assertTrue(np.array_equal(val1, np.array([[6, 1], [4, 3]])))
        self.assertTrue(np.array_equal(val2,
                                       np.array([[[2, 0], [0, 0]], [[0, 1], [1, 0]]])))
    def test_multidimensional_derivatives(self):
        exprs = [ex('x**2 + y'), ex('x*y + 5')]

        der1 = multidimensional_derivative(exprs, ['x', 'y'])
        der2 = multidimensional_derivative(der1, ['x', 'y'])

        func1 = multidimensional_lambdify(['x', 'y'], der1)
        func2 = multidimensional_lambdify(['x', 'y'], der2)

        val1 = func1(3, 4)
        val2 = func2(3, 4)

        self.assertTrue(np.array_equal(val1, np.array([[6, 1], [4, 3]])))
        self.assertTrue(
            np.array_equal(val2, np.array([[[2, 0], [0, 0]], [[0, 1], [1,
                                                                       0]]])))
Example #3
0
    def build_odes(self, parameters: Sequence[str] = ()):
        parameters = tuple(parameters)

        active_constant_indexes = []
        active_constants = []  # type: List[Constant]
        inactive_constant_indexes = []
        inactive_constants = []  # type: List[Constant]
        rule_indexes = []
        rules = []  # type: List[Rule]
        state_indexes = []
        states = []  # type: List[State]
        
        for i, part in enumerate(self.parts):
            if isinstance(part, Constant):
                if part.name in parameters:
                    active_constant_indexes.append(i)
                    active_constants.append(part)
                else:
                    inactive_constant_indexes.append(i)
                    inactive_constants.append(part)
            elif isinstance(part, Rule):
                rule_indexes.append(i)
                rules.append(part)
            elif isinstance(part, State):
                state_indexes.append(i)
                states.append(part)

        # Substitute constants and rules into rules
        rules = Rule.topological_sort(rules)
        for i in range(len(rules)):
            rules[i] = rules[i].subs(inactive_constants)
            rules[i] = rules[i].subs(rules[i+1:])

        # Substitute constants and rules into odes
        states = tuple(state.subs(inactive_constants) for state in states)
        states = tuple(state.subs(rules) for state in states)

        # Replace symbols
        n_parameters = len(parameters)
        parameter_values = array([constant.value for constant in active_constants])

        n_states = len(states)
        state_names = tuple(state.name for state in states)
        initials = [state.initial.value for state in states]

        initials_function = lambdify(parameters, initials)

        all_parameters = ('t',) + state_names + parameters

        ode = [state.ode.value.to_sympy() for state in states]
        ode_function = lambdify(all_parameters, ode)

        jacobian = multidimensional_derivative(ode, state_names)
        jacobian_function = multidimensional_lambdify(all_parameters, jacobian)

        # Collect discontinuities
        discontinuities = [time for state in states for time in state.ode.value.discontinuities]
        discontinuities = tuple(sorted(set(discontinuities)))  # sorted distinct

        # Build dose functions
        # Map time to every dose given at that time
        dose_groups = dict()  # type: Dict[float, List[Expr]]
        for (i, state) in enumerate(states):
            for dose in state.doses:
                time = dose.time
                if time not in dose_groups:
                    # Initialize a dose at this time
                    dose_groups[time] = [state.name for state in states]  # list is going to be mutated

                # Replace the zero at the state's index with it's dose function
                # Time is guaranteed to be unique within a state so overwriting is fine
                dose_groups[time][i] = dose.value

        dose_functions = dict()
        for (time, exprs) in dose_groups.items():
            dose_functions[time] = lambdify(all_parameters, exprs)

        # Dose times
        dose_times = tuple(sorted(dose_groups.keys()))  # sorted distinct

        # Build output functions
        # TODO (drhagen): handle the discontinuities
        output_functions = dict()
        for i, part in zip(active_constant_indexes, active_constants):
            func = lambdify(all_parameters, part.name)
            output_functions.update({i: func, part.name: func, str(part.name): func})

        for i, part in zip(inactive_constant_indexes, inactive_constants):
            func = lambdify(all_parameters, part.value)
            output_functions.update({i: func, part.name: func, str(part.name): func})

        for i, part in zip(rule_indexes, rules):
            func = lambdify(all_parameters, part.value.to_sympy())
            output_functions.update({i: func, part.name: func, str(part.name): func})

        for i, part in zip(state_indexes, states):
            func = lambdify(all_parameters, part.name)
            output_functions.update({i: func, part.name: func, str(part.name): func})

        return IntegrableSystem(n_parameters, n_states, parameter_values, initials_function, ode_function,
                                jacobian_function, discontinuities, dose_functions, dose_times, output_functions)