Coverage for /usr/lib/python3/dist-packages/sympy/printing/octave.py: 25%

296 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2Octave (and Matlab) code printer 

3 

4The `OctaveCodePrinter` converts SymPy expressions into Octave expressions. 

5It uses a subset of the Octave language for Matlab compatibility. 

6 

7A complete code generator, which uses `octave_code` extensively, can be found 

8in `sympy.utilities.codegen`. The `codegen` module can be used to generate 

9complete source code files. 

10 

11""" 

12 

13from __future__ import annotations 

14from typing import Any 

15 

16from sympy.core import Mul, Pow, S, Rational 

17from sympy.core.mul import _keep_coeff 

18from sympy.core.numbers import equal_valued 

19from sympy.printing.codeprinter import CodePrinter 

20from sympy.printing.precedence import precedence, PRECEDENCE 

21from re import search 

22 

23# List of known functions. First, those that have the same name in 

24# SymPy and Octave. This is almost certainly incomplete! 

25known_fcns_src1 = ["sin", "cos", "tan", "cot", "sec", "csc", 

26 "asin", "acos", "acot", "atan", "atan2", "asec", "acsc", 

27 "sinh", "cosh", "tanh", "coth", "csch", "sech", 

28 "asinh", "acosh", "atanh", "acoth", "asech", "acsch", 

29 "erfc", "erfi", "erf", "erfinv", "erfcinv", 

30 "besseli", "besselj", "besselk", "bessely", 

31 "bernoulli", "beta", "euler", "exp", "factorial", "floor", 

32 "fresnelc", "fresnels", "gamma", "harmonic", "log", 

33 "polylog", "sign", "zeta", "legendre"] 

34 

35# These functions have different names ("SymPy": "Octave"), more 

36# generally a mapping to (argument_conditions, octave_function). 

37known_fcns_src2 = { 

38 "Abs": "abs", 

39 "arg": "angle", # arg/angle ok in Octave but only angle in Matlab 

40 "binomial": "bincoeff", 

41 "ceiling": "ceil", 

42 "chebyshevu": "chebyshevU", 

43 "chebyshevt": "chebyshevT", 

44 "Chi": "coshint", 

45 "Ci": "cosint", 

46 "conjugate": "conj", 

47 "DiracDelta": "dirac", 

48 "Heaviside": "heaviside", 

49 "im": "imag", 

50 "laguerre": "laguerreL", 

51 "LambertW": "lambertw", 

52 "li": "logint", 

53 "loggamma": "gammaln", 

54 "Max": "max", 

55 "Min": "min", 

56 "Mod": "mod", 

57 "polygamma": "psi", 

58 "re": "real", 

59 "RisingFactorial": "pochhammer", 

60 "Shi": "sinhint", 

61 "Si": "sinint", 

62} 

63 

64 

65class OctaveCodePrinter(CodePrinter): 

66 """ 

67 A printer to convert expressions to strings of Octave/Matlab code. 

