Skip to content

Commit 4bbffd0

Browse files
authored
Fix PetabStrPrinter for non-integer rational exponents (#489)
A non-integer Rational exponent (e.g. a square root, exponent 1/2) is a sympy Atom but prints as the multi-token "1/2", so PetabStrPrinter emitted `sqrt(a)` as the unparenthesized `a ^ 1/2`. Since `^` binds tighter than `/`, that re-parses as `(a^1)/2 = a/2` -- a silent round-trip corruption (`petab_math_str(sympify_petab(...))` is not the identity for square roots). The `not exp.is_Atom` guard added in #421 covers non-atomic exponents but not this atomic-yet-multi-token case; parenthesize a non-integer rational exponent explicitly, so `petab_math_str(sqrt(a)) == "a ^ (1/2)"`, which re-parses correctly. Integer powers and the #421 cases are unchanged.
1 parent 17a70e8 commit 4bbffd0

2 files changed

Lines changed: 8 additions & 1 deletion

File tree

petab/v1/math/printer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ def _print_Pow(self, expr: sp.Pow):
4141
str_exp = self._print(exp)
4242
if not base.is_Atom:
4343
str_base = f"({str_base})"
44-
if not exp.is_Atom:
44+
# A non-integer Rational exponent (e.g. sqrt -> 1/2) is an Atom but
45+
# prints as the multi-token "1/2", so without parentheses "x ^ 1/2"
46+
# re-parses as (x^1)/2. Parenthesize it explicitly.
47+
if not exp.is_Atom or (exp.is_Rational and not exp.is_Integer):
4548
str_exp = f"({str_exp})"
4649
return f"{str_base} ^ {str_exp}"
4750

tests/v1/math/test_math.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ def test_printer():
4343
assert petab_math_str(BooleanTrue()) == "true"
4444
assert petab_math_str(BooleanFalse()) == "false"
4545
assert petab_math_str((a + b) ** (c + d)) == "(a + b) ^ (c + d)"
46+
# A non-integer rational exponent must be parenthesized, else "a ^ 1/2"
47+
# re-parses as (a^1)/2 (i.e. sqrt(a) would round-trip to a/2).
48+
assert petab_math_str(sp.sqrt(a)) == "a ^ (1/2)"
49+
assert petab_math_str(a ** sp.Rational(2, 3)) == "a ^ (2/3)"
4650

4751

4852
def read_cases():

0 commit comments

Comments
 (0)