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

1from __future__ import annotations 

2from typing import Any 

3 

4from functools import wraps 

5 

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 

16 

17 

18class requires: 

19 """ Decorator for registering requirements on print methods. """ 

20 def __init__(self, **kwargs): 

21 self._req = kwargs 

22 

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) 

29 

30 

31class AssignmentError(Exception): 

32 """ 

33 Raised if an assignment variable for a loop is missing. 

34 """ 

35 pass 

36 

37 

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 

46 

47 

48class CodePrinter(StrPrinter): 

49 """ 

50 The base class for code-printing subclasses. 

51 """ 

52 

53 _operators = { 

54 'and': '&&', 

55 'or': '||', 

56 'not': '!', 

57 } 

58 

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 } 

68 

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 } 

108 

109 def __init__(self, settings=None): 

110 

111 super().__init__(settings=settings) 

112 if not hasattr(self, 'reserved_words'): 

113 self.reserved_words = set() 

114 

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)) 

118 

119 def doprint(self, expr, assign_to=None): 

120 """ 

121 Print the expression as code. 

122 

123 Parameters 

124 ---------- 

125 expr : Expression 

126 The expression to be printed. 

127 

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 

134 

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) 

151 

152 expr = _convert_python_lists(expr) 

153 expr = _handle_assign_to(expr, assign_to) 

154 

155 # Remove re(...) nodes due to UnevaluatedExpr.is_real always is None: 

156 expr = self._handle_UnevaluatedExpr(expr) 

157 

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() 

162 

163 lines = self._print(expr).splitlines() 

164 

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 

185 

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. 

191 

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) 

202 

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) 

209 

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) 

219 

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) 

226 

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: 

236 

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") 

254 

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) 

262 

263 return "\n".join(lines) 

264 

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) 

269 

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) 

276 

277 return self._sort_optimized(rinds, assign_to) 

278 

279 def _sort_optimized(self, indices, expr): 

280 

281 from sympy.tensor.indexed import Indexed 

282 

283 if not indices: 

284 return [] 

285 

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 

291 

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 

299 

300 return sorted(indices, key=lambda x: score_table[x]) 

301 

302 def _rate_index_position(self, p): 

303 """function to calculate score based on position among indices 

304 

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.") 

310 

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.") 

315 

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.") 

320 

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.") 

325 

326 def _format_code(self, lines): 

327 """Take in a list of lines of code, and format them accordingly. 

328 

329 This may include indenting, wrapping long lines, etc...""" 

330 raise NotImplementedError("This function must be implemented by " 

331 "subclass of CodePrinter.") 

332 

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.") 

338 

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) 

344 

345 def _print_CodeBlock(self, expr): 

346 return '\n'.join([self._print(i) for i in expr.args]) 

347 

348 def _print_String(self, string): 

349 return str(string) 

350 

351 def _print_QuotedString(self, arg): 

352 return '"%s"' % arg.text 

353 

354 def _print_Comment(self, string): 

355 return self._get_comment(str(string)) 

356 

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)) 

393 

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]))) 

399 

400 def _print_FunctionCall(self, expr): 

401 return '%s(%s)' % ( 

402 expr.name, 

403 ', '.join((self._print(arg) for arg in expr.function_args))) 

404 

405 def _print_Variable(self, expr): 

406 return self._print(expr.symbol) 

407 

408 def _print_Symbol(self, expr): 

409 

410 name = super()._print_Symbol(expr) 

411 

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 

420 

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) 

425 

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) 

452 

453 _print_Expr = _print_Function 

454 

455 # Don't inherit the str-printer method for Heaviside to the code printers 

456 _print_Heaviside = None 

457 

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) 

467 

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) 

480 

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)) 

485 

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)) 

490 

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) 

497 

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) 

504 

505 def _print_Not(self, expr): 

506 PREC = precedence(expr) 

507 return self._operators['not'] + self.parenthesize(expr.args[0], PREC) 

508 

509 def _print_BooleanFunction(self, expr): 

510 return self._print(expr.to_nnf()) 

511 

512 def _print_Mul(self, expr): 

513 

514 prec = precedence(expr) 

515 

516 c, e = expr.as_coeff_Mul() 

517 if c < 0: 

518 expr = _keep_coeff(-c, e) 

519 sign = "-" 

520 else: 

521 sign = "" 

522 

523 a = [] # items in the numerator 

524 b = [] # items that are in the denominator (if any) 

525 

526 pow_paren = [] # Will collect all pow with more than one base element and exp = -1 

527 

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) 

533 

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) 

545 

546 a = a or [S.One] 

547 

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] 

557 

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)] 

562 

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) 

569 

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) 

577 

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 

602 

603 

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. 

607 

608def ccode(expr, assign_to=None, standard='c99', **settings): 

609 """Converts an expr to a string of c code 

610 

611 Parameters 

612 ========== 

613 

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]. 

650 

651 Examples 

652 ======== 

653 

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)' 

666 

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)]. 

671 

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))' 

681 

682 or if the C-function takes a subset of the original arguments: 

683 

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)' 

688 

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. 

695 

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 } 

705 

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: 

710 

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]);' 

720 

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: 

724 

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) 

740 

741def print_ccode(expr, **settings): 

742 """Prints C representation of the given expression.""" 

743 print(ccode(expr, **settings)) 

744 

745def fcode(expr, assign_to=None, **settings): 

746 """Converts an expr to a string of fortran code 

747 

748 Parameters 

749 ========== 

750 

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] 

790 

791 Examples 

792 ======== 

793 

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)' 

800 

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)]. 

805 

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)' 

813 

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. 

820 

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 

829 

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: 

834 

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))' 

844 

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: 

848 

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) 

863 

864 

865def print_fcode(expr, **settings): 

866 """Prints the Fortran representation of the given expression. 

867 

868 See fcode for the meaning of the optional arguments. 

869 """ 

870 print(fcode(expr, **settings)) 

871 

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)