68 """ 

69 printmethod = "_octave" 

70 language = "Octave" 

71 

72 _operators = { 

73 'and': '&', 

74 'or': '|', 

75 'not': '~', 

76 } 

77 

78 _default_settings: dict[str, Any] = { 

79 'order': None, 

80 'full_prec': 'auto', 

81 'precision': 17, 

82 'user_functions': {}, 

83 'human': True, 

84 'allow_unknown_functions': False, 

85 'contract': True, 

86 'inline': True, 

87 } 

88 # Note: contract is for expressing tensors as loops (if True), or just 

89 # assignment (if False). FIXME: this should be looked a more carefully 

90 # for Octave. 

91 

92 

93 def __init__(self, settings={}): 

94 super().__init__(settings) 

95 self.known_functions = dict(zip(known_fcns_src1, known_fcns_src1)) 

96 self.known_functions.update(dict(known_fcns_src2)) 

97 userfuncs = settings.get('user_functions', {}) 

98 self.known_functions.update(userfuncs) 

99 

100 

101 def _rate_index_position(self, p): 

102 return p*5 

103 

104 

105 def _get_statement(self, codestring): 

106 return "%s;" % codestring 

107 

108 

109 def _get_comment(self, text): 

110 return "% {}".format(text) 

111 

112 

113 def _declare_number_const(self, name, value): 

114 return "{} = {};".format(name, value) 

115 

116 

117 def _format_code(self, lines): 

118 return self.indent_code(lines) 

119 

120 

121 def _traverse_matrix_indices(self, mat): 

122 # Octave uses Fortran order (column-major) 

123 rows, cols = mat.shape 

124 return ((i, j) for j in range(cols) for i in range(rows)) 

125 

126 

127 def _get_loop_opening_ending(self, indices): 

128 open_lines = [] 

129 close_lines = [] 

130 for i in indices: 

131 # Octave arrays start at 1 and end at dimension 

132 var, start, stop = map(self._print, 

133 [i.label, i.lower + 1, i.upper + 1]) 

134 open_lines.append("for %s = %s:%s" % (var, start, stop)) 

135 close_lines.append("end") 

136 return open_lines, close_lines 

137 

138 

139 def _print_Mul(self, expr): 

140 # print complex numbers nicely in Octave 

141 if (expr.is_number and expr.is_imaginary and 

142 (S.ImaginaryUnit*expr).is_Integer): 

143 return "%si" % self._print(-S.ImaginaryUnit*expr) 

144 

145 # cribbed from str.py 

146 prec = precedence(expr) 

147 

148 c, e = expr.as_coeff_Mul() 

149 if c < 0: 

150 expr = _keep_coeff(-c, e) 

151 sign = "-" 

152 else: 

153 sign = "" 

154 

155 a = [] # items in the numerator 

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

157 

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

159 

160 if self.order not in ('old', 'none'): 

161 args = expr.as_ordered_factors() 

162 else: 

163 # use make_args in case expr was something like -x -> x 

164 args = Mul.make_args(expr) 

165 

166 # Gather args for numerator/denominator 

167 for item in args: 

168 if (item.is_commutative and item.is_Pow and item.exp.is_Rational 

169 and item.exp.is_negative): 

170 if item.exp != -1: 

171 b.append(Pow(item.base, -item.exp, evaluate=False)) 

172 else: 

173 if len(item.args[0].args) != 1 and isinstance(item.base, Mul): # To avoid situations like #14160 

174 pow_paren.append(item) 

175 b.append(Pow(item.base, -item.exp)) 

176 elif item.is_Rational and item is not S.Infinity: 

177 if item.p != 1: 

178 a.append(Rational(item.p)) 

179 if item.q != 1: 

180 b.append(Rational(item.q)) 

181 else: 

182 a.append(item) 

183 

184 a = a or [S.One] 

185 

186 a_str = [self.parenthesize(x, prec) for x in a] 

187 b_str = [self.parenthesize(x, prec) for x in b] 

188 

189 # To parenthesize Pow with exp = -1 and having more than one Symbol 

190 for item in pow_paren: 

191 if item.base in b: 

192 b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)] 

193 

194 # from here it differs from str.py to deal with "*" and ".*" 

195 def multjoin(a, a_str): 

196 # here we probably are assuming the constants will come first 

197 r = a_str[0] 

198 for i in range(1, len(a)): 

199 mulsym = '*' if a[i-1].is_number else '.*' 

200 r = r + mulsym + a_str[i] 

201 return r 

202 

203 if not b: 

204 return sign + multjoin(a, a_str) 

205 elif len(b) == 1: 

206 divsym = '/' if b[0].is_number else './' 

207 return sign + multjoin(a, a_str) + divsym + b_str[0] 

208 else: 

209 divsym = '/' if all(bi.is_number for bi in b) else './' 

210 return (sign + multjoin(a, a_str) + 

211 divsym + "(%s)" % multjoin(b, b_str)) 

212 

213 def _print_Relational(self, expr): 

214 lhs_code = self._print(expr.lhs) 

215 rhs_code = self._print(expr.rhs) 

216 op = expr.rel_op 

217 return "{} {} {}".format(lhs_code, op, rhs_code) 

218 

219 def _print_Pow(self, expr): 

220 powsymbol = '^' if all(x.is_number for x in expr.args) else '.^' 

221 

222 PREC = precedence(expr) 

223 

224 if equal_valued(expr.exp, 0.5): 

225 return "sqrt(%s)" % self._print(expr.base) 

226 

227 if expr.is_commutative: 

228 if equal_valued(expr.exp, -0.5): 

229 sym = '/' if expr.base.is_number else './' 

230 return "1" + sym + "sqrt(%s)" % self._print(expr.base) 

231 if equal_valued(expr.exp, -1): 

232 sym = '/' if expr.base.is_number else './' 

233 return "1" + sym + "%s" % self.parenthesize(expr.base, PREC) 

234 

235 return '%s%s%s' % (self.parenthesize(expr.base, PREC), powsymbol, 

236 self.parenthesize(expr.exp, PREC)) 

237 

238 

239 def _print_MatPow(self, expr): 

240 PREC = precedence(expr) 

241 return '%s^%s' % (self.parenthesize(expr.base, PREC), 

242 self.parenthesize(expr.exp, PREC)) 

243 

244 def _print_MatrixSolve(self, expr): 

245 PREC = precedence(expr) 

246 return "%s \\ %s" % (self.parenthesize(expr.matrix, PREC), 

247 self.parenthesize(expr.vector, PREC)) 

248 

249 def _print_Pi(self, expr): 

250 return 'pi' 

251 

252 

253 def _print_ImaginaryUnit(self, expr): 

254 return "1i" 

255 

256 

257 def _print_Exp1(self, expr): 

258 return "exp(1)" 

259 

260 

261 def _print_GoldenRatio(self, expr): 

262 # FIXME: how to do better, e.g., for octave_code(2*GoldenRatio)? 

263 #return self._print((1+sqrt(S(5)))/2) 

264 return "(1+sqrt(5))/2" 

265 

266 

267 def _print_Assignment(self, expr): 

268 from sympy.codegen.ast import Assignment 

269 from sympy.functions.elementary.piecewise import Piecewise 

270 from sympy.tensor.indexed import IndexedBase 

271 # Copied from codeprinter, but remove special MatrixSymbol treatment 

272 lhs = expr.lhs 

273 rhs = expr.rhs 

274 # We special case assignments that take multiple lines 

275 if not self._settings["inline"] and isinstance(expr.rhs, Piecewise): 

276 # Here we modify Piecewise so each expression is now 

277 # an Assignment, and then continue on the print. 

278 expressions = [] 

279 conditions = [] 

280 for (e, c) in rhs.args: 

281 expressions.append(Assignment(lhs, e)) 

282 conditions.append(c) 

283 temp = Piecewise(*zip(expressions, conditions)) 

284 return self._print(temp) 

285 if self._settings["contract"] and (lhs.has(IndexedBase) or 

286 rhs.has(IndexedBase)): 

287 # Here we check if there is looping to be done, and if so 

288 # print the required loops. 

289 return self._doprint_loops(rhs, lhs) 

290 else: 

291 lhs_code = self._print(lhs) 

292 rhs_code = self._print(rhs) 

293 return self._get_statement("%s = %s" % (lhs_code, rhs_code)) 

294 

295 

296 def _print_Infinity(self, expr): 

297 return 'inf' 

298 

299 

300 def _print_NegativeInfinity(self, expr): 

301 return '-inf' 

302 

303 

304 def _print_NaN(self, expr): 

305 return 'NaN' 

306 

307 

308 def _print_list(self, expr): 

309 return '{' + ', '.join(self._print(a) for a in expr) + '}' 

310 _print_tuple = _print_list 

311 _print_Tuple = _print_list 

312 _print_List = _print_list 

313 

314 

315 def _print_BooleanTrue(self, expr): 

316 return "true" 

317 

318 

319 def _print_BooleanFalse(self, expr): 

320 return "false" 

321 

322 

323 def _print_bool(self, expr): 

324 return str(expr).lower() 

325 

326 

327 # Could generate quadrature code for definite Integrals? 

328 #_print_Integral = _print_not_supported 

329 

330 

331 def _print_MatrixBase(self, A): 

332 # Handle zero dimensions: 

333 if (A.rows, A.cols) == (0, 0): 

334 return '[]' 

335 elif S.Zero in A.shape: 

336 return 'zeros(%s, %s)' % (A.rows, A.cols) 

337 elif (A.rows, A.cols) == (1, 1): 

338 # Octave does not distinguish between scalars and 1x1 matrices 

339 return self._print(A[0, 0]) 

340 return "[%s]" % "; ".join(" ".join([self._print(a) for a in A[r, :]]) 

341 for r in range(A.rows)) 

342 

343 

344 def _print_SparseRepMatrix(self, A): 

345 from sympy.matrices import Matrix 

346 L = A.col_list(); 

347 # make row vectors of the indices and entries 

348 I = Matrix([[k[0] + 1 for k in L]]) 

349 J = Matrix([[k[1] + 1 for k in L]]) 

350 AIJ = Matrix([[k[2] for k in L]]) 

351 return "sparse(%s, %s, %s, %s, %s)" % (self._print(I), self._print(J), 

352 self._print(AIJ), A.rows, A.cols) 

353 

354 

355 def _print_MatrixElement(self, expr): 

356 return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) \ 

357 + '(%s, %s)' % (expr.i + 1, expr.j + 1) 

358 

359 

360 def _print_MatrixSlice(self, expr): 

361 def strslice(x, lim): 

362 l = x[0] + 1 

363 h = x[1] 

364 step = x[2] 

365 lstr = self._print(l) 

366 hstr = 'end' if h == lim else self._print(h) 

367 if step == 1: 

368 if l == 1 and h == lim: 

369 return ':' 

370 if l == h: 

371 return lstr 

372 else: 

373 return lstr + ':' + hstr 

374 else: 

375 return ':'.join((lstr, self._print(step), hstr)) 

376 return (self._print(expr.parent) + '(' + 

377 strslice(expr.rowslice, expr.parent.shape[0]) + ', ' + 

378 strslice(expr.colslice, expr.parent.shape[1]) + ')') 

379 

380 

381 def _print_Indexed(self, expr): 

382 inds = [ self._print(i) for i in expr.indices ] 

383 return "%s(%s)" % (self._print(expr.base.label), ", ".join(inds)) 

384 

385 

386 def _print_Idx(self, expr): 

387 return self._print(expr.label) 

388 

389 

390 def _print_KroneckerDelta(self, expr): 

391 prec = PRECEDENCE["Pow"] 

392 return "double(%s == %s)" % tuple(self.parenthesize(x, prec) 

393 for x in expr.args) 

394 

395 def _print_HadamardProduct(self, expr): 

396 return '.*'.join([self.parenthesize(arg, precedence(expr)) 

397 for arg in expr.args]) 

398 

399 def _print_HadamardPower(self, expr): 

400 PREC = precedence(expr) 

401 return '.**'.join([ 

402 self.parenthesize(expr.base, PREC), 

403 self.parenthesize(expr.exp, PREC) 

404 ]) 

405 

406 def _print_Identity(self, expr): 

407 shape = expr.shape 

408 if len(shape) == 2 and shape[0] == shape[1]: 

409 shape = [shape[0]] 

410 s = ", ".join(self._print(n) for n in shape) 

411 return "eye(" + s + ")" 

412 

413 def _print_lowergamma(self, expr): 

414 # Octave implements regularized incomplete gamma function 

415 return "(gammainc({1}, {0}).*gamma({0}))".format( 

416 self._print(expr.args[0]), self._print(expr.args[1])) 

417 

418 

419 def _print_uppergamma(self, expr): 

420 return "(gammainc({1}, {0}, 'upper').*gamma({0}))".format( 

421 self._print(expr.args[0]), self._print(expr.args[1])) 

422 

423 

424 def _print_sinc(self, expr): 

425 #Note: Divide by pi because Octave implements normalized sinc function. 

426 return "sinc(%s)" % self._print(expr.args[0]/S.Pi) 

427 

428 

429 def _print_hankel1(self, expr): 

430 return "besselh(%s, 1, %s)" % (self._print(expr.order), 

431 self._print(expr.argument)) 

432 

433 

434 def _print_hankel2(self, expr): 

435 return "besselh(%s, 2, %s)" % (self._print(expr.order), 

436 self._print(expr.argument)) 

437 

438 

439 # Note: as of 2015, Octave doesn't have spherical Bessel functions 

440 def _print_jn(self, expr): 

441 from sympy.functions import sqrt, besselj 

442 x = expr.argument 

443 expr2 = sqrt(S.Pi/(2*x))*besselj(expr.order + S.Half, x) 

444 return self._print(expr2) 

445 

446 

447 def _print_yn(self, expr): 

448 from sympy.functions import sqrt, bessely 

449 x = expr.argument 

450 expr2 = sqrt(S.Pi/(2*x))*bessely(expr.order + S.Half, x) 

451 return self._print(expr2) 

452 

453 

454 def _print_airyai(self, expr): 

455 return "airy(0, %s)" % self._print(expr.args[0]) 

456 

457 

458 def _print_airyaiprime(self, expr): 

459 return "airy(1, %s)" % self._print(expr.args[0]) 

460 

461 

462 def _print_airybi(self, expr): 

463 return "airy(2, %s)" % self._print(expr.args[0]) 

464 

465 

466 def _print_airybiprime(self, expr): 

467 return "airy(3, %s)" % self._print(expr.args[0]) 

468 

469 

470 def _print_expint(self, expr): 

471 mu, x = expr.args 

472 if mu != 1: 

473 return self._print_not_supported(expr) 

474 return "expint(%s)" % self._print(x) 

475 

476 

477 def _one_or_two_reversed_args(self, expr): 

478 assert len(expr.args) <= 2 

479 return '{name}({args})'.format( 

480 name=self.known_functions[expr.__class__.__name__], 

481 args=", ".join([self._print(x) for x in reversed(expr.args)]) 

482 ) 

483 

484 

485 _print_DiracDelta = _print_LambertW = _one_or_two_reversed_args 

486 

487 

488 def _nested_binary_math_func(self, expr): 

489 return '{name}({arg1}, {arg2})'.format( 

490 name=self.known_functions[expr.__class__.__name__], 

491 arg1=self._print(expr.args[0]), 

492 arg2=self._print(expr.func(*expr.args[1:])) 

493 ) 

494 

495 _print_Max = _print_Min = _nested_binary_math_func 

496 

497 

498 def _print_Piecewise(self, expr): 

499 if expr.args[-1].cond != True: 

500 # We need the last conditional to be a True, otherwise the resulting 

501 # function may not return a result. 

502 raise ValueError("All Piecewise expressions must contain an " 

503 "(expr, True) statement to be used as a default " 

504 "condition. Without one, the generated " 

505 "expression may not evaluate to anything under " 

506 "some condition.") 

507 lines = [] 

508 if self._settings["inline"]: 

509 # Express each (cond, expr) pair in a nested Horner form: 

510 # (condition) .* (expr) + (not cond) .* (<others>) 

511 # Expressions that result in multiple statements won't work here. 

512 ecpairs = ["({0}).*({1}) + (~({0})).*(".format 

513 (self._print(c), self._print(e)) 

514 for e, c in expr.args[:-1]] 

515 elast = "%s" % self._print(expr.args[-1].expr) 

516 pw = " ...\n".join(ecpairs) + elast + ")"*len(ecpairs) 

517 # Note: current need these outer brackets for 2*pw. Would be 

518 # nicer to teach parenthesize() to do this for us when needed! 

519 return "(" + pw + ")" 

520 else: 

521 for i, (e, c) in enumerate(expr.args): 

522 if i == 0: 

523 lines.append("if (%s)" % self._print(c)) 

524 elif i == len(expr.args) - 1 and c == True: 

525 lines.append("else") 

526 else: 

527 lines.append("elseif (%s)" % self._print(c)) 

528 code0 = self._print(e) 

529 lines.append(code0) 

530 if i == len(expr.args) - 1: 

531 lines.append("end") 

532 return "\n".join(lines) 

533 

534 

535 def _print_zeta(self, expr): 

536 if len(expr.args) == 1: 

537 return "zeta(%s)" % self._print(expr.args[0]) 

538 else: 

539 # Matlab two argument zeta is not equivalent to SymPy's 

540 return self._print_not_supported(expr) 

541 

542 

543 def indent_code(self, code): 

544 """Accepts a string of code or a list of code lines""" 

545 

546 # code mostly copied from ccode 

547 if isinstance(code, str): 

548 code_lines = self.indent_code(code.splitlines(True)) 

549 return ''.join(code_lines) 

550 

551 tab = " " 

552 inc_regex = ('^function ', '^if ', '^elseif ', '^else$', '^for ') 

553 dec_regex = ('^end$', '^elseif ', '^else$') 

554 

555 # pre-strip left-space from the code 

556 code = [ line.lstrip(' \t') for line in code ] 

557 

558 increase = [ int(any(search(re, line) for re in inc_regex)) 

559 for line in code ] 

560 decrease = [ int(any(search(re, line) for re in dec_regex)) 

561 for line in code ] 

562 

563 pretty = [] 

564 level = 0 

565 for n, line in enumerate(code): 

566 if line in ('', '\n'): 

567 pretty.append(line) 

568 continue 

569 level -= decrease[n] 

570 pretty.append("%s%s" % (tab*level, line)) 

571 level += increase[n] 

572 return pretty 

573 

574 

575def octave_code(expr, assign_to=None, **settings): 

576 r"""Converts `expr` to a string of Octave (or Matlab) code. 

