def mp_solve(f, p, x0=0.01, limits=None, method='muller'):
    if not limits:
        return mp.findroot(lambda x: f(x, p=p), x0, solver=method)
    else:
        if method == 'muller':
            method = 'anderson'
        return mp.findroot(lambda x: f(x, p=p), limits, solver=method)
Example #2
0
def mp_solve(f, x0=0.01, limits=None, method="muller"):
    if not limits:
        return mp.findroot(f, x0, solver=method)
    else:
        if method == "muller":
            method = "anderson"
        return mp.findroot(f, limits, solver=method)
Example #3
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        _prec, mp.prec = mp.prec, prec

        try:
            func = lambdify(self.poly.gen, self.expr)

            interval = self._get_interval()
            refined =  False

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    x0 = mpc(*map(str, interval.center))

                try:
                    root = findroot(func, x0)
                except ValueError:
                    interval = interval.refine()
                    refined = True
                    continue
                else:
                    if refined:
                        self._set_interval(interval)

                    break
        finally:
            mp.prec = _prec

        return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec)
Example #4
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        _prec, mp.prec = mp.prec, prec

        try:
            func = lambdify(self.poly.gen, self.expr)

            interval = self._get_interval()
            refined = False

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    x0 = mpc(*map(str, interval.center))

                try:
                    root = findroot(func, x0)
                except ValueError:
                    interval = interval.refine()
                    refined = True
                    continue
                else:
                    if refined:
                        self._set_interval(interval)

                    break
        finally:
            mp.prec = _prec

        return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec)
Example #5
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        with workprec(prec):
            g = self.poly.gen
            if not g.is_Symbol:
                d = Dummy('x')
                func = lambdify(d, self.expr.subs(g, d))
            else:
                func = lambdify(g, self.expr)

            interval = self._get_interval()
            if not self.is_real:
                # For complex intervals, we need to keep refining until the
                # imaginary interval is disjunct with other roots, that is,
                # until both ends get refined.
                ay = interval.ay
                by = interval.by
                while interval.ay == ay or interval.by == by:
                    interval = interval.refine()

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    x0 = mpc(*map(str, interval.center))
                try:
                    root = findroot(func, x0, verify=False)
                    # If the (real or complex) root is not in the 'interval',
                    # then keep refining the interval. This happens if findroot
                    # accidentally finds a different root outside of this
                    # interval because our initial estimate 'x0' was not close
                    # enough.
                    if self.is_real:
                        a = mpf(str(interval.a))
                        b = mpf(str(interval.b))
                        if a == b:
                            root = a
                            break
                        if not (a < root < b):
                            raise ValueError("Root not in the interval.")
                    else:
                        ax = mpf(str(interval.ax))
                        bx = mpf(str(interval.bx))
                        ay = mpf(str(interval.ay))
                        by = mpf(str(interval.by))
                        if ax == bx and ay == by:
                            root = ax + S.ImaginaryUnit * by
                            break
                        if not (ax < root.real < bx and ay < root.imag < by):
                            raise ValueError("Root not in the interval.")
                except ValueError:
                    interval = interval.refine()
                    continue
                else:
                    break

        return Float._new(root.real._mpf_,
                          prec) + I * Float._new(root.imag._mpf_, prec)
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        _prec, mp.prec = mp.prec, prec

        try:
            func = lambdify(self.poly.gen, self.expr)

            interval = self._get_interval()
            if not self.is_real:
                # For complex intervals, we need to keep refining until the
                # imaginary interval is disjunct with other roots, that is,
                # until both ends get refined.
                ay = interval.ay
                by = interval.by
                while interval.ay == ay or interval.by == by:
                    interval = interval.refine()

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    x0 = mpc(*map(str, interval.center))

                try:
                    root = findroot(func, x0)
                    # If the (real or complex) root is not in the 'interval',
                    # then keep refining the interval. This happens if findroot
                    # accidentally finds a different root outside of this
                    # interval because our initial estimate 'x0' was not close
                    # enough.
                    if self.is_real:
                        a = mpf(str(interval.a))
                        b = mpf(str(interval.b))
                        # This is needed due to the bug #3364:
                        a, b = min(a, b), max(a, b)
                        if not (a < root < b):
                            raise ValueError("Root not in the interval.")
                    else:
                        ax = mpf(str(interval.ax))
                        bx = mpf(str(interval.bx))
                        ay = mpf(str(interval.ay))
                        by = mpf(str(interval.by))
                        # This is needed due to the bug #3364:
                        ax, bx = min(ax, bx), max(ax, bx)
                        ay, by = min(ay, by), max(ay, by)
                        if not (ax < root.real < bx and ay < root.imag < by):
                            raise ValueError("Root not in the interval.")
                except ValueError:
                    interval = interval.refine()
                    continue
                else:
                    break
        finally:
            mp.prec = _prec

        return Float._new(root.real._mpf_,
                          prec) + I * Float._new(root.imag._mpf_, prec)
