Coverage for /usr/lib/python3/dist-packages/sympy/printing/codeprinter.py: 43%
349 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
1from __future__ import annotations
2from typing import Any
4from functools import wraps
6from sympy.core import Add, Mul, Pow, S, sympify, Float
7from sympy.core.basic import Basic
8from sympy.core.expr import UnevaluatedExpr
9from sympy.core.function import Lambda
10from sympy.core.mul import _keep_coeff
11from sympy.core.sorting import default_sort_key
12from sympy.core.symbol import Symbol
13from sympy.functions.elementary.complexes import re
14from sympy.printing.str import StrPrinter
15from sympy.printing.precedence import precedence, PRECEDENCE
18class requires:
19 """ Decorator for registering requirements on print methods. """
20 def __init__(self, **kwargs):
21 self._req = kwargs
23 def __call__(self, method):
24 def _method_wrapper(self_, *args, **kwargs):
25 for k, v in self._req.items():
26 getattr(self_, k).update(v)
27 return method(self_, *args, **kwargs)
28 return wraps(method)(_method_wrapper)
31class AssignmentError(Exception):
32 """
33 Raised if an assignment variable for a loop is missing.
34 """
35 pass
38def _convert_python_lists(arg):
39 if isinstance(arg, list):
40 from sympy.codegen.abstract_nodes import List
41 return List(*(_convert_python_lists(e) for e in arg))
42 elif isinstance(arg, tuple):
43 return tuple(_convert_python_lists(e) for e in arg)
44 else:
45 return arg
48class CodePrinter(StrPrinter):
49 """
50 The base class for code-printing subclasses.
51 """
53 _operators = {
54 'and': '&&',
55 'or': '||',
56 'not': '!',
57 }
59 _default_settings: dict[str, Any] = {
60 'order': None,
61 'full_prec': 'auto',
62 'error_on_reserved': False,
63 'reserved_word_suffix': '_',
64 'human': True,
65 'inline': False,
66 'allow_unknown_functions': False,
67 }
69 # Functions which are "simple" to rewrite to other functions that
70 # may be supported
71 # function_to_rewrite : (function_to_rewrite_to, iterable_with_other_functions_required)
72 _rewriteable_functions = {
73 'cot': ('tan', []),
74 'csc': ('sin', []),
75 'sec': ('cos', []),
76 'acot': ('atan', []),
77 'acsc': ('asin', []),
78 'asec': ('acos', []),
79 'coth': ('exp', []),
80 'csch': ('exp', []),
81 'sech': ('exp', []),
82 'acoth': ('log', []),
83 'acsch': ('log', []),
84 'asech': ('log', []),
85 'catalan': ('gamma', []),
86 'fibonacci': ('sqrt', []),
87 'lucas': ('sqrt', []),
88 'beta': ('gamma', []),
89 'sinc': ('sin', ['Piecewise']),
90 'Mod': ('floor', []),
91 'factorial': ('gamma', []),
92 'factorial2': ('gamma', ['Piecewise']),
93 'subfactorial': ('uppergamma', []),
94 'RisingFactorial': ('gamma', ['Piecewise']),
95 'FallingFactorial': ('gamma', ['Piecewise']),
96 'binomial': ('gamma', []),
97 'frac': ('floor', []),
98 'Max': ('Piecewise', []),
99 'Min': ('Piecewise', []),
100 'Heaviside': ('Piecewise', []),
101 'erf2': ('erf', []),
102 'erfc': ('erf', []),
103 'Li': ('li', []),
104 'Ei': ('li', []),
105 'dirichlet_eta': ('zeta', []),
106 'riemann_xi': ('zeta', ['gamma']),
107 }
109 def __init__(self, settings=None):
111 super().__init__(settings=settings)
112 if not hasattr(self, 'reserved_words'):
113 self.reserved_words = set()
115 def _handle_UnevaluatedExpr(self, expr):
116 return expr.replace(re, lambda arg: arg if isinstance(
117 arg, UnevaluatedExpr) and arg.args[0].is_real else re(arg))
119 def doprint(self, expr, assign_to=None):
120 """
121 Print the expression as code.
123 Parameters
124 ----------
125 expr : Expression
126 The expression to be printed.
128 assign_to : Symbol, string, MatrixSymbol, list of strings or Symbols (optional)
129 If provided, the printed code will set the expression to a variable or multiple variables
130 with the name or names given in ``assign_to``.
131 """
132 from sympy.matrices.expressions.matexpr import MatrixSymbol
133 from sympy.codegen.ast import CodeBlock, Assignment
135 def _handle_assign_to(expr, assign_to):
136 if assign_to is None:
137 return sympify(expr)
138 if isinstance(assign_to, (list, tuple)):
139 if len(expr) != len(assign_to):
140 raise ValueError('Failed to assign an expression of length {} to {} variables'.format(len(expr), len(assign_to)))
141 return CodeBlock(*[_handle_assign_to(lhs, rhs) for lhs, rhs in zip(expr, assign_to)])
142 if isinstance(assign_to, str):
143 if expr.is_Matrix:
144 assign_to = MatrixSymbol(assign_to, *expr.shape)
145 else:
146 assign_to = Symbol(assign_to)
147 elif not isinstance(assign_to, Basic):
148 raise TypeError("{} cannot assign to object of type {}".format(
149 type(self).__name__, type(assign_to)))
150 return Assignment(assign_to, expr)
152 expr = _convert_python_lists(expr)
153 expr = _handle_assign_to(expr, assign_to)
155 # Remove re(...) nodes due to UnevaluatedExpr.is_real always is None:
156 expr = self._handle_UnevaluatedExpr(expr)
158 # keep a set of expressions that are not strictly translatable to Code
159 # and number constants that must be declared and initialized
160 self._not_supported = set()
161 self._number_symbols = set()
163 lines = self._print(expr).splitlines()
165 # format the output
166 if self._settings["human"]:
167 frontlines = []
168 if self._not_supported:
169 frontlines.append(self._get_comment(
170 "Not supported in {}:".format(self.language)))
171 for expr in sorted(self._not_supported, key=str):
172 frontlines.append(self._get_comment(type(expr).__name__))
173 for name, value in sorted(self._number_symbols, key=str):
174 frontlines.append(self._declare_number_const(name, value))
175 lines = frontlines + lines
176 lines = self._format_code(lines)
177 result = "\n".join(lines)
178 else:
179 lines = self._format_code(lines)
180 num_syms = {(k, self._print(v)) for k, v in self._number_symbols}
181 result = (num_syms, self._not_supported, "\n".join(lines))
182 self._not_supported = set()
183 self._number_symbols = set()
184 return result
186 def _doprint_loops(self, expr, assign_to=None):
187 # Here we print an expression that contains Indexed objects, they
188 # correspond to arrays in the generated code. The low-level implementation
189 # involves looping over array elements and possibly storing results in temporary
190 # variables or accumulate it in the assign_to object.
192 if self._settings.get('contract', True):
193 from sympy.tensor import get_contraction_structure
194 # Setup loops over non-dummy indices -- all terms need these
195 indices = self._get_expression_indices(expr, assign_to)
196 # Setup loops over dummy indices -- each term needs separate treatment
197 dummies = get_contraction_structure(expr)
198 else:
199 indices = []
200 dummies = {None: (expr,)}
201 openloop, closeloop = self._get_loop_opening_ending(indices)
203 # terms with no summations first
204 if None in dummies:
205 text = StrPrinter.doprint(self, Add(*dummies[None]))
206 else:
207 # If all terms have summations we must initialize array to Zero
208 text = StrPrinter.doprint(self, 0)
210 # skip redundant assignments (where lhs == rhs)
211 lhs_printed = self._print(assign_to)
212 lines = []
213 if text != lhs_printed:
214 lines.extend(openloop)
215 if assign_to is not None:
216 text = self._get_statement("%s = %s" % (lhs_printed, text))
217 lines.append(text)
218 lines.extend(closeloop)
220 # then terms with summations
221 for d in dummies:
222 if isinstance(d, tuple):
223 indices = self._sort_optimized(d, expr)
224 openloop_d, closeloop_d = self._get_loop_opening_ending(
225 indices)
227 for term in dummies[d]:
228 if term in dummies and not ([list(f.keys()) for f in dummies[term]]
229 == [[None] for f in dummies[term]]):
230 # If one factor in the term has it's own internal
231 # contractions, those must be computed first.
232 # (temporary variables?)
233 raise NotImplementedError(
234 "FIXME: no support for contractions in factor yet")
235 else:
237 # We need the lhs expression as an accumulator for
238 # the loops, i.e
239 #
240 # for (int d=0; d < dim; d++){
241 # lhs[] = lhs[] + term[][d]
242 # } ^.................. the accumulator
243 #
244 # We check if the expression already contains the
245 # lhs, and raise an exception if it does, as that
246 # syntax is currently undefined. FIXME: What would be
247 # a good interpretation?
248 if assign_to is None:
249 raise AssignmentError(
250 "need assignment variable for loops")
251 if term.has(assign_to):
252 raise ValueError("FIXME: lhs present in rhs,\
253 this is undefined in CodePrinter")
255 lines.extend(openloop)
256 lines.extend(openloop_d)
257 text = "%s = %s" % (lhs_printed, StrPrinter.doprint(
258 self, assign_to + term))
259 lines.append(self._get_statement(text))
260 lines.extend(closeloop_d)
261 lines.extend(closeloop)
263 return "\n".join(lines)
265 def _get_expression_indices(self, expr, assign_to):
266 from sympy.tensor import get_indices
267 rinds, junk = get_indices(expr)
268 linds, junk = get_indices(assign_to)
270 # support broadcast of scalar
271 if linds and not rinds:
272 rinds = linds
273 if rinds != linds:
274 raise ValueError("lhs indices must match non-dummy"
275 " rhs indices in %s" % expr)
277 return self._sort_optimized(rinds, assign_to)
279 def _sort_optimized(self, indices, expr):
281 from sympy.tensor.indexed import Indexed
283 if not indices:
284 return []
286 # determine optimized loop order by giving a score to each index
287 # the index with the highest score are put in the innermost loop.
288 score_table = {}
289 for i in indices:
290 score_table[i] = 0
292 arrays = expr.atoms(Indexed)
293 for arr in arrays:
294 for p, ind in enumerate(arr.indices):
295 try:
296 score_table[ind] += self._rate_index_position(p)
297 except KeyError:
298 pass
300 return sorted(indices, key=lambda x: score_table[x])
302 def _rate_index_position(self, p):
303 """function to calculate score based on position among indices
305 This method is used to sort loops in an optimized order, see
306 CodePrinter._sort_optimized()
307 """
308 raise NotImplementedError("This function must be implemented by "
309 "subclass of CodePrinter.")
311 def _get_statement(self, codestring):
312 """Formats a codestring with the proper line ending."""
313 raise NotImplementedError("This function must be implemented by "
314 "subclass of CodePrinter.")
316 def _get_comment(self, text):
317 """Formats a text string as a comment."""
318 raise NotImplementedError("This function must be implemented by "
319 "subclass of CodePrinter.")
321 def _declare_number_const(self, name, value):
322 """Declare a numeric constant at the top of a function"""
323 raise NotImplementedError("This function must be implemented by "
324 "subclass of CodePrinter.")
326 def _format_code(self, lines):
327 """Take in a list of lines of code, and format them accordingly.
329 This may include indenting, wrapping long lines, etc..."""
330 raise NotImplementedError("This function must be implemented by "
331 "subclass of CodePrinter.")
333 def _get_loop_opening_ending(self, indices):
334 """Returns a tuple (open_lines, close_lines) containing lists
335 of codelines"""
336 raise NotImplementedError("This function must be implemented by "
337 "subclass of CodePrinter.")
339 def _print_Dummy(self, expr):
340 if expr.name.startswith('Dummy_'):
341 return '_' + expr.name
342 else:
343 return '%s_%d' % (expr.name, expr.dummy_index)
345 def _print_CodeBlock(self, expr):
346 return '\n'.join([self._print(i) for i in expr.args])
348 def _print_String(self, string):
349 return str(string)
351 def _print_QuotedString(self, arg):
352 return '"%s"' % arg.text
354 def _print_Comment(self, string):
355 return self._get_comment(str(string))
357 def _print_Assignment(self, expr):
358 from sympy.codegen.ast import Assignment
359 from sympy.functions.elementary.piecewise import Piecewise
360 from sympy.matrices.expressions.matexpr import MatrixSymbol
361 from sympy.tensor.indexed import IndexedBase
362 lhs = expr.lhs
363 rhs = expr.rhs
364 # We special case assignments that take multiple lines
365 if isinstance(expr.rhs, Piecewise):
366 # Here we modify Piecewise so each expression is now
367 # an Assignment, and then continue on the print.
368 expressions = []
369 conditions = []
370 for (e, c) in rhs.args:
371 expressions.append(Assignment(lhs, e))
372 conditions.append(c)
373 temp = Piecewise(*zip(expressions, conditions))
374 return self._print(temp)
375 elif isinstance(lhs, MatrixSymbol):
376 # Here we form an Assignment for each element in the array,
377 # printing each one.
378 lines = []
379 for (i, j) in self._traverse_matrix_indices(lhs):
380 temp = Assignment(lhs[i, j], rhs[i, j])
381 code0 = self._print(temp)
382 lines.append(code0)
383 return "\n".join(lines)
384 elif self._settings.get("contract", False) and (lhs.has(IndexedBase) or
385 rhs.has(IndexedBase)):
386 # Here we check if there is looping to be done, and if so
387 # print the required loops.
388 return self._doprint_loops(rhs, lhs)
389 else:
390 lhs_code = self._print(lhs)
391 rhs_code = self._print(rhs)
392 return self._get_statement("%s = %s" % (lhs_code, rhs_code))
394 def _print_AugmentedAssignment(self, expr):
395 lhs_code = self._print(expr.lhs)
396 rhs_code = self._print(expr.rhs)
397 return self._get_statement("{} {} {}".format(
398 *(self._print(arg) for arg in [lhs_code, expr.op, rhs_code])))
400 def _print_FunctionCall(self, expr):
401 return '%s(%s)' % (
402 expr.name,
403 ', '.join((self._print(arg) for arg in expr.function_args)))
405 def _print_Variable(self, expr):
406 return self._print(expr.symbol)
408 def _print_Symbol(self, expr):
410 name = super()._print_Symbol(expr)
412 if name in self.reserved_words:
413 if self._settings['error_on_reserved']:
414 msg = ('This expression includes the symbol "{}" which is a '
415 'reserved keyword in this language.')
416 raise ValueError(msg.format(name))
417 return name + self._settings['reserved_word_suffix']
418 else:
419 return name
421 def _can_print(self, name):
422 """ Check if function ``name`` is either a known function or has its own
423 printing method. Used to check if rewriting is possible."""
424 return name in self.known_functions or getattr(self, '_print_{}'.format(name), False)
426 def _print_Function(self, expr):
427 if expr.func.__name__ in self.known_functions:
428 cond_func = self.known_functions[expr.func.__name__]
429 if isinstance(cond_func, str):
430 return "%s(%s)" % (cond_func, self.stringify(expr.args, ", "))
431 else:
432 for cond, func in cond_func:
433 if cond(*expr.args):
434 break
435 if func is not None:
436 try:
437 return func(*[self.parenthesize(item, 0) for item in expr.args])
438 except TypeError:
439 return "%s(%s)" % (func, self.stringify(expr.args, ", "))
440 elif hasattr(expr, '_imp_') and isinstance(expr._imp_, Lambda):
441 # inlined function
442 return self._print(expr._imp_(*expr.args))
443 elif expr.func.__name__ in self._rewriteable_functions:
444 # Simple rewrite to supported function possible
445 target_f, required_fs = self._rewriteable_functions[expr.func.__name__]
446 if self._can_print(target_f) and all(self._can_print(f) for f in required_fs):
447 return self._print(expr.rewrite(target_f))
448 if expr.is_Function and self._settings.get('allow_unknown_functions', False):
449 return '%s(%s)' % (self._print(expr.func), ', '.join(map(self._print, expr.args)))
450 else:
451 return self._print_not_supported(expr)
453 _print_Expr = _print_Function
455 # Don't inherit the str-printer method for Heaviside to the code printers
456 _print_Heaviside = None
458 def _print_NumberSymbol(self, expr):
459 if self._settings.get("inline", False):
460 return self._print(Float(expr.evalf(self._settings["precision"])))
461 else:
462 # A Number symbol that is not implemented here or with _printmethod
463 # is registered and evaluated
464 self._number_symbols.add((expr,
465 Float(expr.evalf(self._settings["precision"]))))
466 return str(expr)
468 def _print_Catalan(self, expr):
469 return self._print_NumberSymbol(expr)
470 def _print_EulerGamma(self, expr):
471 return self._print_NumberSymbol(expr)
472 def _print_GoldenRatio(self, expr):
473 return self._print_NumberSymbol(expr)
474 def _print_TribonacciConstant(self, expr):
475 return self._print_NumberSymbol(expr)
476 def _print_Exp1(self, expr):
477 return self._print_NumberSymbol(expr)
478 def _print_Pi(self, expr):
479 return self._print_NumberSymbol(expr)
481 def _print_And(self, expr):
482 PREC = precedence(expr)
483 return (" %s " % self._operators['and']).join(self.parenthesize(a, PREC)
484 for a in sorted(expr.args, key=default_sort_key))
486 def _print_Or(self, expr):
487 PREC = precedence(expr)
488 return (" %s " % self._operators['or']).join(self.parenthesize(a, PREC)
489 for a in sorted(expr.args, key=default_sort_key))
491 def _print_Xor(self, expr):
492 if self._operators.get('xor') is None:
493 return self._print(expr.to_nnf())
494 PREC = precedence(expr)
495 return (" %s " % self._operators['xor']).join(self.parenthesize(a, PREC)
496 for a in expr.args)
498 def _print_Equivalent(self, expr):
499 if self._operators.get('equivalent') is None:
500 return self._print(expr.to_nnf())
501 PREC = precedence(expr)
502 return (" %s " % self._operators['equivalent']).join(self.parenthesize(a, PREC)
503 for a in expr.args)
505 def _print_Not(self, expr):
506 PREC = precedence(expr)
507 return self._operators['not'] + self.parenthesize(expr.args[0], PREC)
509 def _print_BooleanFunction(self, expr):
510 return self._print(expr.to_nnf())
512 def _print_Mul(self, expr):
514 prec = precedence(expr)
516 c, e = expr.as_coeff_Mul()
517 if c < 0:
518 expr = _keep_coeff(-c, e)
519 sign = "-"
520 else:
521 sign = ""
523 a = [] # items in the numerator
524 b = [] # items that are in the denominator (if any)
526 pow_paren = [] # Will collect all pow with more than one base element and exp = -1
528 if self.order not in ('old', 'none'):
529 args = expr.as_ordered_factors()
530 else:
531 # use make_args in case expr was something like -x -> x
532 args = Mul.make_args(expr)
534 # Gather args for numerator/denominator
535 for item in args:
536 if item.is_commutative and item.is_Pow and item.exp.is_Rational and item.exp.is_negative:
537 if item.exp != -1:
538 b.append(Pow(item.base, -item.exp, evaluate=False))
539 else:
540 if len(item.args[0].args) != 1 and isinstance(item.base, Mul): # To avoid situations like #14160
541 pow_paren.append(item)
542 b.append(Pow(item.base, -item.exp))
543 else:
544 a.append(item)
546 a = a or [S.One]
548 if len(a) == 1 and sign == "-":
549 # Unary minus does not have a SymPy class, and hence there's no
550 # precedence weight associated with it, Python's unary minus has
551 # an operator precedence between multiplication and exponentiation,
552 # so we use this to compute a weight.
553 a_str = [self.parenthesize(a[0], 0.5*(PRECEDENCE["Pow"]+PRECEDENCE["Mul"]))]
554 else:
555 a_str = [self.parenthesize(x, prec) for x in a]
556 b_str = [self.parenthesize(x, prec) for x in b]
558 # To parenthesize Pow with exp = -1 and having more than one Symbol
559 for item in pow_paren:
560 if item.base in b:
561 b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
563 if not b:
564 return sign + '*'.join(a_str)
565 elif len(b) == 1:
566 return sign + '*'.join(a_str) + "/" + b_str[0]
567 else:
568 return sign + '*'.join(a_str) + "/(%s)" % '*'.join(b_str)
570 def _print_not_supported(self, expr):
571 try:
572 self._not_supported.add(expr)
573 except TypeError:
574 # not hashable
575 pass
576 return self.emptyPrinter(expr)
578 # The following can not be simply translated into C or Fortran
579 _print_Basic = _print_not_supported
580 _print_ComplexInfinity = _print_not_supported
581 _print_Derivative = _print_not_supported
582 _print_ExprCondPair = _print_not_supported
583 _print_GeometryEntity = _print_not_supported
584 _print_Infinity = _print_not_supported
585 _print_Integral = _print_not_supported
586 _print_Interval = _print_not_supported
587 _print_AccumulationBounds = _print_not_supported
588 _print_Limit = _print_not_supported
589 _print_MatrixBase = _print_not_supported
590 _print_DeferredVector = _print_not_supported
591 _print_NaN = _print_not_supported
592 _print_NegativeInfinity = _print_not_supported
593 _print_Order = _print_not_supported
594 _print_RootOf = _print_not_supported
595 _print_RootsOf = _print_not_supported
596 _print_RootSum = _print_not_supported
597 _print_Uniform = _print_not_supported
598 _print_Unit = _print_not_supported
599 _print_Wild = _print_not_supported
600 _print_WildFunction = _print_not_supported
601 _print_Relational = _print_not_supported
604# Code printer functions. These are included in this file so that they can be
605# imported in the top-level __init__.py without importing the sympy.codegen
606# module.
608def ccode(expr, assign_to=None, standard='c99', **settings):
609 """Converts an expr to a string of c code
611 Parameters
612 ==========
614 expr : Expr
615 A SymPy expression to be converted.
616 assign_to : optional
617 When given, the argument is used as the name of the variable to which
618 the expression is assigned. Can be a string, ``Symbol``,
619 ``MatrixSymbol``, or ``Indexed`` type. This is helpful in case of
620 line-wrapping, or for expressions that generate multi-line statements.
621 standard : str, optional
622 String specifying the standard. If your compiler supports a more modern
623 standard you may set this to 'c99' to allow the printer to use more math
624 functions. [default='c89'].
625 precision : integer, optional
626 The precision for numbers such as pi [default=17].
627 user_functions : dict, optional
628 A dictionary where the keys are string representations of either
629 ``FunctionClass`` or ``UndefinedFunction`` instances and the values
630 are their desired C string representations. Alternatively, the
631 dictionary value can be a list of tuples i.e. [(argument_test,
632 cfunction_string)] or [(argument_test, cfunction_formater)]. See below
633 for examples.
634 dereference : iterable, optional
635 An iterable of symbols that should be dereferenced in the printed code
636 expression. These would be values passed by address to the function.
637 For example, if ``dereference=[a]``, the resulting code would print
638 ``(*a)`` instead of ``a``.
639 human : bool, optional
640 If True, the result is a single string that may contain some constant
641 declarations for the number symbols. If False, the same information is
642 returned in a tuple of (symbols_to_declare, not_supported_functions,
643 code_text). [default=True].
644 contract: bool, optional
645 If True, ``Indexed`` instances are assumed to obey tensor contraction
646 rules and the corresponding nested loops over indices are generated.
647 Setting contract=False will not generate loops, instead the user is
648 responsible to provide values for the indices in the code.
649 [default=True].
651 Examples
652 ========
654 >>> from sympy import ccode, symbols, Rational, sin, ceiling, Abs, Function
655 >>> x, tau = symbols("x, tau")
656 >>> expr = (2*tau)**Rational(7, 2)
657 >>> ccode(expr)
658 '8*M_SQRT2*pow(tau, 7.0/2.0)'
659 >>> ccode(expr, math_macros={})
660 '8*sqrt(2)*pow(tau, 7.0/2.0)'
661 >>> ccode(sin(x), assign_to="s")
662 's = sin(x);'
663 >>> from sympy.codegen.ast import real, float80
664 >>> ccode(expr, type_aliases={real: float80})
665 '8*M_SQRT2l*powl(tau, 7.0L/2.0L)'
667 Simple custom printing can be defined for certain types by passing a
668 dictionary of {"type" : "function"} to the ``user_functions`` kwarg.
669 Alternatively, the dictionary value can be a list of tuples i.e.
670 [(argument_test, cfunction_string)].
672 >>> custom_functions = {
673 ... "ceiling": "CEIL",
674 ... "Abs": [(lambda x: not x.is_integer, "fabs"),
675 ... (lambda x: x.is_integer, "ABS")],
676 ... "func": "f"
677 ... }
678 >>> func = Function('func')
679 >>> ccode(func(Abs(x) + ceiling(x)), standard='C89', user_functions=custom_functions)
680 'f(fabs(x) + CEIL(x))'
682 or if the C-function takes a subset of the original arguments:
684 >>> ccode(2**x + 3**x, standard='C99', user_functions={'Pow': [
685 ... (lambda b, e: b == 2, lambda b, e: 'exp2(%s)' % e),
686 ... (lambda b, e: b != 2, 'pow')]})
687 'exp2(x) + pow(3, x)'
689 ``Piecewise`` expressions are converted into conditionals. If an
690 ``assign_to`` variable is provided an if statement is created, otherwise
691 the ternary operator is used. Note that if the ``Piecewise`` lacks a
692 default term, represented by ``(expr, True)`` then an error will be thrown.
693 This is to prevent generating an expression that may not evaluate to
694 anything.
696 >>> from sympy import Piecewise
697 >>> expr = Piecewise((x + 1, x > 0), (x, True))
698 >>> print(ccode(expr, tau, standard='C89'))
699 if (x > 0) {
700 tau = x + 1;
701 }
702 else {
703 tau = x;
704 }
706 Support for loops is provided through ``Indexed`` types. With
707 ``contract=True`` these expressions will be turned into loops, whereas
708 ``contract=False`` will just print the assignment expression that should be
709 looped over:
711 >>> from sympy import Eq, IndexedBase, Idx
712 >>> len_y = 5
713 >>> y = IndexedBase('y', shape=(len_y,))
714 >>> t = IndexedBase('t', shape=(len_y,))
715 >>> Dy = IndexedBase('Dy', shape=(len_y-1,))
716 >>> i = Idx('i', len_y-1)
717 >>> e=Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
718 >>> ccode(e.rhs, assign_to=e.lhs, contract=False, standard='C89')
719 'Dy[i] = (y[i + 1] - y[i])/(t[i + 1] - t[i]);'
721 Matrices are also supported, but a ``MatrixSymbol`` of the same dimensions
722 must be provided to ``assign_to``. Note that any expression that can be
723 generated normally can also exist inside a Matrix:
725 >>> from sympy import Matrix, MatrixSymbol
726 >>> mat = Matrix([x**2, Piecewise((x + 1, x > 0), (x, True)), sin(x)])
727 >>> A = MatrixSymbol('A', 3, 1)
728 >>> print(ccode(mat, A, standard='C89'))
729 A[0] = pow(x, 2);
730 if (x > 0) {
731 A[1] = x + 1;
732 }
733 else {
734 A[1] = x;
735 }
736 A[2] = sin(x);
737 """
738 from sympy.printing.c import c_code_printers
739 return c_code_printers[standard.lower()](settings).doprint(expr, assign_to)
741def print_ccode(expr, **settings):
742 """Prints C representation of the given expression."""
743 print(ccode(expr, **settings))
745def fcode(expr, assign_to=None, **settings):
746 """Converts an expr to a string of fortran code
748 Parameters
749 ==========
751 expr : Expr
752 A SymPy expression to be converted.
753 assign_to : optional
754 When given, the argument is used as the name of the variable to which
755 the expression is assigned. Can be a string, ``Symbol``,
756 ``MatrixSymbol``, or ``Indexed`` type. This is helpful in case of
757 line-wrapping, or for expressions that generate multi-line statements.
758 precision : integer, optional
759 DEPRECATED. Use type_mappings instead. The precision for numbers such
760 as pi [default=17].
761 user_functions : dict, optional
762 A dictionary where keys are ``FunctionClass`` instances and values are
763 their string representations. Alternatively, the dictionary value can
764 be a list of tuples i.e. [(argument_test, cfunction_string)]. See below
765 for examples.
766 human : bool, optional
767 If True, the result is a single string that may contain some constant
768 declarations for the number symbols. If False, the same information is
769 returned in a tuple of (symbols_to_declare, not_supported_functions,
770 code_text). [default=True].
771 contract: bool, optional
772 If True, ``Indexed`` instances are assumed to obey tensor contraction
773 rules and the corresponding nested loops over indices are generated.
774 Setting contract=False will not generate loops, instead the user is
775 responsible to provide values for the indices in the code.
776 [default=True].
777 source_format : optional
778 The source format can be either 'fixed' or 'free'. [default='fixed']
779 standard : integer, optional
780 The Fortran standard to be followed. This is specified as an integer.
781 Acceptable standards are 66, 77, 90, 95, 2003, and 2008. Default is 77.
782 Note that currently the only distinction internally is between
783 standards before 95, and those 95 and after. This may change later as
784 more features are added.
785 name_mangling : bool, optional
786 If True, then the variables that would become identical in
787 case-insensitive Fortran are mangled by appending different number
788 of ``_`` at the end. If False, SymPy Will not interfere with naming of
789 variables. [default=True]
791 Examples
792 ========
794 >>> from sympy import fcode, symbols, Rational, sin, ceiling, floor
795 >>> x, tau = symbols("x, tau")
796 >>> fcode((2*tau)**Rational(7, 2))
797 ' 8*sqrt(2.0d0)*tau**(7.0d0/2.0d0)'
798 >>> fcode(sin(x), assign_to="s")
799 ' s = sin(x)'
801 Custom printing can be defined for certain types by passing a dictionary of
802 "type" : "function" to the ``user_functions`` kwarg. Alternatively, the
803 dictionary value can be a list of tuples i.e. [(argument_test,
804 cfunction_string)].
806 >>> custom_functions = {
807 ... "ceiling": "CEIL",
808 ... "floor": [(lambda x: not x.is_integer, "FLOOR1"),
809 ... (lambda x: x.is_integer, "FLOOR2")]
810 ... }
811 >>> fcode(floor(x) + ceiling(x), user_functions=custom_functions)
812 ' CEIL(x) + FLOOR1(x)'
814 ``Piecewise`` expressions are converted into conditionals. If an
815 ``assign_to`` variable is provided an if statement is created, otherwise
816 the ternary operator is used. Note that if the ``Piecewise`` lacks a
817 default term, represented by ``(expr, True)`` then an error will be thrown.
818 This is to prevent generating an expression that may not evaluate to
819 anything.
821 >>> from sympy import Piecewise
822 >>> expr = Piecewise((x + 1, x > 0), (x, True))
823 >>> print(fcode(expr, tau))
824 if (x > 0) then
825 tau = x + 1
826 else
827 tau = x
828 end if
830 Support for loops is provided through ``Indexed`` types. With
831 ``contract=True`` these expressions will be turned into loops, whereas
832 ``contract=False`` will just print the assignment expression that should be
833 looped over:
835 >>> from sympy import Eq, IndexedBase, Idx
836 >>> len_y = 5
837 >>> y = IndexedBase('y', shape=(len_y,))
838 >>> t = IndexedBase('t', shape=(len_y,))
839 >>> Dy = IndexedBase('Dy', shape=(len_y-1,))
840 >>> i = Idx('i', len_y-1)
841 >>> e=Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
842 >>> fcode(e.rhs, assign_to=e.lhs, contract=False)
843 ' Dy(i) = (y(i + 1) - y(i))/(t(i + 1) - t(i))'
845 Matrices are also supported, but a ``MatrixSymbol`` of the same dimensions
846 must be provided to ``assign_to``. Note that any expression that can be
847 generated normally can also exist inside a Matrix:
849 >>> from sympy import Matrix, MatrixSymbol
850 >>> mat = Matrix([x**2, Piecewise((x + 1, x > 0), (x, True)), sin(x)])
851 >>> A = MatrixSymbol('A', 3, 1)
852 >>> print(fcode(mat, A))
853 A(1, 1) = x**2
854 if (x > 0) then
855 A(2, 1) = x + 1
856 else
857 A(2, 1) = x
858 end if
859 A(3, 1) = sin(x)
860 """
861 from sympy.printing.fortran import FCodePrinter
862 return FCodePrinter(settings).doprint(expr, assign_to)
865def print_fcode(expr, **settings):
866 """Prints the Fortran representation of the given expression.
868 See fcode for the meaning of the optional arguments.
869 """
870 print(fcode(expr, **settings))
872def cxxcode(expr, assign_to=None, standard='c++11', **settings):
873 """ C++ equivalent of :func:`~.ccode`. """
874 from sympy.printing.cxx import cxx_code_printers
875 return cxx_code_printers[standard.lower()](settings).doprint(expr, assign_to)