577 

578 The string uses a subset of the Octave language for Matlab compatibility. 

579 

580 Parameters 

581 ========== 

582 

583 expr : Expr 

584 A SymPy expression to be converted. 

585 assign_to : optional 

586 When given, the argument is used as the name of the variable to which 

587 the expression is assigned. Can be a string, ``Symbol``, 

588 ``MatrixSymbol``, or ``Indexed`` type. This can be helpful for 

589 expressions that generate multi-line statements. 

590 precision : integer, optional 

591 The precision for numbers such as pi [default=16]. 

592 user_functions : dict, optional 

593 A dictionary where keys are ``FunctionClass`` instances and values are 

594 their string representations. Alternatively, the dictionary value can 

595 be a list of tuples i.e. [(argument_test, cfunction_string)]. See 

596 below for examples. 

597 human : bool, optional 

598 If True, the result is a single string that may contain some constant 

599 declarations for the number symbols. If False, the same information is 

600 returned in a tuple of (symbols_to_declare, not_supported_functions, 

601 code_text). [default=True]. 

602 contract: bool, optional 

603 If True, ``Indexed`` instances are assumed to obey tensor contraction 

604 rules and the corresponding nested loops over indices are generated. 

605 Setting contract=False will not generate loops, instead the user is 

606 responsible to provide values for the indices in the code. 