Example #7
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        with workprec(prec):
            g = self.poly.gen
            if not g.is_Symbol:
                d = Dummy('x')
                func = lambdify(d, self.expr.subs(g, d))
            else:
                func = lambdify(g, self.expr)

            interval = self._get_interval()
            if not self.is_real:
                # For complex intervals, we need to keep refining until the
                # imaginary interval is disjunct with other roots, that is,
                # until both ends get refined.
                ay = interval.ay
                by = interval.by
                while interval.ay == ay or interval.by == by:
                    interval = interval.refine()

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    x0 = mpc(*map(str, interval.center))
                try:
                    root = findroot(func, x0, verify=False)
                    # If the (real or complex) root is not in the 'interval',
                    # then keep refining the interval. This happens if findroot
                    # accidentally finds a different root outside of this
                    # interval because our initial estimate 'x0' was not close
                    # enough.
                    if self.is_real:
                        a = mpf(str(interval.a))
                        b = mpf(str(interval.b))
                        if a == b:
                            root = a
                            break
                        if not (a < root < b):
                            raise ValueError("Root not in the interval.")
                    else:
                        ax = mpf(str(interval.ax))
                        bx = mpf(str(interval.bx))
                        ay = mpf(str(interval.ay))
                        by = mpf(str(interval.by))
                        if ax == bx and ay == by:
                            root = ax + S.ImaginaryUnit*by
                            break
                        if not (ax < root.real < bx and ay < root.imag < by):
                            raise ValueError("Root not in the interval.")
                except ValueError:
                    interval = interval.refine()
                    continue
                else:
                    break

        return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec)
Example #8
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        _prec, mp.prec = mp.prec, prec

        try:
            func = lambdify(self.poly.gen, self.expr)

            interval = self._get_interval()
            if not self.is_real:
                # For complex intervals, we need to keep refining until the
                # imaginary interval is disjunct with other roots, that is,
                # until both ends get refined.
                ay = interval.ay
                by = interval.by
                while interval.ay == ay or interval.by == by:
                    interval = interval.refine()

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    x0 = mpc(*map(str, interval.center))

                try:
                    root = findroot(func, x0)
                    # If the (real or complex) root is not in the 'interval',
                    # then keep refining the interval. This happens if findroot
                    # accidentally finds a different root outside of this
                    # interval because our initial estimate 'x0' was not close
                    # enough.
                    if self.is_real:
                        a = mpf(str(interval.a))
                        b = mpf(str(interval.b))
                        # This is needed due to the bug #3364:
                        a, b = min(a, b), max(a, b)
                        if not (a < root < b):
                            raise ValueError("Root not in the interval.")
                    else:
                        ax = mpf(str(interval.ax))
                        bx = mpf(str(interval.bx))
                        ay = mpf(str(interval.ay))
                        by = mpf(str(interval.by))
                        # This is needed due to the bug #3364:
                        ax, bx = min(ax, bx), max(ax, bx)
                        ay, by = min(ay, by), max(ay, by)
                        if not (ax < root.real < bx and ay < root.imag < by):
                            raise ValueError("Root not in the interval.")
                except ValueError:
                    interval = interval.refine()
                    continue
                else:
                    break
        finally:
            mp.prec = _prec

        return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec)
