Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion qupulse/expressions/sympy.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ def _try_to_numeric(self) -> Optional[numbers.Number]:
@property
def original_expression(self) -> Union[str, Number]:
if self._original_expression is None:
return str(self._sympified_expression)
return get_most_simple_representation(self._sympified_expression)
else:
return self._original_expression

Expand Down
31 changes: 23 additions & 8 deletions qupulse/utils/sympy.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,30 @@ def sympify(expr: Union[str, Number, sympy.Expr, numpy.str_], **kwargs) -> sympy
raise


class _LosslessFloatPrinter(sympy.StrPrinter):
def _print_Float(self, f):
# Keep normal formatting unless it would break the round-trip
# for this we check using sympy.Float instead of sympy.sympify for performance reasons
normal_repr = super()._print_Float(f)
if sympy.Float(normal_repr) == f:
return normal_repr
else:
return sympy.srepr(f)


def get_most_simple_representation(expression: sympy.Expr) -> Union[str, int, float]:
if expression.free_symbols:
return str(expression)
elif expression.is_Integer:
return int(expression)
elif expression.is_Float:
return float(expression)
else:
return str(expression)
str_repr = _LosslessFloatPrinter().doprint(expression)

# try if we have valid python literals
try:
return int(str_repr)
except ValueError:
pass
try:
return float(str_repr)
except ValueError:
pass
return str_repr


def get_free_symbols(expression: sympy.Expr) -> Sequence[sympy.Symbol]:
Expand Down
6 changes: 6 additions & 0 deletions tests/expressions/expression_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ def test_pickle(self):
loaded = pickle.loads(dumped)
self.assertEqual(expr, loaded)

def test_roundtrip(self):
original = ExpressionScalar("a * 16") / ExpressionScalar(12 / 5) * ExpressionScalar(4)
serialized = original.get_serialization_data()
deserialized = ExpressionScalar(serialized)
self.assertEqual(original, deserialized)


class ExpressionExceptionTests(unittest.TestCase):
def test_expression_variable_missing(self):
Expand Down
Loading