123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- from sympy.core.containers import Tuple
- from sympy.core.numbers import oo
- from sympy.core.relational import (Gt, Lt)
- from sympy.core.symbol import (Dummy, Symbol)
- from sympy.functions.elementary.complexes import Abs
- from sympy.logic.boolalg import And
- from sympy.codegen.ast import (
- Assignment, AddAugmentedAssignment, CodeBlock, Declaration, FunctionDefinition,
- Print, Return, Scope, While, Variable, Pointer, real
- )
- """ This module collects functions for constructing ASTs representing algorithms. """
- def newtons_method(expr, wrt, atol=1e-12, delta=None, debug=False,
- itermax=None, counter=None):
- """ Generates an AST for Newton-Raphson method (a root-finding algorithm).
- Explanation
- ===========
- Returns an abstract syntax tree (AST) based on ``sympy.codegen.ast`` for Netwon's
- method of root-finding.
- Parameters
- ==========
- expr : expression
- wrt : Symbol
- With respect to, i.e. what is the variable.
- atol : number or expr
- Absolute tolerance (stopping criterion)
- delta : Symbol
- Will be a ``Dummy`` if ``None``.
- debug : bool
- Whether to print convergence information during iterations
- itermax : number or expr
- Maximum number of iterations.
- counter : Symbol
- Will be a ``Dummy`` if ``None``.
- Examples
- ========
- >>> from sympy import symbols, cos
- >>> from sympy.codegen.ast import Assignment
- >>> from sympy.codegen.algorithms import newtons_method
- >>> x, dx, atol = symbols('x dx atol')
- >>> expr = cos(x) - x**3
- >>> algo = newtons_method(expr, x, atol, dx)
- >>> algo.has(Assignment(dx, -expr/expr.diff(x)))
- True
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Newton%27s_method
- """
- if delta is None:
- delta = Dummy()
- Wrapper = Scope
- name_d = 'delta'
- else:
- Wrapper = lambda x: x
- name_d = delta.name
- delta_expr = -expr/expr.diff(wrt)
- whl_bdy = [Assignment(delta, delta_expr), AddAugmentedAssignment(wrt, delta)]
- if debug:
- prnt = Print([wrt, delta], r"{}=%12.5g {}=%12.5g\n".format(wrt.name, name_d))
- whl_bdy = [whl_bdy[0], prnt] + whl_bdy[1:]
- req = Gt(Abs(delta), atol)
- declars = [Declaration(Variable(delta, type=real, value=oo))]
- if itermax is not None:
- counter = counter or Dummy(integer=True)
- v_counter = Variable.deduced(counter, 0)
- declars.append(Declaration(v_counter))
- whl_bdy.append(AddAugmentedAssignment(counter, 1))
- req = And(req, Lt(counter, itermax))
- whl = While(req, CodeBlock(*whl_bdy))
- blck = declars + [whl]
- return Wrapper(CodeBlock(*blck))
- def _symbol_of(arg):
- if isinstance(arg, Declaration):
- arg = arg.variable.symbol
- elif isinstance(arg, Variable):
- arg = arg.symbol
- return arg
- def newtons_method_function(expr, wrt, params=None, func_name="newton", attrs=Tuple(), *, delta=None, **kwargs):
- """ Generates an AST for a function implementing the Newton-Raphson method.
- Parameters
- ==========
- expr : expression
- wrt : Symbol
- With respect to, i.e. what is the variable
- params : iterable of symbols
- Symbols appearing in expr that are taken as constants during the iterations
- (these will be accepted as parameters to the generated function).
- func_name : str
- Name of the generated function.
- attrs : Tuple
- Attribute instances passed as ``attrs`` to ``FunctionDefinition``.
- \\*\\*kwargs :
- Keyword arguments passed to :func:`sympy.codegen.algorithms.newtons_method`.
- Examples
- ========
- >>> from sympy import symbols, cos
- >>> from sympy.codegen.algorithms import newtons_method_function
- >>> from sympy.codegen.pyutils import render_as_module
- >>> x = symbols('x')
- >>> expr = cos(x) - x**3
- >>> func = newtons_method_function(expr, x)
- >>> py_mod = render_as_module(func) # source code as string
- >>> namespace = {}
- >>> exec(py_mod, namespace, namespace)
- >>> res = eval('newton(0.5)', namespace)
- >>> abs(res - 0.865474033102) < 1e-12
- True
- See Also
- ========
- sympy.codegen.algorithms.newtons_method
- """
- if params is None:
- params = (wrt,)
- pointer_subs = {p.symbol: Symbol('(*%s)' % p.symbol.name)
- for p in params if isinstance(p, Pointer)}
- if delta is None:
- delta = Symbol('d_' + wrt.name)
- if expr.has(delta):
- delta = None # will use Dummy
- algo = newtons_method(expr, wrt, delta=delta, **kwargs).xreplace(pointer_subs)
- if isinstance(algo, Scope):
- algo = algo.body
- not_in_params = expr.free_symbols.difference({_symbol_of(p) for p in params})
- if not_in_params:
- raise ValueError("Missing symbols in params: %s" % ', '.join(map(str, not_in_params)))
- declars = tuple(Variable(p, real) for p in params)
- body = CodeBlock(algo, Return(wrt))
- return FunctionDefinition(real, func_name, declars, body, attrs=attrs)
|