607 [default=True]. 

608 inline: bool, optional 

609 If True, we try to create single-statement code instead of multiple 

610 statements. [default=True]. 

611 

612 Examples 

613 ======== 

614 

615 >>> from sympy import octave_code, symbols, sin, pi 

616 >>> x = symbols('x') 

617 >>> octave_code(sin(x).series(x).removeO()) 

618 'x.^5/120 - x.^3/6 + x' 

619 

620 >>> from sympy import Rational, ceiling 

621 >>> x, y, tau = symbols("x, y, tau") 

622 >>> octave_code((2*tau)**Rational(7, 2)) 

623 '8*sqrt(2)*tau.^(7/2)' 

624 

625 Note that element-wise (Hadamard) operations are used by default between 

626 symbols. This is because its very common in Octave to write "vectorized" 

627 code. It is harmless if the values are scalars. 

628 

629 >>> octave_code(sin(pi*x*y), assign_to="s") 

630 's = sin(pi*x.*y);' 

631 

632 If you need a matrix product "*" or matrix power "^", you can specify the 

633 symbol as a ``MatrixSymbol``. 

634 

635 >>> from sympy import Symbol, MatrixSymbol 

636 >>> n = Symbol('n', integer=True, positive=True) 

637 >>> A = MatrixSymbol('A', n, n) 