Example #9
0
def critical_par(par="L1", p=p, extra_par={}):
    old_p = p.copy()
    p.update(extra_par)
    if par != "r":
        g = lambda x: G(0, p=p, extra_par={par: x}) - p["r"]
    else:
        g = lambda x: G(0, p=p, extra_par={par: x}) - x
    result = mp.findroot(g, p[par], solver="secant")
    p.update(old_p)
    return result
def search_extreme_points(list_a):
    def f(x):
        return d_polynomial(x, list_a) - sm.cos(x);

    def df(x):
        return dd_polynomial(x, list_a) + sm.sin(x);

    check_points = numpy.linspace(0.0, sm.pi, 100)

    sign_reverse_section = \
        [p for p in zip(check_points, check_points[1:]) if f(p[0])*f(p[1]) <= 0.0]

    return [sm.findroot(f, x, df=df, tol=1.0e-20) for x,_ in sign_reverse_section]
def search_extreme_points(list_a):
    def f(x):
        return d_polynomial(x, list_a) - sm.cos(x)

    def df(x):
        return dd_polynomial(x, list_a) + sm.sin(x)

    check_points = numpy.linspace(0.0, sm.pi, 100)

    sign_reverse_section = \
        [p for p in zip(check_points, check_points[1:]) if f(p[0])*f(p[1]) <= 0.0]

    return [
        sm.findroot(f, x, df=df, tol=1.0e-20) for x, _ in sign_reverse_section
    ]
Example #12
0
 def solver(f, x):
     if method == "sympy":
         # findroot(solver="newton") or findroot(solver="secant") can't find
         # the root within the given tolerance. So we use solver="muller",
         # which converges towards complex roots (even for real starting
         # points), and so we need to chop all complex parts (that are small
         # anyway). Also we need to set the tolerance, as it sometimes fail
         # without it.
         def f_real(x):
             return f(complex(x).real)
         root = findroot(f_real, x, solver="muller", tol=1e-9)
         root = complex(root).real
     elif method == "scipy":
         root = newton(f, x)
     else:
         raise NotImplementedError("Unknown method.")
     return root
Example #13
0
    def improve_mu_m(self,
                     beam_type,
                     mode,
                     decimal_precision=DEFAULT_DECIMAL_PRECISION,
                     **kwargs):
        f = self._get_f(beam_type, decimal_precision)

        with mpmath.workdps(decimal_precision):
            # If not converted to `sympy.Float` precision will be lost after the
            # original `mpmath` context is restored
            return Float(
                mpmath.findroot(f=f,
                                x0=self.x0(beam_type, mode, decimal_precision),
                                solver=self.solver_name,
                                maxsteps=self.max_iterations,
                                verify=False,
                                **kwargs), decimal_precision)
Example #14
0
File: bessel.py Project: Aang/sympy
 def solver(f, x):
     if method == "sympy":
         # findroot(solver="newton") or findroot(solver="secant") can't find
         # the root within the given tolerance. So we use solver="muller",
         # which converges towards complex roots (even for real starting
         # points), and so we need to chop all complex parts (that are small
         # anyway). Also we need to set the tolerance, as it sometimes fail
         # without it.
         def f_real(x):
             return f(complex(x).real)
         root = findroot(f_real, x, solver="muller", tol=1e-9)
         root = complex(root).real
     elif method == "scipy":
         root = newton(f, x)
     else:
         raise NotImplementedError("Unknown method.")
     return root
Example #15
0
def find_roots(p):
    '''Return set of roots of polynomial *p*.

  :param p: sympy polynomial

  This uses the *nroots* method of the SymPy polynomial class to give
  rough roots, and subsequently refines these roots to arbitrary
  precision using mpmath.

  Returns a sorted *set* of roots.
  '''

    x = sympy.var('x')
    roots = set()

    for x0 in p.nroots():
        xi = mpmath.findroot(lambda z: p.eval(x, z), x0)
        roots.add(xi)

    return sorted(roots)
