Ejemplo n.º 1
0
    def build(self):
        self.code_lines.append(
            f'  {constants.DT}_sqrt = {constants.DT} ** 0.5')

        # 2.1 noise
        _noise_terms(self.code_lines, self.variables, triple_integral=False)

        # 2.2 stage 1
        _state1(self.code_lines, self.variables, self.parameters)

        # 2.3 stage 2
        # ----
        # H1s2 = x + dt * f_H0s1 + dt_sqrt * g_H1s1
        # g_H1s2 = g(H1s2, t0, *args)
        all_H1s2 = []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H1s2 = {var} + {constants.DT} * {var}_f_H0s1 + dt_sqrt * {var}_g_H1s1'
            )
            all_H1s2.append(f'{var}_H1s2')
        g_names = [f'{var}_g_H1s2' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s2 + self.parameters)})'
        )
        self.code_lines.append('  ')

        # 2.4 final stage
        # ----
        # g1 = (I1 - I11 / dt_sqrt + I10 / dt)
        # g2 = I11 / dt_sqrt
        # y1 = x + dt * f_H0s1 + g1 * g_H1s1 + g2 * g_H1s2
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_g1 = -{var}_I1 + {var}_I11/dt_sqrt + {var}_I10/{constants.DT}'
            )
            self.code_lines.append(f'  {var}_g2 = {var}_I11 / dt_sqrt')
            self.code_lines.append(
                f'  {var}_new = {var} + {constants.DT} * {var}_f_H0s1 + '
                f'{var}_g1 * {var}_g_H1s1 + {var}_g2 * {var}_g_H1s2')
            self.code_lines.append('  ')

        # returns
        new_vars = [f'{var}_new' for var in self.variables]
        self.code_lines.append(f'  return {", ".join(new_vars)}')

        # return and compile
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)
Ejemplo n.º 2
0
 def build(self):
     # step stage
     common.step(self.variables, C.DT, self.A, self.C, self.code_lines,
                 self.parameters)
     # variable update
     return_args = common.update(self.variables, C.DT, self.B,
                                 self.code_lines)
     # returns
     self.code_lines.append(f'  return {", ".join(return_args)}')
     # compile
     self.integral = utils.compile_code(
         code_scope={k: v
                     for k, v in self.code_scope.items()},
         code_lines=self.code_lines,
         show_code=self.show_code,
         func_name=self.func_name)
Ejemplo n.º 3
0
 def build(self):
     # step stage
     common.step(self.variables, C.DT, self.A, self.C, self.code_lines,
                 self.parameters)
     # variable update
     return_args = common.update(self.variables, C.DT, self.B1,
                                 self.code_lines)
     # error adaptive item
     if self.adaptive:
         errors_ = []
         for v in self.variables:
             result = []
             for i, (b1, b2) in enumerate(zip(self.B1, self.B2)):
                 if isinstance(b1, str):
                     b1 = eval(b1)
                 if isinstance(b2, str):
                     b2 = eval(b2)
                 diff = b1 - b2
                 if diff != 0.:
                     result.append(f'd{v}_k{i + 1} * {C.DT} * {diff}')
             if len(result) > 0:
                 if self.var_type == C.SCALAR_VAR:
                     self.code_lines.append(
                         f'  {v}_te = abs({" + ".join(result)})')
                 else:
                     self.code_lines.append(
                         f'  {v}_te = sum(abs({" + ".join(result)}))')
                 errors_.append(f'{v}_te')
         if len(errors_) > 0:
             self.code_lines.append(f'  error = {" + ".join(errors_)}')
             self.code_lines.append(
                 f'  {C.DT}_new = math.where(error > tol, 0.9*{C.DT}*(tol/error)**0.2, {C.DT})'
             )
             return_args.append(f'{C.DT}_new')
     # returns
     self.code_lines.append(f'  return {", ".join(return_args)}')
     # compile
     self.integral = utils.compile_code(
         code_scope={k: v
                     for k, v in self.code_scope.items()},
         code_lines=self.code_lines,
         show_code=self.show_code,
         func_name=self.func_name)
