From 2cc4261ca86c5b0f78d5f26e370c0d38cec86cc9 Mon Sep 17 00:00:00 2001 From: Mark Skilbeck Date: Thu, 12 Sep 2019 10:41:03 -0700 Subject: [PATCH] Check for multiples of pi at op construction time Rather than trying to figure it out before printing. ffffflake --- CHANGELOG.md | 2 ++ pyquil/quilatom.py | 15 +++++++++------ pyquil/tests/test_quil.py | 11 +++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a91cd180d..9c8e1fb2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ Changelog immediate `float` value. Finally, the `CONVERT` instruction now accepts any valid memory reference designator (a `MemoryReference`, a string, or a tuple of type `(str, int)`) for both its arguments (@appleby, gh-1010). +- Fix the interaction between pretty-printing multiples of π and discarding of + parentheses that would lead to incorrect arithmetic expressions (@notmgsk, gh-993). [v2.11](https://github.com/rigetti/pyquil/compare/v2.10.0...v2.11.0) (September 3, 2019) ---------------------------------------------------------------------------------------- diff --git a/pyquil/quilatom.py b/pyquil/quilatom.py index 1d0072d10..ea9e5e95e 100644 --- a/pyquil/quilatom.py +++ b/pyquil/quilatom.py @@ -202,7 +202,7 @@ def format_parameter(element): if isinstance(element, integer_types) or isinstance(element, np.int_): return repr(element) elif isinstance(element, float): - return _check_for_pi(element) + return _expression_to_string(_check_for_pi(element)) elif isinstance(element, complex): out = '' r = element.real @@ -233,6 +233,8 @@ def format_parameter(element): return _expression_to_string(element) elif isinstance(element, MemoryReference): return element.out() + elif isinstance(element, str): + return element assert False, "Invalid parameter: %r" % element @@ -464,7 +466,8 @@ def fn(a, b): return a / b def __init__(self, op1, op2): - super(Div, self).__init__(op1, op2) + super(Div, self).__init__(_check_for_pi(op1) if isinstance(op1, float) else op1, + _check_for_pi(op2) if isinstance(op2, float) else op2) class Pow(BinaryExp): @@ -483,7 +486,7 @@ def __init__(self, op1, op2): def _expression_to_string(expression): """ Recursively converts an expression to a string taking into account precedence and associativity for placing - parenthesis + parentheses. :param Expression expression: expression involving parameters :return: string such as '%x*(%y-4)' @@ -549,11 +552,11 @@ def _check_for_pi(element): elif abs(num) == 1 and den == 1: return sign + "pi" elif abs(num) == 1: - return sign + "pi/" + repr(den) + return Div(sign + "pi", den) elif den == 1: - return repr(num) + "*pi" + return Mul(num, "pi") else: - return repr(num) + "*pi/" + repr(den) + return Mul(num, Div("pi", den)) else: return repr(element) diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py index bdb60a711..6de375ed5 100755 --- a/pyquil/tests/test_quil.py +++ b/pyquil/tests/test_quil.py @@ -1298,3 +1298,14 @@ def test_placeholders_preserves_modifiers(): a = address_qubits(p) assert a[0].modifiers == g.modifiers + + +def test_arithmetic_pretty_printing(): + import numpy as np + from pyquil.quilatom import _expression_to_string + pi = np.pi + theta = [2] + math = "3 * theta[0] / (2 * pi)" + exp = Program(f"RX({math}) 0")[0].params[0] + + assert eval(_expression_to_string(exp)) == eval(math)