Example #16
0
def find_roots(p):
  '''Return set of roots of polynomial *p*.

  :param p: sympy polynomial

  This uses the *nroots* method of the SymPy polynomial class to give
  rough roots, and subsequently refines these roots to arbitrary
  precision using mpmath.

  Returns a sorted *set* of roots.
  '''

  x = sympy.var('x')
  roots = set()

  for x0 in p.nroots():
    xi = mpmath.findroot(lambda z: p.eval(x, z), x0)
    roots.add(xi)

  return sorted(roots)
Example #17
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        _prec, mp.prec = mp.prec, prec

        try:
            func = lambdify(self.poly.gen, self.expr)
            interval, refined = self._get_interval(), False

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    re, im = interval.center

                    re = mpf(str(re))
                    im = mpf(str(im))

                    x0 = mpc(re, im)

                try:
                    root = findroot(func, x0)
                except ValueError:
                    interval = interval.refine()
                    refined = True
                    continue
                else:
                    if refined:
                        self._set_interval(interval)

                    if self.is_conjugate:
                        root = root.conjugate()

                    break
        finally:
            mp.prec = _prec

        return Real._new(root.real._mpf_,
                         prec) + I * Real._new(root.imag._mpf_, prec)
Example #18
0
    def _eval_evalf(self, prec):
        """Evaluate this complex root to the given precision. """
        _prec, mp.prec = mp.prec, prec

        try:
            func = lambdify(self.poly.gen, self.expr)
            interval, refined = self._get_interval(), False

            while True:
                if self.is_real:
                    x0 = mpf(str(interval.center))
                else:
                    re, im = interval.center

                    re = mpf(str(re))
                    im = mpf(str(im))

                    x0 = mpc(re, im)

                try:
                    root = findroot(func, x0)
                except ValueError:
                    interval = interval.refine()
                    refined = True
                    continue
                else:
                    if refined:
                        self._set_interval(interval)

                    if self.is_conjugate:
                        root = root.conjugate()

                    break
        finally:
            mp.prec = _prec

        return Real._new(root.real._mpf_, prec) + I*Real._new(root.imag._mpf_, prec)
Example #19
0
def nsolve(*args, **kwargs):
    """
    Solve a nonlinear equation system numerically.

    nsolve(f, [args,] x0, modules=['mpmath'], **kwargs)

    f is a vector function of symbolic expressions representing the system.
    args are the variables. If there is only one variable, this argument can be
    omitted.
    x0 is a starting vector close to a solution.

    Use the modules keyword to specify which modules should be used to evaluate
    the function and the Jacobian matrix. Make sure to use a module that
    supports matrices. For more information on the syntax, please see the
    docstring of lambdify.

    Overdetermined systems are supported.

    >>> from sympy import Symbol, nsolve
    >>> import sympy
    >>> sympy.mpmath.mp.dps = 15
    >>> x1 = Symbol('x1')
    >>> x2 = Symbol('x2')
    >>> f1 = 3 * x1**2 - 2 * x2**2 - 1
    >>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8
    >>> print nsolve((f1, f2), (x1, x2), (-1, 1))
    [-1.19287309935246]
    [ 1.27844411169911]

    For one-dimensional functions the syntax is simplified:

    >>> from sympy import sin, nsolve
    >>> from sympy.abc import x
    >>> nsolve(sin(x), x, 2)
    3.14159265358979
    >>> nsolve(sin(x), 2)
    3.14159265358979

    mpmath.findroot is used, you can find there more extensive documentation,
    especially concerning keyword parameters and available solvers.
    """
    # interpret arguments
    if len(args) == 3:
        f = args[0]
        fargs = args[1]
        x0 = args[2]
    elif len(args) == 2:
        f = args[0]
        fargs = None
        x0 = args[1]
    elif len(args) < 2:
        raise TypeError("nsolve expected at least 2 arguments, got %i" % len(args))
    else:
        raise TypeError("nsolve expected at most 3 arguments, got %i" % len(args))
    modules = kwargs.get("modules", ["mpmath"])
    if isinstance(f, (list, tuple)):
        f = Matrix(f).T
    if not isinstance(f, Matrix):
        # assume it's a sympy expression
        if isinstance(f, Equality):
            f = f.lhs - f.rhs
        f = f.evalf()
        atoms = f.atoms(Symbol)
        if fargs is None:
            fargs = atoms.copy().pop()
        if not (len(atoms) == 1 and (fargs in atoms or fargs[0] in atoms)):
            raise ValueError("expected a one-dimensional and numerical function")

        # the function is much better behaved if there is no denominator
        f = f.as_numer_denom()[0]

        f = lambdify(fargs, f, modules)
        return findroot(f, x0, **kwargs)
    if len(fargs) > f.cols:
        raise NotImplementedError("need at least as many equations as variables")
    verbose = kwargs.get("verbose", False)
    if verbose:
        print "f(x):"
        print f
    # derive Jacobian
    J = f.jacobian(fargs)
    if verbose:
        print "J(x):"
        print J
    # create functions
    f = lambdify(fargs, f.T, modules)
    J = lambdify(fargs, J, modules)
    # solve the system numerically
    x = findroot(f, x0, J=J, **kwargs)
    return x