638 >>> octave_code(3*pi*A**3) 

639 '(3*pi)*A^3' 

640 

641 This class uses several rules to decide which symbol to use a product. 

642 Pure numbers use "*", Symbols use ".*" and MatrixSymbols use "*". 

643 A HadamardProduct can be used to specify componentwise multiplication ".*" 

644 of two MatrixSymbols. There is currently there is no easy way to specify 

645 scalar symbols, so sometimes the code might have some minor cosmetic 

646 issues. For example, suppose x and y are scalars and A is a Matrix, then 

647 while a human programmer might write "(x^2*y)*A^3", we generate: 

648 

649 >>> octave_code(x**2*y*A**3) 

650 '(x.^2.*y)*A^3' 

651 

652 Matrices are supported using Octave inline notation. When using 

653 ``assign_to`` with matrices, the name can be specified either as a string 

654 or as a ``MatrixSymbol``. The dimensions must align in the latter case. 

655 

656 >>> from sympy import Matrix, MatrixSymbol 

657 >>> mat = Matrix([[x**2, sin(x), ceiling(x)]]) 

658 >>> octave_code(mat, assign_to='A') 

659 'A = [x.^2 sin(x) ceil(x)];' 

660 

661 ``Piecewise`` expressions are implemented with logical masking by default. 