Ejemplo n.º 4
0
    def build(self):
        self.code_lines.append(
            f'  {constants.DT}_sqrt = {constants.DT} ** 0.5')

        # 2.1 df, dg
        df_and_dg(self.code_lines, self.variables, self.parameters)

        # 2.2 dfdt
        dfdt(self.code_lines, self.variables)

        # 2.3 dW
        noise_terms(self.code_lines, self.variables)

        # 2.3 dgdW
        # ----
        # SCALAR_WIENER : dg * dW
        # VECTOR_WIENER : math.sum(dg * dW, axis=-1)

        if self.wiener_type == constants.SCALAR_WIENER:
            for var in self.variables:
                self.code_lines.append(f'  {var}_dgdW = {var}_dg * {var}_dW')
        else:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_dgdW = math.sum({var}_dg * {var}_dW, axis=-1)')
        self.code_lines.append('  ')

        if self.intg_type == constants.ITO_SDE:
            # 2.4 new var
            # ----
            # y = x + dfdt + dgdW
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_new = {var} + {var}_dfdt + {var}_dgdW')
            self.code_lines.append('  ')

        elif self.intg_type == constants.STRA_SDE:
            # 2.4  y_bar = x + math.sum(dgdW, axis=-1)
            all_bar = [f'{var}_bar' for var in self.variables]
            for var in self.variables:
                self.code_lines.append(f'  {var}_bar = {var} + {var}_dgdW')
            self.code_lines.append('  ')

            # 2.5  dg_bar = g(y_bar, t, *args)
            all_dg_bar = [f'{var}_dg_bar' for var in self.variables]
            self.code_lines.append(
                f'  {", ".join(all_dg_bar)} = g({", ".join(all_bar + self.parameters)})'
            )

            # 2.6 dgdW2
            # ----
            # SCALAR_WIENER : dgdW2 = dg_bar * dW
            # VECTOR_WIENER : dgdW2 = math.sum(dg_bar * dW, axis=-1)
            if self.wiener_type == constants.SCALAR_WIENER:
                for var in self.variables:
                    self.code_lines.append(
                        f'  {var}_dgdW2 = {var}_dg_bar * {var}_dW')
            else:
                for var in self.variables:
                    self.code_lines.append(
                        f'  {var}_dgdW2 = math.sum({var}_dg_bar * {var}_dW, axis=-1)'
                    )
            self.code_lines.append('  ')

            # 2.7 new var
            # ----
            # y = x + dfdt + 0.5 * (dgdW + dgdW2)
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_new = {var} + {var}_dfdt + 0.5 * ({var}_dgdW + {var}_dgdW2)'
                )
            self.code_lines.append('  ')
        else:
            raise ValueError(
                f'Unknown SDE_INT type: {self.intg_type}. We only '
                f'supports {constants.SUPPORTED_INTG_TYPE}.')

        # returns
        new_vars = [f'{var}_new' for var in self.variables]
        self.code_lines.append(f'  return {", ".join(new_vars)}')

        # return and compile
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)
Ejemplo n.º 5
0
    def symbolic_build(self):
        if self.var_type == constants.SYSTEM_VAR:
            raise errors.IntegratorError(
                f'Exponential Euler method do not support {self.var_type} variable type.'
            )
        if self.intg_type != constants.ITO_SDE:
            raise errors.IntegratorError(
                f'Exponential Euler method only supports Ito integral, but we got {self.intg_type}.'
            )

        if sympy is None or analysis_by_sympy is None:
            raise errors.PackageMissingError('SymPy must be installed when '
                                             'using exponential integrators.')

        # check bound method
        if hasattr(self.derivative[constants.F], '__self__'):
            self.code_lines = [
                f'def {self.func_name}({", ".join(["self"] + list(self.arguments))}):'
            ]

        # 1. code scope
        closure_vars = inspect.getclosurevars(self.derivative[constants.F])
        self.code_scope.update(closure_vars.nonlocals)
        self.code_scope.update(dict(closure_vars.globals))
        self.code_scope['math'] = math

        # 2. code lines
        code_lines = self.code_lines
        # code_lines = [f'def {self.func_name}({", ".join(self.arguments)}):']
        code_lines.append(f'  {constants.DT}_sqrt = {constants.DT} ** 0.5')

        # 2.1 dg
        # dg = g(x, t, *args)
        all_dg = [f'{var}_dg' for var in self.variables]
        code_lines.append(
            f'  {", ".join(all_dg)} = g({", ".join(self.variables + self.parameters)})'
        )
        code_lines.append('  ')

        # 2.2 dW
        noise_terms(code_lines, self.variables)

        # 2.3 dgdW
        # ----
        # SCALAR_WIENER : dg * dW
        # VECTOR_WIENER : math.sum(dg * dW, axis=-1)

        if self.wiener_type == constants.SCALAR_WIENER:
            for var in self.variables:
                code_lines.append(f'  {var}_dgdW = {var}_dg * {var}_dW')
        else:
            for var in self.variables:
                code_lines.append(
                    f'  {var}_dgdW = math.sum({var}_dg * {var}_dW, axis=-1)')
        code_lines.append('  ')

        # 2.4 new var
        # ----
        analysis = separate_variables(self.derivative[constants.F])
        variables_for_returns = analysis['variables_for_returns']
        expressions_for_returns = analysis['expressions_for_returns']
        for vi, (key, vars) in enumerate(variables_for_returns.items()):
            # separate variables
            sd_variables = []
            for v in vars:
                if len(v) > 1:
                    raise ValueError(
                        'Cannot analyze multi-assignment code line.')
                sd_variables.append(v[0])
            expressions = expressions_for_returns[key]
            var_name = self.variables[vi]
            diff_eq = analysis_by_sympy.SingleDiffEq(var_name=var_name,
                                                     variables=sd_variables,
                                                     expressions=expressions,
                                                     derivative_expr=key,
                                                     scope=self.code_scope,
                                                     func_name=self.func_name)

            f_expressions = diff_eq.get_f_expressions(
                substitute_vars=diff_eq.var_name)

            # code lines
            code_lines.extend(
                [f"  {str(expr)}" for expr in f_expressions[:-1]])

            # get the linear system using sympy
            f_res = f_expressions[-1]
            df_expr = analysis_by_sympy.str2sympy(f_res.code).expr.expand()
            s_df = sympy.Symbol(f"{f_res.var_name}")
            code_lines.append(
                f'  {s_df.name} = {analysis_by_sympy.sympy2str(df_expr)}')
            var = sympy.Symbol(diff_eq.var_name, real=True)

            # get df part
            s_linear = sympy.Symbol(f'_{diff_eq.var_name}_linear')
            s_linear_exp = sympy.Symbol(f'_{diff_eq.var_name}_linear_exp')
            s_df_part = sympy.Symbol(f'_{diff_eq.var_name}_df_part')
            if df_expr.has(var):
                # linear
                linear = sympy.collect(df_expr, var, evaluate=False)[var]
                code_lines.append(
                    f'  {s_linear.name} = {analysis_by_sympy.sympy2str(linear)}'
                )
                # linear exponential
                code_lines.append(
                    f'  {s_linear_exp.name} = math.exp({analysis_by_sympy.sympy2str(linear)} * {constants.DT})'
                )
                # df part
                df_part = (s_linear_exp - 1) / s_linear * s_df
                code_lines.append(
                    f'  {s_df_part.name} = {analysis_by_sympy.sympy2str(df_part)}'
                )

            else:
                # linear exponential
                code_lines.append(
                    f'  {s_linear_exp.name} = {constants.DT}_sqrt')
                # df part
                code_lines.append(
                    f'  {s_df_part.name} = {s_df.name} * {constants.DT}')

            # update expression
            update = var + s_df_part

            # The actual update step
            code_lines.append(
                f'  {diff_eq.var_name}_new = {analysis_by_sympy.sympy2str(update)} + {var_name}_dgdW'
            )
            code_lines.append('')

        # returns
        new_vars = [f'{var}_new' for var in self.variables]
        code_lines.append(f'  return {", ".join(new_vars)}')

        # return and compile
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)

        if hasattr(self.derivative[constants.F], '__self__'):
            host = self.derivative[constants.F].__self__
            self.integral = self.integral.__get__(host, host.__class__)