Example #20
0
def nsolve(*args, **kwargs):
    """
    Solve a nonlinear equation system numerically.

    nsolve(f, [args,] x0, modules=['mpmath'], **kwargs)

    f is a vector function of symbolic expressions representing the system.
    args are the variables. If there is only one variable, this argument can be
    omitted.
    x0 is a starting vector close to a solution.

    Use the modules keyword to specify which modules should be used to evaluate
    the function and the Jacobian matrix. Make sure to use a module that
    supports matrices. For more information on the syntax, please see the
    docstring of lambdify.

    Overdetermined systems are supported.

    >>> from sympy import Symbol, nsolve
    >>> import sympy
    >>> sympy.mpmath.mp.dps = 15
    >>> x1 = Symbol('x1')
    >>> x2 = Symbol('x2')
    >>> f1 = 3 * x1**2 - 2 * x2**2 - 1
    >>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8
    >>> print nsolve((f1, f2), (x1, x2), (-1, 1))
    [-1.19287309935246]
    [ 1.27844411169911]

    For one-dimensional functions the syntax is simplified:

    >>> from sympy import sin, nsolve
    >>> from sympy.abc import x
    >>> nsolve(sin(x), x, 2)
    3.14159265358979
    >>> nsolve(sin(x), 2)
    3.14159265358979

    mpmath.findroot is used, you can find there more extensive documentation,
    especially concerning keyword parameters and available solvers.
    """
    # interpret arguments
    if len(args) == 3:
        f = args[0]
        fargs = args[1]
        x0 = args[2]
    elif len(args) == 2:
        f = args[0]
        fargs = None
        x0 = args[1]
    elif len(args) < 2:
        raise TypeError('nsolve expected at least 2 arguments, got %i' %
                        len(args))
    else:
        raise TypeError('nsolve expected at most 3 arguments, got %i' %
                        len(args))
    modules = kwargs.get('modules', ['mpmath'])
    if isinstance(f, (list, tuple)):
        f = Matrix(f).T
    if not isinstance(f, Matrix):
        # assume it's a sympy expression
        if isinstance(f, Equality):
            f = f.lhs - f.rhs
        f = f.evalf()
        atoms = f.atoms(Symbol)
        if fargs is None:
            fargs = atoms.copy().pop()
        if not (len(atoms) == 1 and (fargs in atoms or fargs[0] in atoms)):
            raise ValueError(
                'expected a one-dimensional and numerical function')

        # the function is much better behaved if there is no denominator
        f = f.as_numer_denom()[0]

        f = lambdify(fargs, f, modules)
        return findroot(f, x0, **kwargs)
    if len(fargs) > f.cols:
        raise NotImplementedError(
            'need at least as many equations as variables')
    verbose = kwargs.get('verbose', False)
    if verbose:
        print 'f(x):'
        print f
    # derive Jacobian
    J = f.jacobian(fargs)
    if verbose:
        print 'J(x):'
        print J
    # create functions
    f = lambdify(fargs, f.T, modules)
    J = lambdify(fargs, J, modules)
    # solve the system numerically
    x = findroot(f, x0, J=J, **kwargs)
    return x