662 Alternatively, you can pass "inline=False" to use if-else conditionals. 

663 Note that if the ``Piecewise`` lacks a default term, represented by 

664 ``(expr, True)`` then an error will be thrown. This is to prevent 

665 generating an expression that may not evaluate to anything. 

666 

667 >>> from sympy import Piecewise 

668 >>> pw = Piecewise((x + 1, x > 0), (x, True)) 

669 >>> octave_code(pw, assign_to=tau) 

670 'tau = ((x > 0).*(x + 1) + (~(x > 0)).*(x));' 

671 

672 Note that any expression that can be generated normally can also exist 

673 inside a Matrix: 

674 

675 >>> mat = Matrix([[x**2, pw, sin(x)]]) 

676 >>> octave_code(mat, assign_to='A') 

677 'A = [x.^2 ((x > 0).*(x + 1) + (~(x > 0)).*(x)) sin(x)];' 

678 

679 Custom printing can be defined for certain types by passing a dictionary of 

680 "type" : "function" to the ``user_functions`` kwarg. Alternatively, the 

681 dictionary value can be a list of tuples i.e., [(argument_test, 

682 cfunction_string)]. This can be used to call a custom Octave function. 

683 

684 >>> from sympy import Function 

685 >>> f = Function('f') 

686 >>> g = Function('g') 