Ejemplo n.º 6
0
    def build(self):
        # 2. code lines
        self.code_lines.append(
            f'  {constants.DT}_sqrt = {constants.DT} ** 0.5')

        # 2.1 df, dg
        df_and_dg(self.code_lines, self.variables, self.parameters)

        # 2.2 dfdt
        dfdt(self.code_lines, self.variables)

        # 2.3 dW
        noise_terms(self.code_lines, self.variables)

        # 2.3 dgdW
        # ----
        # dg * dW
        for var in self.variables:
            self.code_lines.append(f'  {var}_dgdW = {var}_dg * {var}_dW')
        self.code_lines.append('  ')

        # 2.4  df_bar = x + dfdt + math.sum(dg * dt_sqrt, axis=-1)
        all_df_bar = [f'{var}_df_bar' for var in self.variables]
        if self.wiener_type == constants.SCALAR_WIENER:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_df_bar = {var} + {var}_dfdt + {var}_dg * {constants.DT}_sqrt'
                )
        else:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_df_bar = {var} + {var}_dfdt + math.sum('
                    f'{var}_dg * {constants.DT}_sqrt, axis=-1)')

        # 2.5  dg_bar = g(y_bar, t, *args)
        all_dg_bar = [f'{var}_dg_bar' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(all_dg_bar)} = g({", ".join(all_df_bar + self.parameters)})'
        )
        self.code_lines.append('  ')

        # 2.6 dgdW2
        # ----
        # dgdW2 = 0.5 * (dg_bar - dg) * (dW * dW / dt_sqrt - dt_sqrt)
        if self.intg_type == constants.ITO_SDE:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_dgdW2 = 0.5 * ({var}_dg_bar - {var}_dg) * '
                    f'({var}_dW * {var}_dW / {constants.DT}_sqrt - {constants.DT}_sqrt)'
                )
        elif self.intg_type == constants.STRA_SDE:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_dgdW2 = 0.5 * ({var}_dg_bar - {var}_dg) * '
                    f'{var}_dW * {var}_dW / {constants.DT}_sqrt')
        else:
            raise ValueError(f'Unknown SDE_INT type: {self.intg_type}')
        self.code_lines.append('  ')

        # 2.7 new var
        # ----
        # SCALAR_WIENER : y = x + dfdt + dgdW + dgdW2
        # VECTOR_WIENER : y = x + dfdt + math.sum(dgdW + dgdW2, axis=-1)
        if self.wiener_type == constants.SCALAR_WIENER:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_new = {var} + {var}_dfdt + {var}_dgdW + {var}_dgdW2'
                )
        elif self.wiener_type == constants.VECTOR_WIENER:
            for var in self.variables:
                self.code_lines.append(
                    f'  {var}_new = {var} + {var}_dfdt + math.sum({var}_dgdW + {var}_dgdW2, axis=-1)'
                )
        else:
            raise ValueError(f'Unknown Wiener Process : {self.wiener_type}')
        self.code_lines.append('  ')

        # returns
        new_vars = [f'{var}_new' for var in self.variables]
        self.code_lines.append(f'  return {", ".join(new_vars)}')

        # return and compile
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)
Ejemplo n.º 7
0
def _srk1_wrapper(f, g, dt, sde_type, var_type, wiener_type, show_code,
                  num_iter):
    vdt, variables, parameters, arguments, func_name = basic_info(f=f, g=g)

    # 1. code scope
    code_scope = {
        'f': f,
        'g': g,
        vdt: dt,
        f'{vdt}_sqrt': dt**0.5,
        'math': math,
        'num_iter': num_iter
    }

    # 2. code lines
    code_lines = [f'def {func_name}({", ".join(arguments)}):']

    if var_type == constants.SYSTEM_VAR:
        if len(variables) > 1:
            raise ValueError(
                f'SDE_INT with {constants.SYSTEM_VAR} variable type only '
                f'supports one system variable. But we got {variables}.')

        if wiener_type == constants.SCALAR_WIENER:
            _srk1_system_var_with_scalar_wiener(sde_type, code_lines,
                                                variables, parameters, vdt)
        elif wiener_type == constants.VECTOR_WIENER:
            _srk1_system_var_with_vector_wiener(sde_type, code_lines,
                                                variables, parameters, vdt)
        else:
            raise ValueError(f'Unknown Wiener type: {wiener_type}, we only '
                             f'supports {constants.SUPPORTED_WIENER_TYPE}')

    elif var_type == constants.SCALAR_VAR:
        if wiener_type == constants.SCALAR_WIENER:
            _srk2_pop_or_scalar_var_scalar_wiener(sde_type, code_lines,
                                                  variables, parameters, vdt)
        elif wiener_type == constants.VECTOR_WIENER:
            _srk1_scalar_var_with_vector_wiener(sde_type, code_lines,
                                                variables, parameters, vdt)
        else:
            raise ValueError(f'Unknown Wiener type: {wiener_type}, we only '
                             f'supports {constants.SUPPORTED_WIENER_TYPE}')

    elif var_type == constants.POP_VAR:
        if wiener_type == constants.SCALAR_WIENER:
            _srk2_pop_or_scalar_var_scalar_wiener(sde_type, code_lines,
                                                  variables, parameters, vdt)
        elif wiener_type == constants.VECTOR_WIENER:
            _srk2_pop_var_vector_wiener(sde_type, code_lines, variables,
                                        parameters, vdt)
        else:
            raise ValueError(f'Unknown Wiener type: {wiener_type}, we only '
                             f'supports {constants.SUPPORTED_WIENER_TYPE}')

    else:
        raise ValueError(f'Unknown var type: {var_type}, we only '
                         f'supports {constants.SUPPORTED_VAR_TYPE}')
    # returns
    new_vars = [f'{var}_new' for var in variables]
    code_lines.append(f'  return {", ".join(new_vars)}')

    # return and compile
    utils.compile_code(code_lines, code_scope, show_code, variables)
    return code_scope[func_name]
