def test_one_dimensional_derivative(self): exprs = [ex('x**2 + y'), ex('x*y + 5')] der1 = multidimensional_derivative(exprs, ['x', 'y']) der2 = multidimensional_derivative(der1, ['x', 'y']) self.assertTrue(np.array_equal(der1, np.array([[ex('2*x'), 1], [ex('y'), ex('x')]]))) self.assertTrue(np.array_equal(der2, np.array([[[2, 0], [0, 0]], [[0, 1], [1, 0]]])))
def test_one_dimensional_derivative(self): exprs = [ex('x**2 + y'), ex('x*y + 5')] der1 = multidimensional_derivative(exprs, ['x', 'y']) der2 = multidimensional_derivative(der1, ['x', 'y']) self.assertTrue( np.array_equal(der1, np.array([[ex('2*x'), 1], [ex('y'), ex('x')]]))) self.assertTrue( np.array_equal(der2, 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]]])))
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 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)