687 >>> custom_functions = { 

688 ... "f": "existing_octave_fcn", 

689 ... "g": [(lambda x: x.is_Matrix, "my_mat_fcn"), 

690 ... (lambda x: not x.is_Matrix, "my_fcn")] 

691 ... } 

692 >>> mat = Matrix([[1, x]]) 

693 >>> octave_code(f(x) + g(x) + g(mat), user_functions=custom_functions) 

694 'existing_octave_fcn(x) + my_fcn(x) + my_mat_fcn([1 x])' 

695 

696 Support for loops is provided through ``Indexed`` types. With 

697 ``contract=True`` these expressions will be turned into loops, whereas 

698 ``contract=False`` will just print the assignment expression that should be 

699 looped over: 

700 

701 >>> from sympy import Eq, IndexedBase, Idx 

702 >>> len_y = 5 

703 >>> y = IndexedBase('y', shape=(len_y,)) 

704 >>> t = IndexedBase('t', shape=(len_y,)) 

705 >>> Dy = IndexedBase('Dy', shape=(len_y-1,)) 

706 >>> i = Idx('i', len_y-1) 

707 >>> e = Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i])) 

708 >>> octave_code(e.rhs, assign_to=e.lhs, contract=False) 

709 'Dy(i) = (y(i + 1) - y(i))./(t(i + 1) - t(i));' 

710 """ 

711 return OctaveCodePrinter(settings).doprint(expr, assign_to) 

712 

713 

714def print_octave_code(expr, **settings): 

715 """Prints the Octave (or Matlab) representation of the given expression. 

716 

717 See `octave_code` for the meaning of the optional arguments. 

718 """ 

719 print(octave_code(expr, **settings))