Coverage for /usr/lib/python3/dist-packages/sympy/codegen/ast.py: 54%
548 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2Types used to represent a full function/module as an Abstract Syntax Tree.
4Most types are small, and are merely used as tokens in the AST. A tree diagram
5has been included below to illustrate the relationships between the AST types.
8AST Type Tree
9-------------
10::
12 *Basic*
13 |
14 |
15 CodegenAST
16 |
17 |--->AssignmentBase
18 | |--->Assignment
19 | |--->AugmentedAssignment
20 | |--->AddAugmentedAssignment
21 | |--->SubAugmentedAssignment
22 | |--->MulAugmentedAssignment
23 | |--->DivAugmentedAssignment
24 | |--->ModAugmentedAssignment
25 |
26 |--->CodeBlock
27 |
28 |
29 |--->Token
30 |--->Attribute
31 |--->For
32 |--->String
33 | |--->QuotedString
34 | |--->Comment
35 |--->Type
36 | |--->IntBaseType
37 | | |--->_SizedIntType
38 | | |--->SignedIntType
39 | | |--->UnsignedIntType
40 | |--->FloatBaseType
41 | |--->FloatType
42 | |--->ComplexBaseType
43 | |--->ComplexType
44 |--->Node
45 | |--->Variable
46 | | |---> Pointer
47 | |--->FunctionPrototype
48 | |--->FunctionDefinition
49 |--->Element
50 |--->Declaration
51 |--->While
52 |--->Scope
53 |--->Stream
54 |--->Print
55 |--->FunctionCall
56 |--->BreakToken
57 |--->ContinueToken
58 |--->NoneToken
59 |--->Return
62Predefined types
63----------------
65A number of ``Type`` instances are provided in the ``sympy.codegen.ast`` module
66for convenience. Perhaps the two most common ones for code-generation (of numeric
67codes) are ``float32`` and ``float64`` (known as single and double precision respectively).
68There are also precision generic versions of Types (for which the codeprinters selects the
69underlying data type at time of printing): ``real``, ``integer``, ``complex_``, ``bool_``.
71The other ``Type`` instances defined are:
73- ``intc``: Integer type used by C's "int".
74- ``intp``: Integer type used by C's "unsigned".
75- ``int8``, ``int16``, ``int32``, ``int64``: n-bit integers.
76- ``uint8``, ``uint16``, ``uint32``, ``uint64``: n-bit unsigned integers.
77- ``float80``: known as "extended precision" on modern x86/amd64 hardware.
78- ``complex64``: Complex number represented by two ``float32`` numbers
79- ``complex128``: Complex number represented by two ``float64`` numbers
81Using the nodes
82---------------
84It is possible to construct simple algorithms using the AST nodes. Let's construct a loop applying
85Newton's method::
87 >>> from sympy import symbols, cos
88 >>> from sympy.codegen.ast import While, Assignment, aug_assign, Print
89 >>> t, dx, x = symbols('tol delta val')
90 >>> expr = cos(x) - x**3
91 >>> whl = While(abs(dx) > t, [
92 ... Assignment(dx, -expr/expr.diff(x)),
93 ... aug_assign(x, '+', dx),
94 ... Print([x])
95 ... ])
96 >>> from sympy import pycode
97 >>> py_str = pycode(whl)
98 >>> print(py_str)
99 while (abs(delta) > tol):
100 delta = (val**3 - math.cos(val))/(-3*val**2 - math.sin(val))
101 val += delta
102 print(val)
103 >>> import math
104 >>> tol, val, delta = 1e-5, 0.5, float('inf')
105 >>> exec(py_str)
106 1.1121416371
107 0.909672693737
108 0.867263818209
109 0.865477135298
110 0.865474033111
111 >>> print('%3.1g' % (math.cos(val) - val**3))
112 -3e-11
114If we want to generate Fortran code for the same while loop we simple call ``fcode``::
116 >>> from sympy import fcode
117 >>> print(fcode(whl, standard=2003, source_format='free'))
118 do while (abs(delta) > tol)
119 delta = (val**3 - cos(val))/(-3*val**2 - sin(val))
120 val = val + delta
121 print *, val
122 end do
124There is a function constructing a loop (or a complete function) like this in
125:mod:`sympy.codegen.algorithms`.
127"""
129from __future__ import annotations
130from typing import Any
132from collections import defaultdict
134from sympy.core.relational import (Ge, Gt, Le, Lt)
135from sympy.core import Symbol, Tuple, Dummy
136from sympy.core.basic import Basic
137from sympy.core.expr import Expr, Atom
138from sympy.core.numbers import Float, Integer, oo
139from sympy.core.sympify import _sympify, sympify, SympifyError
140from sympy.utilities.iterables import (iterable, topological_sort,
141 numbered_symbols, filter_symbols)
144def _mk_Tuple(args):
145 """
146 Create a SymPy Tuple object from an iterable, converting Python strings to
147 AST strings.
149 Parameters
150 ==========
152 args: iterable
153 Arguments to :class:`sympy.Tuple`.
155 Returns
156 =======
158 sympy.Tuple
159 """
160 args = [String(arg) if isinstance(arg, str) else arg for arg in args]
161 return Tuple(*args)
164class CodegenAST(Basic):
165 __slots__ = ()
168class Token(CodegenAST):
169 """ Base class for the AST types.
171 Explanation
172 ===========
174 Defining fields are set in ``_fields``. Attributes (defined in _fields)
175 are only allowed to contain instances of Basic (unless atomic, see
176 ``String``). The arguments to ``__new__()`` correspond to the attributes in
177 the order defined in ``_fields`. The ``defaults`` class attribute is a
178 dictionary mapping attribute names to their default values.
180 Subclasses should not need to override the ``__new__()`` method. They may
181 define a class or static method named ``_construct_<attr>`` for each
182 attribute to process the value passed to ``__new__()``. Attributes listed
183 in the class attribute ``not_in_args`` are not passed to :class:`~.Basic`.
184 """
186 __slots__: tuple[str, ...] = ()
187 _fields = __slots__
188 defaults: dict[str, Any] = {}
189 not_in_args: list[str] = []
190 indented_args = ['body']
192 @property
193 def is_Atom(self):
194 return len(self._fields) == 0
196 @classmethod
197 def _get_constructor(cls, attr):
198 """ Get the constructor function for an attribute by name. """
199 return getattr(cls, '_construct_%s' % attr, lambda x: x)
201 @classmethod
202 def _construct(cls, attr, arg):
203 """ Construct an attribute value from argument passed to ``__new__()``. """
204 # arg may be ``NoneToken()``, so comparison is done using == instead of ``is`` operator
205 if arg == None:
206 return cls.defaults.get(attr, none)
207 else:
208 if isinstance(arg, Dummy): # SymPy's replace uses Dummy instances
209 return arg
210 else:
211 return cls._get_constructor(attr)(arg)
213 def __new__(cls, *args, **kwargs):
214 # Pass through existing instances when given as sole argument
215 if len(args) == 1 and not kwargs and isinstance(args[0], cls):
216 return args[0]
218 if len(args) > len(cls._fields):
219 raise ValueError("Too many arguments (%d), expected at most %d" % (len(args), len(cls._fields)))
221 attrvals = []
223 # Process positional arguments
224 for attrname, argval in zip(cls._fields, args):
225 if attrname in kwargs:
226 raise TypeError('Got multiple values for attribute %r' % attrname)
228 attrvals.append(cls._construct(attrname, argval))
230 # Process keyword arguments
231 for attrname in cls._fields[len(args):]:
232 if attrname in kwargs:
233 argval = kwargs.pop(attrname)
235 elif attrname in cls.defaults:
236 argval = cls.defaults[attrname]
238 else:
239 raise TypeError('No value for %r given and attribute has no default' % attrname)
241 attrvals.append(cls._construct(attrname, argval))
243 if kwargs:
244 raise ValueError("Unknown keyword arguments: %s" % ' '.join(kwargs))
246 # Parent constructor
247 basic_args = [
248 val for attr, val in zip(cls._fields, attrvals)
249 if attr not in cls.not_in_args
250 ]
251 obj = CodegenAST.__new__(cls, *basic_args)
253 # Set attributes
254 for attr, arg in zip(cls._fields, attrvals):
255 setattr(obj, attr, arg)
257 return obj
259 def __eq__(self, other):
260 if not isinstance(other, self.__class__):
261 return False
262 for attr in self._fields:
263 if getattr(self, attr) != getattr(other, attr):
264 return False
265 return True
267 def _hashable_content(self):
268 return tuple([getattr(self, attr) for attr in self._fields])
270 def __hash__(self):
271 return super().__hash__()
273 def _joiner(self, k, indent_level):
274 return (',\n' + ' '*indent_level) if k in self.indented_args else ', '
276 def _indented(self, printer, k, v, *args, **kwargs):
277 il = printer._context['indent_level']
278 def _print(arg):
279 if isinstance(arg, Token):
280 return printer._print(arg, *args, joiner=self._joiner(k, il), **kwargs)
281 else:
282 return printer._print(arg, *args, **kwargs)
284 if isinstance(v, Tuple):
285 joined = self._joiner(k, il).join([_print(arg) for arg in v.args])
286 if k in self.indented_args:
287 return '(\n' + ' '*il + joined + ',\n' + ' '*(il - 4) + ')'
288 else:
289 return ('({0},)' if len(v.args) == 1 else '({0})').format(joined)
290 else:
291 return _print(v)
293 def _sympyrepr(self, printer, *args, joiner=', ', **kwargs):
294 from sympy.printing.printer import printer_context
295 exclude = kwargs.get('exclude', ())
296 values = [getattr(self, k) for k in self._fields]
297 indent_level = printer._context.get('indent_level', 0)
299 arg_reprs = []
301 for i, (attr, value) in enumerate(zip(self._fields, values)):
302 if attr in exclude:
303 continue
305 # Skip attributes which have the default value
306 if attr in self.defaults and value == self.defaults[attr]:
307 continue
309 ilvl = indent_level + 4 if attr in self.indented_args else 0
310 with printer_context(printer, indent_level=ilvl):
311 indented = self._indented(printer, attr, value, *args, **kwargs)
312 arg_reprs.append(('{1}' if i == 0 else '{0}={1}').format(attr, indented.lstrip()))
314 return "{}({})".format(self.__class__.__name__, joiner.join(arg_reprs))
316 _sympystr = _sympyrepr
318 def __repr__(self): # sympy.core.Basic.__repr__ uses sstr
319 from sympy.printing import srepr
320 return srepr(self)
322 def kwargs(self, exclude=(), apply=None):
323 """ Get instance's attributes as dict of keyword arguments.
325 Parameters
326 ==========
328 exclude : collection of str
329 Collection of keywords to exclude.
331 apply : callable, optional
332 Function to apply to all values.
333 """
334 kwargs = {k: getattr(self, k) for k in self._fields if k not in exclude}
335 if apply is not None:
336 return {k: apply(v) for k, v in kwargs.items()}
337 else:
338 return kwargs
340class BreakToken(Token):
341 """ Represents 'break' in C/Python ('exit' in Fortran).
343 Use the premade instance ``break_`` or instantiate manually.
345 Examples
346 ========
348 >>> from sympy import ccode, fcode
349 >>> from sympy.codegen.ast import break_
350 >>> ccode(break_)
351 'break'
352 >>> fcode(break_, source_format='free')
353 'exit'
354 """
356break_ = BreakToken()
359class ContinueToken(Token):
360 """ Represents 'continue' in C/Python ('cycle' in Fortran)
362 Use the premade instance ``continue_`` or instantiate manually.
364 Examples
365 ========
367 >>> from sympy import ccode, fcode
368 >>> from sympy.codegen.ast import continue_
369 >>> ccode(continue_)
370 'continue'
371 >>> fcode(continue_, source_format='free')
372 'cycle'
373 """
375continue_ = ContinueToken()
377class NoneToken(Token):
378 """ The AST equivalence of Python's NoneType
380 The corresponding instance of Python's ``None`` is ``none``.
382 Examples
383 ========
385 >>> from sympy.codegen.ast import none, Variable
386 >>> from sympy import pycode
387 >>> print(pycode(Variable('x').as_Declaration(value=none)))
388 x = None
390 """
391 def __eq__(self, other):
392 return other is None or isinstance(other, NoneToken)
394 def _hashable_content(self):
395 return ()
397 def __hash__(self):
398 return super().__hash__()
401none = NoneToken()
404class AssignmentBase(CodegenAST):
405 """ Abstract base class for Assignment and AugmentedAssignment.
407 Attributes:
408 ===========
410 op : str
411 Symbol for assignment operator, e.g. "=", "+=", etc.
412 """
414 def __new__(cls, lhs, rhs):
415 lhs = _sympify(lhs)
416 rhs = _sympify(rhs)
418 cls._check_args(lhs, rhs)
420 return super().__new__(cls, lhs, rhs)
422 @property
423 def lhs(self):
424 return self.args[0]
426 @property
427 def rhs(self):
428 return self.args[1]
430 @classmethod
431 def _check_args(cls, lhs, rhs):
432 """ Check arguments to __new__ and raise exception if any problems found.
434 Derived classes may wish to override this.
435 """
436 from sympy.matrices.expressions.matexpr import (
437 MatrixElement, MatrixSymbol)
438 from sympy.tensor.indexed import Indexed
439 from sympy.tensor.array.expressions import ArrayElement
441 # Tuple of things that can be on the lhs of an assignment
442 assignable = (Symbol, MatrixSymbol, MatrixElement, Indexed, Element, Variable,
443 ArrayElement)
444 if not isinstance(lhs, assignable):
445 raise TypeError("Cannot assign to lhs of type %s." % type(lhs))
447 # Indexed types implement shape, but don't define it until later. This
448 # causes issues in assignment validation. For now, matrices are defined
449 # as anything with a shape that is not an Indexed
450 lhs_is_mat = hasattr(lhs, 'shape') and not isinstance(lhs, Indexed)
451 rhs_is_mat = hasattr(rhs, 'shape') and not isinstance(rhs, Indexed)
453 # If lhs and rhs have same structure, then this assignment is ok
454 if lhs_is_mat:
455 if not rhs_is_mat:
456 raise ValueError("Cannot assign a scalar to a matrix.")
457 elif lhs.shape != rhs.shape:
458 raise ValueError("Dimensions of lhs and rhs do not align.")
459 elif rhs_is_mat and not lhs_is_mat:
460 raise ValueError("Cannot assign a matrix to a scalar.")
463class Assignment(AssignmentBase):
464 """
465 Represents variable assignment for code generation.
467 Parameters
468 ==========
470 lhs : Expr
471 SymPy object representing the lhs of the expression. These should be
472 singular objects, such as one would use in writing code. Notable types
473 include Symbol, MatrixSymbol, MatrixElement, and Indexed. Types that
474 subclass these types are also supported.
476 rhs : Expr
477 SymPy object representing the rhs of the expression. This can be any
478 type, provided its shape corresponds to that of the lhs. For example,
479 a Matrix type can be assigned to MatrixSymbol, but not to Symbol, as
480 the dimensions will not align.
482 Examples
483 ========
485 >>> from sympy import symbols, MatrixSymbol, Matrix
486 >>> from sympy.codegen.ast import Assignment
487 >>> x, y, z = symbols('x, y, z')
488 >>> Assignment(x, y)
489 Assignment(x, y)
490 >>> Assignment(x, 0)
491 Assignment(x, 0)
492 >>> A = MatrixSymbol('A', 1, 3)
493 >>> mat = Matrix([x, y, z]).T
494 >>> Assignment(A, mat)
495 Assignment(A, Matrix([[x, y, z]]))
496 >>> Assignment(A[0, 1], x)
497 Assignment(A[0, 1], x)
498 """
500 op = ':='
503class AugmentedAssignment(AssignmentBase):
504 """
505 Base class for augmented assignments.
507 Attributes:
508 ===========
510 binop : str
511 Symbol for binary operation being applied in the assignment, such as "+",
512 "*", etc.
513 """
514 binop = None # type: str
516 @property
517 def op(self):
518 return self.binop + '='
521class AddAugmentedAssignment(AugmentedAssignment):
522 binop = '+'
525class SubAugmentedAssignment(AugmentedAssignment):
526 binop = '-'
529class MulAugmentedAssignment(AugmentedAssignment):
530 binop = '*'
533class DivAugmentedAssignment(AugmentedAssignment):
534 binop = '/'
537class ModAugmentedAssignment(AugmentedAssignment):
538 binop = '%'
541# Mapping from binary op strings to AugmentedAssignment subclasses
542augassign_classes = {
543 cls.binop: cls for cls in [
544 AddAugmentedAssignment, SubAugmentedAssignment, MulAugmentedAssignment,
545 DivAugmentedAssignment, ModAugmentedAssignment
546 ]
547}
550def aug_assign(lhs, op, rhs):
551 """
552 Create 'lhs op= rhs'.
554 Explanation
555 ===========
557 Represents augmented variable assignment for code generation. This is a
558 convenience function. You can also use the AugmentedAssignment classes
559 directly, like AddAugmentedAssignment(x, y).
561 Parameters
562 ==========
564 lhs : Expr
565 SymPy object representing the lhs of the expression. These should be
566 singular objects, such as one would use in writing code. Notable types
567 include Symbol, MatrixSymbol, MatrixElement, and Indexed. Types that
568 subclass these types are also supported.
570 op : str
571 Operator (+, -, /, \\*, %).
573 rhs : Expr
574 SymPy object representing the rhs of the expression. This can be any
575 type, provided its shape corresponds to that of the lhs. For example,
576 a Matrix type can be assigned to MatrixSymbol, but not to Symbol, as
577 the dimensions will not align.
579 Examples
580 ========
582 >>> from sympy import symbols
583 >>> from sympy.codegen.ast import aug_assign
584 >>> x, y = symbols('x, y')
585 >>> aug_assign(x, '+', y)
586 AddAugmentedAssignment(x, y)
587 """
588 if op not in augassign_classes:
589 raise ValueError("Unrecognized operator %s" % op)
590 return augassign_classes[op](lhs, rhs)
593class CodeBlock(CodegenAST):
594 """
595 Represents a block of code.
597 Explanation
598 ===========
600 For now only assignments are supported. This restriction will be lifted in
601 the future.
603 Useful attributes on this object are:
605 ``left_hand_sides``:
606 Tuple of left-hand sides of assignments, in order.
607 ``left_hand_sides``:
608 Tuple of right-hand sides of assignments, in order.
609 ``free_symbols``: Free symbols of the expressions in the right-hand sides
610 which do not appear in the left-hand side of an assignment.
612 Useful methods on this object are:
614 ``topological_sort``:
615 Class method. Return a CodeBlock with assignments
616 sorted so that variables are assigned before they
617 are used.
618 ``cse``:
619 Return a new CodeBlock with common subexpressions eliminated and
620 pulled out as assignments.
622 Examples
623 ========
625 >>> from sympy import symbols, ccode
626 >>> from sympy.codegen.ast import CodeBlock, Assignment
627 >>> x, y = symbols('x y')
628 >>> c = CodeBlock(Assignment(x, 1), Assignment(y, x + 1))
629 >>> print(ccode(c))
630 x = 1;
631 y = x + 1;
633 """
634 def __new__(cls, *args):
635 left_hand_sides = []
636 right_hand_sides = []
637 for i in args:
638 if isinstance(i, Assignment):
639 lhs, rhs = i.args
640 left_hand_sides.append(lhs)
641 right_hand_sides.append(rhs)
643 obj = CodegenAST.__new__(cls, *args)
645 obj.left_hand_sides = Tuple(*left_hand_sides)
646 obj.right_hand_sides = Tuple(*right_hand_sides)
647 return obj
649 def __iter__(self):
650 return iter(self.args)
652 def _sympyrepr(self, printer, *args, **kwargs):
653 il = printer._context.get('indent_level', 0)
654 joiner = ',\n' + ' '*il
655 joined = joiner.join(map(printer._print, self.args))
656 return ('{}(\n'.format(' '*(il-4) + self.__class__.__name__,) +
657 ' '*il + joined + '\n' + ' '*(il - 4) + ')')
659 _sympystr = _sympyrepr
661 @property
662 def free_symbols(self):
663 return super().free_symbols - set(self.left_hand_sides)
665 @classmethod
666 def topological_sort(cls, assignments):
667 """
668 Return a CodeBlock with topologically sorted assignments so that
669 variables are assigned before they are used.
671 Examples
672 ========
674 The existing order of assignments is preserved as much as possible.
676 This function assumes that variables are assigned to only once.
678 This is a class constructor so that the default constructor for
679 CodeBlock can error when variables are used before they are assigned.
681 >>> from sympy import symbols
682 >>> from sympy.codegen.ast import CodeBlock, Assignment
683 >>> x, y, z = symbols('x y z')
685 >>> assignments = [
686 ... Assignment(x, y + z),
687 ... Assignment(y, z + 1),
688 ... Assignment(z, 2),
689 ... ]
690 >>> CodeBlock.topological_sort(assignments)
691 CodeBlock(
692 Assignment(z, 2),
693 Assignment(y, z + 1),
694 Assignment(x, y + z)
695 )
697 """
699 if not all(isinstance(i, Assignment) for i in assignments):
700 # Will support more things later
701 raise NotImplementedError("CodeBlock.topological_sort only supports Assignments")
703 if any(isinstance(i, AugmentedAssignment) for i in assignments):
704 raise NotImplementedError("CodeBlock.topological_sort does not yet work with AugmentedAssignments")
706 # Create a graph where the nodes are assignments and there is a directed edge
707 # between nodes that use a variable and nodes that assign that
708 # variable, like
710 # [(x := 1, y := x + 1), (x := 1, z := y + z), (y := x + 1, z := y + z)]
712 # If we then topologically sort these nodes, they will be in
713 # assignment order, like
715 # x := 1
716 # y := x + 1
717 # z := y + z
719 # A = The nodes
720 #
721 # enumerate keeps nodes in the same order they are already in if
722 # possible. It will also allow us to handle duplicate assignments to
723 # the same variable when those are implemented.
724 A = list(enumerate(assignments))
726 # var_map = {variable: [nodes for which this variable is assigned to]}
727 # like {x: [(1, x := y + z), (4, x := 2 * w)], ...}
728 var_map = defaultdict(list)
729 for node in A:
730 i, a = node
731 var_map[a.lhs].append(node)
733 # E = Edges in the graph
734 E = []
735 for dst_node in A:
736 i, a = dst_node
737 for s in a.rhs.free_symbols:
738 for src_node in var_map[s]:
739 E.append((src_node, dst_node))
741 ordered_assignments = topological_sort([A, E])
743 # De-enumerate the result
744 return cls(*[a for i, a in ordered_assignments])
746 def cse(self, symbols=None, optimizations=None, postprocess=None,
747 order='canonical'):
748 """
749 Return a new code block with common subexpressions eliminated.
751 Explanation
752 ===========
754 See the docstring of :func:`sympy.simplify.cse_main.cse` for more
755 information.
757 Examples
758 ========
760 >>> from sympy import symbols, sin
761 >>> from sympy.codegen.ast import CodeBlock, Assignment
762 >>> x, y, z = symbols('x y z')
764 >>> c = CodeBlock(
765 ... Assignment(x, 1),
766 ... Assignment(y, sin(x) + 1),
767 ... Assignment(z, sin(x) - 1),
768 ... )
769 ...
770 >>> c.cse()
771 CodeBlock(
772 Assignment(x, 1),
773 Assignment(x0, sin(x)),
774 Assignment(y, x0 + 1),
775 Assignment(z, x0 - 1)
776 )
778 """
779 from sympy.simplify.cse_main import cse
781 # Check that the CodeBlock only contains assignments to unique variables
782 if not all(isinstance(i, Assignment) for i in self.args):
783 # Will support more things later
784 raise NotImplementedError("CodeBlock.cse only supports Assignments")
786 if any(isinstance(i, AugmentedAssignment) for i in self.args):
787 raise NotImplementedError("CodeBlock.cse does not yet work with AugmentedAssignments")
789 for i, lhs in enumerate(self.left_hand_sides):
790 if lhs in self.left_hand_sides[:i]:
791 raise NotImplementedError("Duplicate assignments to the same "
792 "variable are not yet supported (%s)" % lhs)
794 # Ensure new symbols for subexpressions do not conflict with existing
795 existing_symbols = self.atoms(Symbol)
796 if symbols is None:
797 symbols = numbered_symbols()
798 symbols = filter_symbols(symbols, existing_symbols)
800 replacements, reduced_exprs = cse(list(self.right_hand_sides),
801 symbols=symbols, optimizations=optimizations, postprocess=postprocess,
802 order=order)
804 new_block = [Assignment(var, expr) for var, expr in
805 zip(self.left_hand_sides, reduced_exprs)]
806 new_assignments = [Assignment(var, expr) for var, expr in replacements]
807 return self.topological_sort(new_assignments + new_block)
810class For(Token):
811 """Represents a 'for-loop' in the code.
813 Expressions are of the form:
814 "for target in iter:
815 body..."
817 Parameters
818 ==========
820 target : symbol
821 iter : iterable
822 body : CodeBlock or iterable
823! When passed an iterable it is used to instantiate a CodeBlock.
825 Examples
826 ========
828 >>> from sympy import symbols, Range
829 >>> from sympy.codegen.ast import aug_assign, For
830 >>> x, i, j, k = symbols('x i j k')
831 >>> for_i = For(i, Range(10), [aug_assign(x, '+', i*j*k)])
832 >>> for_i # doctest: -NORMALIZE_WHITESPACE
833 For(i, iterable=Range(0, 10, 1), body=CodeBlock(
834 AddAugmentedAssignment(x, i*j*k)
835 ))
836 >>> for_ji = For(j, Range(7), [for_i])
837 >>> for_ji # doctest: -NORMALIZE_WHITESPACE
838 For(j, iterable=Range(0, 7, 1), body=CodeBlock(
839 For(i, iterable=Range(0, 10, 1), body=CodeBlock(
840 AddAugmentedAssignment(x, i*j*k)
841 ))
842 ))
843 >>> for_kji =For(k, Range(5), [for_ji])
844 >>> for_kji # doctest: -NORMALIZE_WHITESPACE
845 For(k, iterable=Range(0, 5, 1), body=CodeBlock(
846 For(j, iterable=Range(0, 7, 1), body=CodeBlock(
847 For(i, iterable=Range(0, 10, 1), body=CodeBlock(
848 AddAugmentedAssignment(x, i*j*k)
849 ))
850 ))
851 ))
852 """
853 __slots__ = _fields = ('target', 'iterable', 'body')
854 _construct_target = staticmethod(_sympify)
856 @classmethod
857 def _construct_body(cls, itr):
858 if isinstance(itr, CodeBlock):
859 return itr
860 else:
861 return CodeBlock(*itr)
863 @classmethod
864 def _construct_iterable(cls, itr):
865 if not iterable(itr):
866 raise TypeError("iterable must be an iterable")
867 if isinstance(itr, list): # _sympify errors on lists because they are mutable
868 itr = tuple(itr)
869 return _sympify(itr)
872class String(Atom, Token):
873 """ SymPy object representing a string.
875 Atomic object which is not an expression (as opposed to Symbol).
877 Parameters
878 ==========
880 text : str
882 Examples
883 ========
885 >>> from sympy.codegen.ast import String
886 >>> f = String('foo')
887 >>> f
888 foo
889 >>> str(f)
890 'foo'
891 >>> f.text
892 'foo'
893 >>> print(repr(f))
894 String('foo')
896 """
897 __slots__ = _fields = ('text',)
898 not_in_args = ['text']
899 is_Atom = True
901 @classmethod
902 def _construct_text(cls, text):
903 if not isinstance(text, str):
904 raise TypeError("Argument text is not a string type.")
905 return text
907 def _sympystr(self, printer, *args, **kwargs):
908 return self.text
910 def kwargs(self, exclude = (), apply = None):
911 return {}
913 #to be removed when Atom is given a suitable func
914 @property
915 def func(self):
916 return lambda: self
918 def _latex(self, printer):
919 from sympy.printing.latex import latex_escape
920 return r'\texttt{{"{}"}}'.format(latex_escape(self.text))
922class QuotedString(String):
923 """ Represents a string which should be printed with quotes. """
925class Comment(String):
926 """ Represents a comment. """
928class Node(Token):
929 """ Subclass of Token, carrying the attribute 'attrs' (Tuple)
931 Examples
932 ========
934 >>> from sympy.codegen.ast import Node, value_const, pointer_const
935 >>> n1 = Node([value_const])
936 >>> n1.attr_params('value_const') # get the parameters of attribute (by name)
937 ()
938 >>> from sympy.codegen.fnodes import dimension
939 >>> n2 = Node([value_const, dimension(5, 3)])
940 >>> n2.attr_params(value_const) # get the parameters of attribute (by Attribute instance)
941 ()
942 >>> n2.attr_params('dimension') # get the parameters of attribute (by name)
943 (5, 3)
944 >>> n2.attr_params(pointer_const) is None
945 True
947 """
949 __slots__: tuple[str, ...] = ('attrs',)
950 _fields = __slots__
952 defaults: dict[str, Any] = {'attrs': Tuple()}
954 _construct_attrs = staticmethod(_mk_Tuple)
956 def attr_params(self, looking_for):
957 """ Returns the parameters of the Attribute with name ``looking_for`` in self.attrs """
958 for attr in self.attrs:
959 if str(attr.name) == str(looking_for):
960 return attr.parameters
963class Type(Token):
964 """ Represents a type.
966 Explanation
967 ===========
969 The naming is a super-set of NumPy naming. Type has a classmethod
970 ``from_expr`` which offer type deduction. It also has a method
971 ``cast_check`` which casts the argument to its type, possibly raising an
972 exception if rounding error is not within tolerances, or if the value is not
973 representable by the underlying data type (e.g. unsigned integers).
975 Parameters
976 ==========
978 name : str
979 Name of the type, e.g. ``object``, ``int16``, ``float16`` (where the latter two
980 would use the ``Type`` sub-classes ``IntType`` and ``FloatType`` respectively).
981 If a ``Type`` instance is given, the said instance is returned.
983 Examples
984 ========
986 >>> from sympy.codegen.ast import Type
987 >>> t = Type.from_expr(42)
988 >>> t
989 integer
990 >>> print(repr(t))
991 IntBaseType(String('integer'))
992 >>> from sympy.codegen.ast import uint8
993 >>> uint8.cast_check(-1) # doctest: +ELLIPSIS
994 Traceback (most recent call last):
995 ...
996 ValueError: Minimum value for data type bigger than new value.
997 >>> from sympy.codegen.ast import float32
998 >>> v6 = 0.123456
999 >>> float32.cast_check(v6)
1000 0.123456
1001 >>> v10 = 12345.67894
1002 >>> float32.cast_check(v10) # doctest: +ELLIPSIS
1003 Traceback (most recent call last):
1004 ...
1005 ValueError: Casting gives a significantly different value.
1006 >>> boost_mp50 = Type('boost::multiprecision::cpp_dec_float_50')
1007 >>> from sympy import cxxcode
1008 >>> from sympy.codegen.ast import Declaration, Variable
1009 >>> cxxcode(Declaration(Variable('x', type=boost_mp50)))
1010 'boost::multiprecision::cpp_dec_float_50 x'
1012 References
1013 ==========
1015 .. [1] https://numpy.org/doc/stable/user/basics.types.html
1017 """
1018 __slots__: tuple[str, ...] = ('name',)
1019 _fields = __slots__
1021 _construct_name = String
1023 def _sympystr(self, printer, *args, **kwargs):
1024 return str(self.name)
1026 @classmethod
1027 def from_expr(cls, expr):
1028 """ Deduces type from an expression or a ``Symbol``.
1030 Parameters
1031 ==========
1033 expr : number or SymPy object
1034 The type will be deduced from type or properties.
1036 Examples
1037 ========
1039 >>> from sympy.codegen.ast import Type, integer, complex_
1040 >>> Type.from_expr(2) == integer
1041 True
1042 >>> from sympy import Symbol
1043 >>> Type.from_expr(Symbol('z', complex=True)) == complex_
1044 True
1045 >>> Type.from_expr(sum) # doctest: +ELLIPSIS
1046 Traceback (most recent call last):
1047 ...
1048 ValueError: Could not deduce type from expr.
1050 Raises
1051 ======
1053 ValueError when type deduction fails.
1055 """
1056 if isinstance(expr, (float, Float)):
1057 return real
1058 if isinstance(expr, (int, Integer)) or getattr(expr, 'is_integer', False):
1059 return integer
1060 if getattr(expr, 'is_real', False):
1061 return real
1062 if isinstance(expr, complex) or getattr(expr, 'is_complex', False):
1063 return complex_
1064 if isinstance(expr, bool) or getattr(expr, 'is_Relational', False):
1065 return bool_
1066 else:
1067 raise ValueError("Could not deduce type from expr.")
1069 def _check(self, value):
1070 pass
1072 def cast_check(self, value, rtol=None, atol=0, precision_targets=None):
1073 """ Casts a value to the data type of the instance.
1075 Parameters
1076 ==========
1078 value : number
1079 rtol : floating point number
1080 Relative tolerance. (will be deduced if not given).
1081 atol : floating point number
1082 Absolute tolerance (in addition to ``rtol``).
1083 type_aliases : dict
1084 Maps substitutions for Type, e.g. {integer: int64, real: float32}
1086 Examples
1087 ========
1089 >>> from sympy.codegen.ast import integer, float32, int8
1090 >>> integer.cast_check(3.0) == 3
1091 True
1092 >>> float32.cast_check(1e-40) # doctest: +ELLIPSIS
1093 Traceback (most recent call last):
1094 ...
1095 ValueError: Minimum value for data type bigger than new value.
1096 >>> int8.cast_check(256) # doctest: +ELLIPSIS
1097 Traceback (most recent call last):
1098 ...
1099 ValueError: Maximum value for data type smaller than new value.
1100 >>> v10 = 12345.67894
1101 >>> float32.cast_check(v10) # doctest: +ELLIPSIS
1102 Traceback (most recent call last):
1103 ...
1104 ValueError: Casting gives a significantly different value.
1105 >>> from sympy.codegen.ast import float64
1106 >>> float64.cast_check(v10)
1107 12345.67894
1108 >>> from sympy import Float
1109 >>> v18 = Float('0.123456789012345646')
1110 >>> float64.cast_check(v18)
1111 Traceback (most recent call last):
1112 ...
1113 ValueError: Casting gives a significantly different value.
1114 >>> from sympy.codegen.ast import float80
1115 >>> float80.cast_check(v18)
1116 0.123456789012345649
1118 """
1119 val = sympify(value)
1121 ten = Integer(10)
1122 exp10 = getattr(self, 'decimal_dig', None)
1124 if rtol is None:
1125 rtol = 1e-15 if exp10 is None else 2.0*ten**(-exp10)
1127 def tol(num):
1128 return atol + rtol*abs(num)
1130 new_val = self.cast_nocheck(value)
1131 self._check(new_val)
1133 delta = new_val - val
1134 if abs(delta) > tol(val): # rounding, e.g. int(3.5) != 3.5
1135 raise ValueError("Casting gives a significantly different value.")
1137 return new_val
1139 def _latex(self, printer):
1140 from sympy.printing.latex import latex_escape
1141 type_name = latex_escape(self.__class__.__name__)
1142 name = latex_escape(self.name.text)
1143 return r"\text{{{}}}\left(\texttt{{{}}}\right)".format(type_name, name)
1146class IntBaseType(Type):
1147 """ Integer base type, contains no size information. """
1148 __slots__ = ()
1149 cast_nocheck = lambda self, i: Integer(int(i))
1152class _SizedIntType(IntBaseType):
1153 __slots__ = ('nbits',)
1154 _fields = Type._fields + __slots__
1156 _construct_nbits = Integer
1158 def _check(self, value):
1159 if value < self.min:
1160 raise ValueError("Value is too small: %d < %d" % (value, self.min))
1161 if value > self.max:
1162 raise ValueError("Value is too big: %d > %d" % (value, self.max))
1165class SignedIntType(_SizedIntType):
1166 """ Represents a signed integer type. """
1167 __slots__ = ()
1168 @property
1169 def min(self):
1170 return -2**(self.nbits-1)
1172 @property
1173 def max(self):
1174 return 2**(self.nbits-1) - 1
1177class UnsignedIntType(_SizedIntType):
1178 """ Represents an unsigned integer type. """
1179 __slots__ = ()
1180 @property
1181 def min(self):
1182 return 0
1184 @property
1185 def max(self):
1186 return 2**self.nbits - 1
1188two = Integer(2)
1190class FloatBaseType(Type):
1191 """ Represents a floating point number type. """
1192 __slots__ = ()
1193 cast_nocheck = Float
1195class FloatType(FloatBaseType):
1196 """ Represents a floating point type with fixed bit width.
1198 Base 2 & one sign bit is assumed.
1200 Parameters
1201 ==========
1203 name : str
1204 Name of the type.
1205 nbits : integer
1206 Number of bits used (storage).
1207 nmant : integer
1208 Number of bits used to represent the mantissa.
1209 nexp : integer
1210 Number of bits used to represent the mantissa.
1212 Examples
1213 ========
1215 >>> from sympy import S
1216 >>> from sympy.codegen.ast import FloatType
1217 >>> half_precision = FloatType('f16', nbits=16, nmant=10, nexp=5)
1218 >>> half_precision.max
1219 65504
1220 >>> half_precision.tiny == S(2)**-14
1221 True
1222 >>> half_precision.eps == S(2)**-10
1223 True
1224 >>> half_precision.dig == 3
1225 True
1226 >>> half_precision.decimal_dig == 5
1227 True
1228 >>> half_precision.cast_check(1.0)
1229 1.0
1230 >>> half_precision.cast_check(1e5) # doctest: +ELLIPSIS
1231 Traceback (most recent call last):
1232 ...
1233 ValueError: Maximum value for data type smaller than new value.
1234 """
1236 __slots__ = ('nbits', 'nmant', 'nexp',)
1237 _fields = Type._fields + __slots__
1239 _construct_nbits = _construct_nmant = _construct_nexp = Integer
1242 @property
1243 def max_exponent(self):
1244 """ The largest positive number n, such that 2**(n - 1) is a representable finite value. """
1245 # cf. C++'s ``std::numeric_limits::max_exponent``
1246 return two**(self.nexp - 1)
1248 @property
1249 def min_exponent(self):
1250 """ The lowest negative number n, such that 2**(n - 1) is a valid normalized number. """
1251 # cf. C++'s ``std::numeric_limits::min_exponent``
1252 return 3 - self.max_exponent
1254 @property
1255 def max(self):
1256 """ Maximum value representable. """
1257 return (1 - two**-(self.nmant+1))*two**self.max_exponent
1259 @property
1260 def tiny(self):
1261 """ The minimum positive normalized value. """
1262 # See C macros: FLT_MIN, DBL_MIN, LDBL_MIN
1263 # or C++'s ``std::numeric_limits::min``
1264 # or numpy.finfo(dtype).tiny
1265 return two**(self.min_exponent - 1)
1268 @property
1269 def eps(self):
1270 """ Difference between 1.0 and the next representable value. """
1271 return two**(-self.nmant)
1273 @property
1274 def dig(self):
1275 """ Number of decimal digits that are guaranteed to be preserved in text.
1277 When converting text -> float -> text, you are guaranteed that at least ``dig``
1278 number of digits are preserved with respect to rounding or overflow.
1279 """
1280 from sympy.functions import floor, log
1281 return floor(self.nmant * log(2)/log(10))
1283 @property
1284 def decimal_dig(self):
1285 """ Number of digits needed to store & load without loss.
1287 Explanation
1288 ===========
1290 Number of decimal digits needed to guarantee that two consecutive conversions
1291 (float -> text -> float) to be idempotent. This is useful when one do not want
1292 to loose precision due to rounding errors when storing a floating point value
1293 as text.
1294 """
1295 from sympy.functions import ceiling, log
1296 return ceiling((self.nmant + 1) * log(2)/log(10) + 1)
1298 def cast_nocheck(self, value):
1299 """ Casts without checking if out of bounds or subnormal. """
1300 if value == oo: # float(oo) or oo
1301 return float(oo)
1302 elif value == -oo: # float(-oo) or -oo
1303 return float(-oo)
1304 return Float(str(sympify(value).evalf(self.decimal_dig)), self.decimal_dig)
1306 def _check(self, value):
1307 if value < -self.max:
1308 raise ValueError("Value is too small: %d < %d" % (value, -self.max))
1309 if value > self.max:
1310 raise ValueError("Value is too big: %d > %d" % (value, self.max))
1311 if abs(value) < self.tiny:
1312 raise ValueError("Smallest (absolute) value for data type bigger than new value.")
1314class ComplexBaseType(FloatBaseType):
1316 __slots__ = ()
1318 def cast_nocheck(self, value):
1319 """ Casts without checking if out of bounds or subnormal. """
1320 from sympy.functions import re, im
1321 return (
1322 super().cast_nocheck(re(value)) +
1323 super().cast_nocheck(im(value))*1j
1324 )
1326 def _check(self, value):
1327 from sympy.functions import re, im
1328 super()._check(re(value))
1329 super()._check(im(value))
1332class ComplexType(ComplexBaseType, FloatType):
1333 """ Represents a complex floating point number. """
1334 __slots__ = ()
1337# NumPy types:
1338intc = IntBaseType('intc')
1339intp = IntBaseType('intp')
1340int8 = SignedIntType('int8', 8)
1341int16 = SignedIntType('int16', 16)
1342int32 = SignedIntType('int32', 32)
1343int64 = SignedIntType('int64', 64)
1344uint8 = UnsignedIntType('uint8', 8)
1345uint16 = UnsignedIntType('uint16', 16)
1346uint32 = UnsignedIntType('uint32', 32)
1347uint64 = UnsignedIntType('uint64', 64)
1348float16 = FloatType('float16', 16, nexp=5, nmant=10) # IEEE 754 binary16, Half precision
1349float32 = FloatType('float32', 32, nexp=8, nmant=23) # IEEE 754 binary32, Single precision
1350float64 = FloatType('float64', 64, nexp=11, nmant=52) # IEEE 754 binary64, Double precision
1351float80 = FloatType('float80', 80, nexp=15, nmant=63) # x86 extended precision (1 integer part bit), "long double"
1352float128 = FloatType('float128', 128, nexp=15, nmant=112) # IEEE 754 binary128, Quadruple precision
1353float256 = FloatType('float256', 256, nexp=19, nmant=236) # IEEE 754 binary256, Octuple precision
1355complex64 = ComplexType('complex64', nbits=64, **float32.kwargs(exclude=('name', 'nbits')))
1356complex128 = ComplexType('complex128', nbits=128, **float64.kwargs(exclude=('name', 'nbits')))
1358# Generic types (precision may be chosen by code printers):
1359untyped = Type('untyped')
1360real = FloatBaseType('real')
1361integer = IntBaseType('integer')
1362complex_ = ComplexBaseType('complex')
1363bool_ = Type('bool')
1366class Attribute(Token):
1367 """ Attribute (possibly parametrized)
1369 For use with :class:`sympy.codegen.ast.Node` (which takes instances of
1370 ``Attribute`` as ``attrs``).
1372 Parameters
1373 ==========
1375 name : str
1376 parameters : Tuple
1378 Examples
1379 ========
1381 >>> from sympy.codegen.ast import Attribute
1382 >>> volatile = Attribute('volatile')
1383 >>> volatile
1384 volatile
1385 >>> print(repr(volatile))
1386 Attribute(String('volatile'))
1387 >>> a = Attribute('foo', [1, 2, 3])
1388 >>> a
1389 foo(1, 2, 3)
1390 >>> a.parameters == (1, 2, 3)
1391 True
1392 """
1393 __slots__ = _fields = ('name', 'parameters')
1394 defaults = {'parameters': Tuple()}
1396 _construct_name = String
1397 _construct_parameters = staticmethod(_mk_Tuple)
1399 def _sympystr(self, printer, *args, **kwargs):
1400 result = str(self.name)
1401 if self.parameters:
1402 result += '(%s)' % ', '.join((printer._print(
1403 arg, *args, **kwargs) for arg in self.parameters))
1404 return result
1406value_const = Attribute('value_const')
1407pointer_const = Attribute('pointer_const')
1410class Variable(Node):
1411 """ Represents a variable.
1413 Parameters
1414 ==========
1416 symbol : Symbol
1417 type : Type (optional)
1418 Type of the variable.
1419 attrs : iterable of Attribute instances
1420 Will be stored as a Tuple.
1422 Examples
1423 ========
1425 >>> from sympy import Symbol
1426 >>> from sympy.codegen.ast import Variable, float32, integer
1427 >>> x = Symbol('x')
1428 >>> v = Variable(x, type=float32)
1429 >>> v.attrs
1430 ()
1431 >>> v == Variable('x')
1432 False
1433 >>> v == Variable('x', type=float32)
1434 True
1435 >>> v
1436 Variable(x, type=float32)
1438 One may also construct a ``Variable`` instance with the type deduced from
1439 assumptions about the symbol using the ``deduced`` classmethod:
1441 >>> i = Symbol('i', integer=True)
1442 >>> v = Variable.deduced(i)
1443 >>> v.type == integer
1444 True
1445 >>> v == Variable('i')
1446 False
1447 >>> from sympy.codegen.ast import value_const
1448 >>> value_const in v.attrs
1449 False
1450 >>> w = Variable('w', attrs=[value_const])
1451 >>> w
1452 Variable(w, attrs=(value_const,))
1453 >>> value_const in w.attrs
1454 True
1455 >>> w.as_Declaration(value=42)
1456 Declaration(Variable(w, value=42, attrs=(value_const,)))
1458 """
1460 __slots__ = ('symbol', 'type', 'value')
1461 _fields = __slots__ + Node._fields
1463 defaults = Node.defaults.copy()
1464 defaults.update({'type': untyped, 'value': none})
1466 _construct_symbol = staticmethod(sympify)
1467 _construct_value = staticmethod(sympify)
1469 @classmethod
1470 def deduced(cls, symbol, value=None, attrs=Tuple(), cast_check=True):
1471 """ Alt. constructor with type deduction from ``Type.from_expr``.
1473 Deduces type primarily from ``symbol``, secondarily from ``value``.
1475 Parameters
1476 ==========
1478 symbol : Symbol
1479 value : expr
1480 (optional) value of the variable.
1481 attrs : iterable of Attribute instances
1482 cast_check : bool
1483 Whether to apply ``Type.cast_check`` on ``value``.
1485 Examples
1486 ========
1488 >>> from sympy import Symbol
1489 >>> from sympy.codegen.ast import Variable, complex_
1490 >>> n = Symbol('n', integer=True)
1491 >>> str(Variable.deduced(n).type)
1492 'integer'
1493 >>> x = Symbol('x', real=True)
1494 >>> v = Variable.deduced(x)
1495 >>> v.type
1496 real
1497 >>> z = Symbol('z', complex=True)
1498 >>> Variable.deduced(z).type == complex_
1499 True
1501 """
1502 if isinstance(symbol, Variable):
1503 return symbol
1505 try:
1506 type_ = Type.from_expr(symbol)
1507 except ValueError:
1508 type_ = Type.from_expr(value)
1510 if value is not None and cast_check:
1511 value = type_.cast_check(value)
1512 return cls(symbol, type=type_, value=value, attrs=attrs)
1514 def as_Declaration(self, **kwargs):
1515 """ Convenience method for creating a Declaration instance.
1517 Explanation
1518 ===========
1520 If the variable of the Declaration need to wrap a modified
1521 variable keyword arguments may be passed (overriding e.g.
1522 the ``value`` of the Variable instance).
1524 Examples
1525 ========
1527 >>> from sympy.codegen.ast import Variable, NoneToken
1528 >>> x = Variable('x')
1529 >>> decl1 = x.as_Declaration()
1530 >>> # value is special NoneToken() which must be tested with == operator
1531 >>> decl1.variable.value is None # won't work
1532 False
1533 >>> decl1.variable.value == None # not PEP-8 compliant
1534 True
1535 >>> decl1.variable.value == NoneToken() # OK
1536 True
1537 >>> decl2 = x.as_Declaration(value=42.0)
1538 >>> decl2.variable.value == 42.0
1539 True
1541 """
1542 kw = self.kwargs()
1543 kw.update(kwargs)
1544 return Declaration(self.func(**kw))
1546 def _relation(self, rhs, op):
1547 try:
1548 rhs = _sympify(rhs)
1549 except SympifyError:
1550 raise TypeError("Invalid comparison %s < %s" % (self, rhs))
1551 return op(self, rhs, evaluate=False)
1553 __lt__ = lambda self, other: self._relation(other, Lt)
1554 __le__ = lambda self, other: self._relation(other, Le)
1555 __ge__ = lambda self, other: self._relation(other, Ge)
1556 __gt__ = lambda self, other: self._relation(other, Gt)
1558class Pointer(Variable):
1559 """ Represents a pointer. See ``Variable``.
1561 Examples
1562 ========
1564 Can create instances of ``Element``:
1566 >>> from sympy import Symbol
1567 >>> from sympy.codegen.ast import Pointer
1568 >>> i = Symbol('i', integer=True)
1569 >>> p = Pointer('x')
1570 >>> p[i+1]
1571 Element(x, indices=(i + 1,))
1573 """
1574 __slots__ = ()
1576 def __getitem__(self, key):
1577 try:
1578 return Element(self.symbol, key)
1579 except TypeError:
1580 return Element(self.symbol, (key,))
1583class Element(Token):
1584 """ Element in (a possibly N-dimensional) array.
1586 Examples
1587 ========
1589 >>> from sympy.codegen.ast import Element
1590 >>> elem = Element('x', 'ijk')
1591 >>> elem.symbol.name == 'x'
1592 True
1593 >>> elem.indices
1594 (i, j, k)
1595 >>> from sympy import ccode
1596 >>> ccode(elem)
1597 'x[i][j][k]'
1598 >>> ccode(Element('x', 'ijk', strides='lmn', offset='o'))
1599 'x[i*l + j*m + k*n + o]'
1601 """
1602 __slots__ = _fields = ('symbol', 'indices', 'strides', 'offset')
1603 defaults = {'strides': none, 'offset': none}
1604 _construct_symbol = staticmethod(sympify)
1605 _construct_indices = staticmethod(lambda arg: Tuple(*arg))
1606 _construct_strides = staticmethod(lambda arg: Tuple(*arg))
1607 _construct_offset = staticmethod(sympify)
1610class Declaration(Token):
1611 """ Represents a variable declaration
1613 Parameters
1614 ==========
1616 variable : Variable
1618 Examples
1619 ========
1621 >>> from sympy.codegen.ast import Declaration, NoneToken, untyped
1622 >>> z = Declaration('z')
1623 >>> z.variable.type == untyped
1624 True
1625 >>> # value is special NoneToken() which must be tested with == operator
1626 >>> z.variable.value is None # won't work
1627 False
1628 >>> z.variable.value == None # not PEP-8 compliant
1629 True
1630 >>> z.variable.value == NoneToken() # OK
1631 True
1632 """
1633 __slots__ = _fields = ('variable',)
1634 _construct_variable = Variable
1637class While(Token):
1638 """ Represents a 'for-loop' in the code.
1640 Expressions are of the form:
1641 "while condition:
1642 body..."
1644 Parameters
1645 ==========
1647 condition : expression convertible to Boolean
1648 body : CodeBlock or iterable
1649 When passed an iterable it is used to instantiate a CodeBlock.
1651 Examples
1652 ========
1654 >>> from sympy import symbols, Gt, Abs
1655 >>> from sympy.codegen import aug_assign, Assignment, While
1656 >>> x, dx = symbols('x dx')
1657 >>> expr = 1 - x**2
1658 >>> whl = While(Gt(Abs(dx), 1e-9), [
1659 ... Assignment(dx, -expr/expr.diff(x)),
1660 ... aug_assign(x, '+', dx)
1661 ... ])
1663 """
1664 __slots__ = _fields = ('condition', 'body')
1665 _construct_condition = staticmethod(lambda cond: _sympify(cond))
1667 @classmethod
1668 def _construct_body(cls, itr):
1669 if isinstance(itr, CodeBlock):
1670 return itr
1671 else:
1672 return CodeBlock(*itr)
1675class Scope(Token):
1676 """ Represents a scope in the code.
1678 Parameters
1679 ==========
1681 body : CodeBlock or iterable
1682 When passed an iterable it is used to instantiate a CodeBlock.
1684 """
1685 __slots__ = _fields = ('body',)
1687 @classmethod
1688 def _construct_body(cls, itr):
1689 if isinstance(itr, CodeBlock):
1690 return itr
1691 else:
1692 return CodeBlock(*itr)
1695class Stream(Token):
1696 """ Represents a stream.
1698 There are two predefined Stream instances ``stdout`` & ``stderr``.
1700 Parameters
1701 ==========
1703 name : str
1705 Examples
1706 ========
1708 >>> from sympy import pycode, Symbol
1709 >>> from sympy.codegen.ast import Print, stderr, QuotedString
1710 >>> print(pycode(Print(['x'], file=stderr)))
1711 print(x, file=sys.stderr)
1712 >>> x = Symbol('x')
1713 >>> print(pycode(Print([QuotedString('x')], file=stderr))) # print literally "x"
1714 print("x", file=sys.stderr)
1716 """
1717 __slots__ = _fields = ('name',)
1718 _construct_name = String
1720stdout = Stream('stdout')
1721stderr = Stream('stderr')
1724class Print(Token):
1725 """ Represents print command in the code.
1727 Parameters
1728 ==========
1730 formatstring : str
1731 *args : Basic instances (or convertible to such through sympify)
1733 Examples
1734 ========
1736 >>> from sympy.codegen.ast import Print
1737 >>> from sympy import pycode
1738 >>> print(pycode(Print('x y'.split(), "coordinate: %12.5g %12.5g")))
1739 print("coordinate: %12.5g %12.5g" % (x, y))
1741 """
1743 __slots__ = _fields = ('print_args', 'format_string', 'file')
1744 defaults = {'format_string': none, 'file': none}
1746 _construct_print_args = staticmethod(_mk_Tuple)
1747 _construct_format_string = QuotedString
1748 _construct_file = Stream
1751class FunctionPrototype(Node):
1752 """ Represents a function prototype
1754 Allows the user to generate forward declaration in e.g. C/C++.
1756 Parameters
1757 ==========
1759 return_type : Type
1760 name : str
1761 parameters: iterable of Variable instances
1762 attrs : iterable of Attribute instances
1764 Examples
1765 ========
1767 >>> from sympy import ccode, symbols
1768 >>> from sympy.codegen.ast import real, FunctionPrototype
1769 >>> x, y = symbols('x y', real=True)
1770 >>> fp = FunctionPrototype(real, 'foo', [x, y])
1771 >>> ccode(fp)
1772 'double foo(double x, double y)'
1774 """
1776 __slots__ = ('return_type', 'name', 'parameters')
1777 _fields: tuple[str, ...] = __slots__ + Node._fields
1779 _construct_return_type = Type
1780 _construct_name = String
1782 @staticmethod
1783 def _construct_parameters(args):
1784 def _var(arg):
1785 if isinstance(arg, Declaration):
1786 return arg.variable
1787 elif isinstance(arg, Variable):
1788 return arg
1789 else:
1790 return Variable.deduced(arg)
1791 return Tuple(*map(_var, args))
1793 @classmethod
1794 def from_FunctionDefinition(cls, func_def):
1795 if not isinstance(func_def, FunctionDefinition):
1796 raise TypeError("func_def is not an instance of FunctionDefinition")
1797 return cls(**func_def.kwargs(exclude=('body',)))
1800class FunctionDefinition(FunctionPrototype):
1801 """ Represents a function definition in the code.
1803 Parameters
1804 ==========
1806 return_type : Type
1807 name : str
1808 parameters: iterable of Variable instances
1809 body : CodeBlock or iterable
1810 attrs : iterable of Attribute instances
1812 Examples
1813 ========
1815 >>> from sympy import ccode, symbols
1816 >>> from sympy.codegen.ast import real, FunctionPrototype
1817 >>> x, y = symbols('x y', real=True)
1818 >>> fp = FunctionPrototype(real, 'foo', [x, y])
1819 >>> ccode(fp)
1820 'double foo(double x, double y)'
1821 >>> from sympy.codegen.ast import FunctionDefinition, Return
1822 >>> body = [Return(x*y)]
1823 >>> fd = FunctionDefinition.from_FunctionPrototype(fp, body)
1824 >>> print(ccode(fd))
1825 double foo(double x, double y){
1826 return x*y;
1827 }
1828 """
1830 __slots__ = ('body', )
1831 _fields = FunctionPrototype._fields[:-1] + __slots__ + Node._fields
1833 @classmethod
1834 def _construct_body(cls, itr):
1835 if isinstance(itr, CodeBlock):
1836 return itr
1837 else:
1838 return CodeBlock(*itr)
1840 @classmethod
1841 def from_FunctionPrototype(cls, func_proto, body):
1842 if not isinstance(func_proto, FunctionPrototype):
1843 raise TypeError("func_proto is not an instance of FunctionPrototype")
1844 return cls(body=body, **func_proto.kwargs())
1847class Return(Token):
1848 """ Represents a return command in the code.
1850 Parameters
1851 ==========
1853 return : Basic
1855 Examples
1856 ========
1858 >>> from sympy.codegen.ast import Return
1859 >>> from sympy.printing.pycode import pycode
1860 >>> from sympy import Symbol
1861 >>> x = Symbol('x')
1862 >>> print(pycode(Return(x)))
1863 return x
1865 """
1866 __slots__ = _fields = ('return',)
1867 _construct_return=staticmethod(_sympify)
1870class FunctionCall(Token, Expr):
1871 """ Represents a call to a function in the code.
1873 Parameters
1874 ==========
1876 name : str
1877 function_args : Tuple
1879 Examples
1880 ========
1882 >>> from sympy.codegen.ast import FunctionCall
1883 >>> from sympy import pycode
1884 >>> fcall = FunctionCall('foo', 'bar baz'.split())
1885 >>> print(pycode(fcall))
1886 foo(bar, baz)
1888 """
1889 __slots__ = _fields = ('name', 'function_args')
1891 _construct_name = String
1892 _construct_function_args = staticmethod(lambda args: Tuple(*args))