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
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2Octave (and Matlab) code printer
4The `OctaveCodePrinter` converts SymPy expressions into Octave expressions.
5It uses a subset of the Octave language for Matlab compatibility.
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.
11"""
13from __future__ import annotations
14from typing import Any
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
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"]
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}
65class OctaveCodePrinter(CodePrinter):
66 """
67 A printer to convert expressions to strings of Octave/Matlab code.
68 """
69 printmethod = "_octave"
70 language = "Octave"
72 _operators = {
73 'and': '&',
74 'or': '|',
75 'not': '~',
76 }
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.
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)
101 def _rate_index_position(self, p):
102 return p*5
105 def _get_statement(self, codestring):
106 return "%s;" % codestring
109 def _get_comment(self, text):
110 return "% {}".format(text)
113 def _declare_number_const(self, name, value):
114 return "{} = {};".format(name, value)
117 def _format_code(self, lines):
118 return self.indent_code(lines)
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))
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
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)
145 # cribbed from str.py
146 prec = precedence(expr)
148 c, e = expr.as_coeff_Mul()
149 if c < 0:
150 expr = _keep_coeff(-c, e)
151 sign = "-"
152 else:
153 sign = ""
155 a = [] # items in the numerator
156 b = [] # items that are in the denominator (if any)
158 pow_paren = [] # Will collect all pow with more than one base element and exp = -1
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)
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)
184 a = a or [S.One]
186 a_str = [self.parenthesize(x, prec) for x in a]
187 b_str = [self.parenthesize(x, prec) for x in b]
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)]
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
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))
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)
219 def _print_Pow(self, expr):
220 powsymbol = '^' if all(x.is_number for x in expr.args) else '.^'
222 PREC = precedence(expr)
224 if equal_valued(expr.exp, 0.5):
225 return "sqrt(%s)" % self._print(expr.base)
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)
235 return '%s%s%s' % (self.parenthesize(expr.base, PREC), powsymbol,
236 self.parenthesize(expr.exp, PREC))
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))
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))
249 def _print_Pi(self, expr):
250 return 'pi'
253 def _print_ImaginaryUnit(self, expr):
254 return "1i"
257 def _print_Exp1(self, expr):
258 return "exp(1)"
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"
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))
296 def _print_Infinity(self, expr):
297 return 'inf'
300 def _print_NegativeInfinity(self, expr):
301 return '-inf'
304 def _print_NaN(self, expr):
305 return 'NaN'
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
315 def _print_BooleanTrue(self, expr):
316 return "true"
319 def _print_BooleanFalse(self, expr):
320 return "false"
323 def _print_bool(self, expr):
324 return str(expr).lower()
327 # Could generate quadrature code for definite Integrals?
328 #_print_Integral = _print_not_supported
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))
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)
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)
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]) + ')')
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))
386 def _print_Idx(self, expr):
387 return self._print(expr.label)
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)
395 def _print_HadamardProduct(self, expr):
396 return '.*'.join([self.parenthesize(arg, precedence(expr))
397 for arg in expr.args])
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 ])
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 + ")"
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]))
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]))
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)
429 def _print_hankel1(self, expr):
430 return "besselh(%s, 1, %s)" % (self._print(expr.order),
431 self._print(expr.argument))
434 def _print_hankel2(self, expr):
435 return "besselh(%s, 2, %s)" % (self._print(expr.order),
436 self._print(expr.argument))
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)
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)
454 def _print_airyai(self, expr):
455 return "airy(0, %s)" % self._print(expr.args[0])
458 def _print_airyaiprime(self, expr):
459 return "airy(1, %s)" % self._print(expr.args[0])
462 def _print_airybi(self, expr):
463 return "airy(2, %s)" % self._print(expr.args[0])
466 def _print_airybiprime(self, expr):
467 return "airy(3, %s)" % self._print(expr.args[0])
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)
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 )
485 _print_DiracDelta = _print_LambertW = _one_or_two_reversed_args
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 )
495 _print_Max = _print_Min = _nested_binary_math_func
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)
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)
543 def indent_code(self, code):
544 """Accepts a string of code or a list of code lines"""
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)
551 tab = " "
552 inc_regex = ('^function ', '^if ', '^elseif ', '^else$', '^for ')
553 dec_regex = ('^end$', '^elseif ', '^else$')
555 # pre-strip left-space from the code
556 code = [ line.lstrip(' \t') for line in code ]
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 ]
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
575def octave_code(expr, assign_to=None, **settings):
576 r"""Converts `expr` to a string of Octave (or Matlab) code.
578 The string uses a subset of the Octave language for Matlab compatibility.
580 Parameters
581 ==========
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].
612 Examples
613 ========
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'
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)'
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.
629 >>> octave_code(sin(pi*x*y), assign_to="s")
630 's = sin(pi*x.*y);'
632 If you need a matrix product "*" or matrix power "^", you can specify the
633 symbol as a ``MatrixSymbol``.
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'
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:
649 >>> octave_code(x**2*y*A**3)
650 '(x.^2.*y)*A^3'
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.
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)];'
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.
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));'
672 Note that any expression that can be generated normally can also exist
673 inside a Matrix:
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)];'
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.
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])'
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:
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)
714def print_octave_code(expr, **settings):
715 """Prints the Octave (or Matlab) representation of the given expression.
717 See `octave_code` for the meaning of the optional arguments.
718 """
719 print(octave_code(expr, **settings))