Example #21
0
from sympy import Symbol, ln, latex, diff, pretty_print
from sympy.mpmath import findroot

# p4
x = Symbol('x')
g = (1 + x) * ln(1 + x)
R = (x ** 2) / (2 * (1 + x / 3))

form = g - x - R
diff_1 = diff(form, x, 1)
diff_2 = diff(form, x, 2)


def diff_func(x):
    return 2*x**2/(3*(2*x/3 + 2)**2) - 2*x/(2*x/3 + 2) + ln(x + 1)

findroot(diff_func, 0)
    print('d=', sm.nstr(d, 17))

    # -2*s0*s1*s3*x**3 + s0*s2*x + s1**2*s3*x**4 + x**2*(s0**2*s3 - s1*s2)
    def f(s0,s1,s2,s3):
        return s0*s2            - list_a[1], \
               s0**2*s3 - s1*s2 - list_a[2], \
               -2*s0*s1*s3      - list_a[3], \
               s1**2*s3         - list_a[4]

    print()
    print('Newton method calculating...', end='')
    initilal = (1.2732395447351627, 0.40528473456935109, 0.77633023248007499, 0.22308510060189463);
    list_s = sm.findroot(
            f,
            initilal,
            method='newton',
            maxsteps=10000,
            tol=1.0e-25)
    print(' OK')

    for k,s in enumerate(list_s):
        print('s[' + str(k) + ']=', sm.nstr(s, 17))

#
#Remez algorithm calculating... OK
#a[0]= 0.0
#a[1]= 0.9897151132173856
#a[2]= 0.044771099390202579
#a[3]= -0.22906038058222875
#a[4]= 0.036456091836172492
#d= -0.00073239476651250248
    print('d=', sm.nstr(d, 17))

    # -2*s0*s1*s3*x**3 + s0*s2*x + s1**2*s3*x**4 + x**2*(s0**2*s3 - s1*s2)
    def f(s0, s1, s2, s3):
        return s0*s2            - list_a[1], \
               s0**2*s3 - s1*s2 - list_a[2], \
               -2*s0*s1*s3      - list_a[3], \
               s1**2*s3         - list_a[4]

    print()
    print('Newton method calculating...', end='')
    initilal = (1.2732395447351627, 0.40528473456935109, 0.77633023248007499,
                0.22308510060189463)
    list_s = sm.findroot(f,
                         initilal,
                         method='newton',
                         maxsteps=10000,
                         tol=1.0e-25)
    print(' OK')

    for k, s in enumerate(list_s):
        print('s[' + str(k) + ']=', sm.nstr(s, 17))

#
#Remez algorithm calculating... OK
#a[0]= 0.0
#a[1]= 0.9897151132173856
#a[2]= 0.044771099390202579
#a[3]= -0.22906038058222875
#a[4]= 0.036456091836172492
#d= -0.00073239476651250248
Example #24
0
f = simplify(pQ.pos_from(pP) & N.z)

print("f = {}\n".format(msprint(f)))

# calculate the derivative of f for use with newton-raphson
df = simplify(f.diff(theta))
print("df/dθ = {}\n".format(msprint(df)))

# constraint function for zero steer/lean configuration and
# using the benchmark parameters
f0 = lambdify(theta, f.subs({phi: 0, delta: 0}).subs(benchmark_parameters))
df0 = lambdify(theta, df.subs({phi: 0, delta: 0}).subs(benchmark_parameters))

print("verifying constraint equations are correct")
print("for zero steer/lean, pitch should be pi/10")
findroot(f0, 0.3, solver="newton", tol=1e-8, verbose=True, df=df0)

c_sym = [Symbol('lean'), Symbol('pitch'), Symbol('steer')]
c_sym_dict = dict(zip([phi, theta, delta], c_sym))

fc = ccode(f.subs(c_sym_dict))
dfc = ccode(df.subs(c_sym_dict))

cs_math = {
    'cos': 'Math.Cos',
    'sin': 'Math.Sin',
    'pow': 'Math.Pow',
    'sqrt': 'Math.Sqrt'
}

fcs = fc