Ejemplo n.º 8
0
    def build(self):
        self.code_lines.append(
            f'  {constants.DT}_sqrt = {constants.DT} ** 0.5')

        # 2.1 noise
        _noise_terms(self.code_lines, self.variables, triple_integral=True)

        # 2.2 stage 1
        _state1(self.code_lines, self.variables, self.parameters)

        # 2.3 stage 2
        # ----
        # H0s2 = x + dt * f_H0s1
        # H1s2 = x + dt * 0.25 * f_H0s1 - dt_sqrt * 0.5 * g_H1s1
        # f_H0s2 = f(H0s2, t + dt, *args)
        # g_H1s2 = g(H1s2, t + 0.25 * dt, *args)
        all_H0s2, all_H1s2 = [], []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H0s2 = {var} + {constants.DT} * {var}_f_H0s1')
            all_H0s2.append(f'{var}_H0s2')
            self.code_lines.append(
                f'  {var}_H1s2 = {var} + {constants.DT} * 0.25 * {var}_f_H0s1 - '
                f'dt_sqrt * 0.5 * {var}_g_H1s1')
            all_H1s2.append(f'{var}_H1s2')
        all_H0s2.append(f't + {constants.DT}')  # t
        all_H1s2.append(f't + 0.25 * {constants.DT}')  # t
        f_names = [f'{var}_f_H0s2' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(f_names)} = f({", ".join(all_H0s2 + self.parameters[1:])})'
        )
        g_names = [f'{var}_g_H1s2' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s2 + self.parameters[1:])})'
        )
        self.code_lines.append('  ')

        # 2.4 state 3
        # ---
        # H0s3 = x + dt * (0.25 * f_H0s1 + 0.25 * f_H0s2) + (g_H1s1 + 0.5 * g_H1s2) * I10 / dt
        # H1s3 = x + dt * f_H0s1 + dt_sqrt * g_H1s1
        # f_H0s3 = g(H0s3, t + 0.5 * dt, *args)
        # g_H1s3 = g(H1s3, t + dt, *args)
        all_H0s3, all_H1s3 = [], []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H0s3 = {var} + {constants.DT} * (0.25 * {var}_f_H0s1 + 0.25 * {var}_f_H0s2) + '
                f'({var}_g_H1s1 + 0.5 * {var}_g_H1s2) * {var}_I10 / {constants.DT}'
            )
            all_H0s3.append(f'{var}_H0s3')
            self.code_lines.append(
                f'  {var}_H1s3 = {var} + {constants.DT} * {var}_f_H0s1 + dt_sqrt * {var}_g_H1s1'
            )
            all_H1s3.append(f'{var}_H1s3')
        all_H0s3.append(f't + 0.5 * {constants.DT}')  # t
        all_H1s3.append(f't + {constants.DT}')  # t
        f_names = [f'{var}_f_H0s3' for var in self.variables]
        g_names = [f'{var}_g_H1s3' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(f_names)} = f({", ".join(all_H0s3 + self.parameters[1:])})'
        )
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s3 + self.parameters[1:])})'
        )
        self.code_lines.append('  ')

        # 2.5 state 4
        # ----
        # H1s4 = x + dt * 0.25 * f_H0s3 + dt_sqrt * (2 * g_H1s1 - g_H1s2 + 0.5 * g_H1s3)
        # g_H1s4 = g(H1s4, t + 0.25 * dt, *args)
        all_H1s4 = []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H1s4 = {var} + 0.25 * {constants.DT} * {var}_f_H0s1 + dt_sqrt * '
                f'(2 * {var}_g_H1s1 - {var}_g_H1s2 + 0.5 * {var}_g_H1s3)')
            all_H1s4.append(f'{var}_H1s4')
        all_H1s4.append(f't + 0.25 * {constants.DT}')  # t
        g_names = [f'{var}_g_H1s4' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s4 + self.parameters[1:])})'
        )
        self.code_lines.append('  ')

        # 2.6 final stage
        # ----
        # f1 = f_H0s1 / 6 + f_H0s2 / 6 + f_H0s3 * 2 / 3
        # g1 = - I1 + I11 / dt_sqrt + 2 * I10 / dt - 2 * I111 / dt
        # g2 = I1 * 4 / 3 - I11 / dt_sqrt * 4 / 3 - I10 / dt * 4 / 3 + I111 / dt * 5 / 3
        # g3 = I1 * 2 / 3 + I11 / dt_sqrt / 3 - I10 / dt * 2 / 3 - I111 / dt * 2 / 3
        # g4 = I111 / dt
        # y1 = x + dt * f1 + g1 * g_H1s1 + g2 * g_H1s2 + g3 * g_H1s3 + g4 * g_H1s4
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_f1 = {var}_f_H0s1/6 + {var}_f_H0s2/6 + {var}_f_H0s3*2/3'
            )
            self.code_lines.append(
                f'  {var}_g1 = -{var}_I1 + {var}_I11/dt_sqrt + 2 * {var}_I10/{constants.DT} - 2 * {var}_I111/{constants.DT}'
            )
            self.code_lines.append(
                f'  {var}_g2 = {var}_I1 * 4/3 - {var}_I11 / dt_sqrt * 4/3 - '
                f'{var}_I10 / {constants.DT} * 4/3 + {var}_I111 / {constants.DT} * 5/3'
            )
            self.code_lines.append(
                f'  {var}_g3 = {var}_I1 * 2/3 + {var}_I11/dt_sqrt/3 - '
                f'{var}_I10 / {constants.DT} * 2/3 - {var}_I111 / {constants.DT} * 2/3'
            )
            self.code_lines.append(f'  {var}_g4 = {var}_I111 / {constants.DT}')
            self.code_lines.append(
                f'  {var}_new = {var} + {constants.DT} * {var}_f1 + {var}_g1 * {var}_g_H1s1 + '
                f'{var}_g2 * {var}_g_H1s2 + {var}_g3 * {var}_g_H1s3 + {var}_g4 * {var}_g_H1s4'
            )
            self.code_lines.append('  ')

        # returns
        new_vars = [f'{var}_new' for var in self.variables]
        self.code_lines.append(f'  return {", ".join(new_vars)}')

        # return and compile
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)
Ejemplo n.º 9
0
    def build(self):
        # 2. code lines
        self.code_lines.append(
            f'  {constants.DT}_sqrt = {constants.DT} ** 0.5')

        # 2.1 noise
        _noise_terms(self.code_lines, self.variables, triple_integral=True)

        # 2.2 stage 1
        _state1(self.code_lines, self.variables, self.parameters)

        # 2.3 stage 2
        all_H0s2, all_H1s2 = [], []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H0s2 = {var} + {constants.DT} * 0.75 * {var}_f_H0s1 + '
                f'1.5 * {var}_g_H1s1 * {var}_I10 / {constants.DT}')
            all_H0s2.append(f'{var}_H0s2')
            self.code_lines.append(
                f'  {var}_H1s2 = {var} + {constants.DT} * 0.25 * {var}_f_H0s1 + '
                f'dt_sqrt * 0.5 * {var}_g_H1s1')
            all_H1s2.append(f'{var}_H1s2')
        all_H0s2.append(f't + 0.75 * {constants.DT}')  # t
        all_H1s2.append(f't + 0.25 * {constants.DT}')  # t
        f_names = [f'{var}_f_H0s2' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(f_names)} = f({", ".join(all_H0s2 + self.parameters[1:])})'
        )
        g_names = [f'{var}_g_H1s2' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s2 + self.parameters[1:])})'
        )
        self.code_lines.append('  ')

        # 2.4 state 3
        all_H1s3 = []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H1s3 = {var} + {constants.DT} * {var}_f_H0s1 - dt_sqrt * {var}_g_H1s1'
            )
            all_H1s3.append(f'{var}_H1s3')
        all_H1s3.append(f't + {constants.DT}')  # t
        g_names = [f'{var}_g_H1s3' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s3 + self.parameters[1:])})'
        )
        self.code_lines.append('  ')

        # 2.5 state 4
        all_H1s4 = []
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_H1s4 = {var} + 0.25 * {constants.DT} * {var}_f_H0s1 + dt_sqrt * '
                f'(-5 * {var}_g_H1s1 + 3 * {var}_g_H1s2 + 0.5 * {var}_g_H1s3)')
            all_H1s4.append(f'{var}_H1s4')
        all_H1s4.append(f't + 0.25 * {constants.DT}')  # t
        g_names = [f'{var}_g_H1s4' for var in self.variables]
        self.code_lines.append(
            f'  {", ".join(g_names)} = g({", ".join(all_H1s4 + self.parameters[1:])})'
        )
        self.code_lines.append('  ')

        # 2.6 final stage
        for var in self.variables:
            self.code_lines.append(
                f'  {var}_f1 = {var}_f_H0s1/3 + {var}_f_H0s2 * 2/3')
            self.code_lines.append(
                f'  {var}_g1 = -{var}_I1 - {var}_I11/dt_sqrt + 2 * {var}_I10/{constants.DT} - 2 * {var}_I111/{constants.DT}'
            )
            self.code_lines.append(
                f'  {var}_g2 = {var}_I1 * 4/3 + {var}_I11 / dt_sqrt * 4/3 - '
                f'{var}_I10 / {constants.DT} * 4/3 + {var}_I111 / {constants.DT} * 5/3'
            )
            self.code_lines.append(
                f'  {var}_g3 = {var}_I1 * 2/3 - {var}_I11/dt_sqrt/3 - '
                f'{var}_I10 / {constants.DT} * 2/3 - {var}_I111 / {constants.DT} * 2/3'
            )
            self.code_lines.append(f'  {var}_g4 = {var}_I111 / {constants.DT}')
            self.code_lines.append(
                f'  {var}_new = {var} + {constants.DT} * {var}_f1 + {var}_g1 * {var}_g_H1s1 + '
                f'{var}_g2 * {var}_g_H1s2 + {var}_g3 * {var}_g_H1s3 + {var}_g4 * {var}_g_H1s4'
            )
            self.code_lines.append('  ')

        # returns
        new_vars = [f'{var}_new' for var in self.variables]
        self.code_lines.append(f'  return {", ".join(new_vars)}')

        # return and compile
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)
Ejemplo n.º 10
0
    def build(self):
        if analysis_by_sympy is None or sympy is None:
            raise errors.PackageMissingError(
                f'Package "sympy" must be installed when the users '
                f'want to utilize {ExponentialEuler.__name__}. ')

        # check bound method
        if hasattr(self.f, '__self__'):
            self.code_lines = [
                f'def {self.func_name}({", ".join(["self"] + list(self.arguments))}):'
            ]

        # code scope
        closure_vars = inspect.getclosurevars(self.f)
        self.code_scope.update(closure_vars.nonlocals)
        self.code_scope.update(dict(closure_vars.globals))
        self.code_scope['math'] = math

        analysis = separate_variables(self.f)
        variables_for_returns = analysis['variables_for_returns']
        expressions_for_returns = analysis['expressions_for_returns']
        for vi, (key, all_var) in enumerate(variables_for_returns.items()):
            # separate variables
            sd_variables = []
            for v in all_var:
                if len(v) > 1:
                    raise ValueError(
                        f'Cannot analyze multi-assignment code line: {v}.')
                sd_variables.append(v[0])
            expressions = expressions_for_returns[key]
            var_name = self.variables[vi]
            diff_eq = analysis_by_sympy.SingleDiffEq(var_name=var_name,
                                                     variables=sd_variables,
                                                     expressions=expressions,
                                                     derivative_expr=key,
                                                     scope=self.code_scope,
                                                     func_name=self.func_name)
            var = sympy.Symbol(diff_eq.var_name, real=True)
            try:
                s_df_part = tools.timeout(self.timeout)(self.solve)(diff_eq,
                                                                    var)
            except KeyboardInterrupt:
                raise errors.DiffEqError(
                    f'{self.__class__} solve {self.f} failed, because '
                    f'symbolic differentiation of SymPy timeout due to {self.timeout} s limit. '
                    f'Instead, you can use {ExpEulerAuto} to make Exponential Euler '
                    f'integration due to due to it is capable of '
                    f'performing automatic differentiation.')
            # update expression
            update = var + s_df_part

            # The actual update step
            self.code_lines.append(
                f'  {diff_eq.var_name}_new = {analysis_by_sympy.sympy2str(update)}'
            )
            self.code_lines.append('')

        self.code_lines.append(
            f'  return {", ".join([f"{v}_new" for v in self.variables])}')
        self.integral = utils.compile_code(
            code_scope={k: v
                        for k, v in self.code_scope.items()},
            code_lines=self.code_lines,
            show_code=self.show_code,
            func_name=self.func_name)

        if hasattr(self.f, '__self__'):
            host = self.f.__self__
            self.integral = self.integral.__get__(host, host.__class__)