From 8b0f5e2fd8a413d1c5640a56b36f828281cfabe4 Mon Sep 17 00:00:00 2001 From: Mike Appleby <86076+appleby@users.noreply.github.com> Date: Mon, 16 Sep 2019 17:05:36 -0700 Subject: [PATCH] Fix operand types for STORE and classical comparison operators - Allow immediate floats as third arg in prepare_ternary_operands - Allow immediate values as source arg for STORE and ClassicalStore - Allow CONVERT to take any valid memory reference designator, i.e. a MemoryReference, a string, or a tuple of (str, int). Fixes #815 --- CHANGELOG.md | 13 ++++++-- pyquil/gates.py | 9 +++-- pyquil/quilbase.py | 5 +-- pyquil/tests/test_parser.py | 21 ++++++++++++ pyquil/tests/test_quil.py | 66 ++++++++++++++++++++++++++++++++----- 5 files changed, 98 insertions(+), 16 deletions(-) mode change 100644 => 100755 pyquil/tests/test_quil.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ab61660d3..1db1ed124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,9 +35,16 @@ Changelog ### Bugfixes -- Strength two symmetrization was not correctly producing orthogonal - arrays due to erroneous truncation, which has been fixed - (@kylegulshen, gh-990). +- Strength two symmetrization was not correctly producing orthogonal arrays due to erroneous + truncation, which has been fixed (@kylegulshen, gh-990). +- The `STORE` instruction now accepts `int` or `float` in addition to `MemoryReference` as it's + `source` argument. As a result, you can now `STORE` an immediate value into a memory register + (@appleby, TBD). +- The `EQ`, `LT`, `LE`, `GT`, and `GE` instructions now all accept `float` in addition to `int` or + `MemoryReference` as their third and final argument. As a result, you can now perform classical + comparisons against an immediate `float` value (@appleby, TBD). +- The `CONVERT` instruction now accepts any valid memory reference designator for both it's + arguments, i.e. a `MemoryReference`, a string, or a tuple of type `(str, int)`. [v2.11](https://github.com/rigetti/pyquil/compare/v2.10.0...v2.11.0) (September 3, 2019) ---------------------------------------------------------------------------------------- diff --git a/pyquil/gates.py b/pyquil/gates.py index c6e87cbcd..fad123a48 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -58,7 +58,7 @@ def prepare_ternary_operands(classical_reg1, classical_reg2, classical_reg3): if isinstance(classical_reg2, int): raise TypeError("Left operand of comparison must be a memory address") classical_reg2 = unpack_classical_reg(classical_reg2) - if not isinstance(classical_reg3, int): + if not isinstance(classical_reg3, int) and not isinstance(classical_reg3, float): classical_reg3 = unpack_classical_reg(classical_reg3) return classical_reg1, classical_reg2, classical_reg3 @@ -639,6 +639,8 @@ def STORE(region_name, offset_reg, source): :param source: Source data. Can be either a MemoryReference or a constant. :return: A ClassicalStore instance. """ + if not isinstance(source, int) and not isinstance(source, float): + source = unpack_classical_reg(source) return ClassicalStore(region_name, unpack_classical_reg(offset_reg), source) @@ -648,9 +650,10 @@ def CONVERT(classical_reg1, classical_reg2): :param classical_reg1: MemoryReference to store to. :param classical_reg2: MemoryReference to read from. - :return: A ClassicalCONVERT instance. + :return: A ClassicalConvert instance. """ - return ClassicalConvert(classical_reg1, classical_reg2) + return ClassicalConvert(unpack_classical_reg(classical_reg1), + unpack_classical_reg(classical_reg2)) def ADD(classical_reg, right): diff --git a/pyquil/quilbase.py b/pyquil/quilbase.py index 617284b61..b81c487aa 100644 --- a/pyquil/quilbase.py +++ b/pyquil/quilbase.py @@ -736,8 +736,9 @@ class ClassicalStore(AbstractInstruction): def __init__(self, target, left, right): if not isinstance(left, MemoryReference): raise TypeError("left operand should be an MemoryReference") - if not isinstance(right, MemoryReference): - raise TypeError("right operand should be an MemoryReference") + if not (isinstance(right, MemoryReference) or isinstance(right, int) + or isinstance(right, float)): + raise TypeError("right operand should be an MemoryReference or an int or float.") self.target = target self.left = left self.right = right diff --git a/pyquil/tests/test_parser.py b/pyquil/tests/test_parser.py index 22059066e..c6ef239a3 100644 --- a/pyquil/tests/test_parser.py +++ b/pyquil/tests/test_parser.py @@ -202,6 +202,7 @@ def test_memory_commands(): parse_equals("DECLARE mem OCTET[32] SHARING mem2 OFFSET 16 REAL OFFSET 32 REAL", Declare("mem", "OCTET", 32, shared_region="mem2", offsets=[(16, "REAL"), (32, "REAL")])) parse_equals("STORE mem ro[2] ro[0]", STORE("mem", MemoryReference("ro", 2), MemoryReference("ro", 0))) + parse_equals("STORE mem ro[2] 7", STORE("mem", MemoryReference("ro", 2), 7)) parse_equals("LOAD ro[8] mem mem[4]", LOAD(MemoryReference("ro", 8), "mem", MemoryReference("mem", 4))) parse_equals("CONVERT ro[1] ro[2]", CONVERT(MemoryReference("ro", 1), MemoryReference("ro", 2))) parse_equals("EXCHANGE ro[0] ro[1]", EXCHANGE(MemoryReference("ro", 0), MemoryReference("ro", 1))) @@ -236,6 +237,26 @@ def test_classical(): GT(MemoryReference("comp", 1), MemoryReference("ro", 3), MemoryReference("ro", 2))) parse_equals("GE comp[1] ro[3] ro[2]", GE(MemoryReference("comp", 1), MemoryReference("ro", 3), MemoryReference("ro", 2))) + parse_equals("EQ comp[1] ro[3] 0", + EQ(MemoryReference("comp", 1), MemoryReference("ro", 3), 0)) + parse_equals("LT comp[1] ro[3] 1", + LT(MemoryReference("comp", 1), MemoryReference("ro", 3), 1)) + parse_equals("LE comp[1] ro[3] 2", + LE(MemoryReference("comp", 1), MemoryReference("ro", 3), 2)) + parse_equals("GT comp[1] ro[3] 3", + GT(MemoryReference("comp", 1), MemoryReference("ro", 3), 3)) + parse_equals("GE comp[1] ro[3] 4", + GE(MemoryReference("comp", 1), MemoryReference("ro", 3), 4)) + parse_equals("EQ comp[1] ro[3] 0.0", + EQ(MemoryReference("comp", 1), MemoryReference("ro", 3), 0.0)) + parse_equals("LT comp[1] ro[3] 1.1", + LT(MemoryReference("comp", 1), MemoryReference("ro", 3), 1.1)) + parse_equals("LE comp[1] ro[3] 2.2", + LE(MemoryReference("comp", 1), MemoryReference("ro", 3), 2.2)) + parse_equals("GT comp[1] ro[3] 3.3", + GT(MemoryReference("comp", 1), MemoryReference("ro", 3), 3.3)) + parse_equals("GE comp[1] ro[3] 4.4", + GE(MemoryReference("comp", 1), MemoryReference("ro", 3), 4.4)) def test_pragma(): diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py old mode 100644 new mode 100755 index cfb79be25..bdb60a711 --- a/pyquil/tests/test_quil.py +++ b/pyquil/tests/test_quil.py @@ -247,22 +247,72 @@ def test_binary_classicals(): 'EXCHANGE ro[0] ro[1]\n' +def test_memory_reference_unpacking(): + p = Program() + + p.inst(AND("ro", ("ro", 1)), + MOVE("ro", ("ro", 1)), + CONVERT("ro", ("ro", 1)), + IOR("ro", ("ro", 1)), + XOR("ro", ("ro", 1)), + ADD("ro", ("ro", 1)), + SUB("ro", ("ro", 1)), + MUL("ro", ("ro", 1)), + DIV("ro", ("ro", 1)), + EXCHANGE("ro", ("ro", 1))) + + assert p.out() == 'AND ro[0] ro[1]\n' \ + 'MOVE ro[0] ro[1]\n' \ + 'CONVERT ro[0] ro[1]\n' \ + 'IOR ro[0] ro[1]\n' \ + 'XOR ro[0] ro[1]\n' \ + 'ADD ro[0] ro[1]\n' \ + 'SUB ro[0] ro[1]\n'\ + 'MUL ro[0] ro[1]\n' \ + 'DIV ro[0] ro[1]\n' \ + 'EXCHANGE ro[0] ro[1]\n' + + def test_ternary_classicals(): p = Program() p.inst(LOAD(MemoryReference("ro", 0), "ro", MemoryReference("n", 0)), STORE("ro", MemoryReference("n", 0), MemoryReference("ro", 0)), - EQ(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 2)), + STORE("ro", MemoryReference("n", 0), 0), + STORE("ro", MemoryReference("n", 0), 0.1), + EQ(MemoryReference("ro", 0), MemoryReference("ro", 1), 0), + EQ(MemoryReference("ro", 0), MemoryReference("ro", 1), 0.0), + EQ(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 0)), + GE(MemoryReference("ro", 0), MemoryReference("ro", 1), 1), + GE(MemoryReference("ro", 0), MemoryReference("ro", 1), 1.1), + GE(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 1)), + GT(MemoryReference("ro", 0), MemoryReference("ro", 1), 2), + GT(MemoryReference("ro", 0), MemoryReference("ro", 1), 2.2), GT(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 2)), - GE(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 2)), - LE(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 2)), - LT(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 2))) + LE(MemoryReference("ro", 0), MemoryReference("ro", 1), 3), + LE(MemoryReference("ro", 0), MemoryReference("ro", 1), 3.3), + LE(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 3)), + LT(MemoryReference("ro", 0), MemoryReference("ro", 1), 4), + LT(MemoryReference("ro", 0), MemoryReference("ro", 1), 4.4), + LT(MemoryReference("ro", 0), MemoryReference("ro", 1), MemoryReference("ro", 4))) assert p.out() == 'LOAD ro[0] ro n[0]\n' \ 'STORE ro n[0] ro[0]\n' \ - 'EQ ro[0] ro[1] ro[2]\n' \ + 'STORE ro n[0] 0\n' \ + 'STORE ro n[0] 0.1\n' \ + 'EQ ro[0] ro[1] 0\n' \ + 'EQ ro[0] ro[1] 0.0\n' \ + 'EQ ro[0] ro[1] ro[0]\n' \ + 'GE ro[0] ro[1] 1\n' \ + 'GE ro[0] ro[1] 1.1\n' \ + 'GE ro[0] ro[1] ro[1]\n' \ + 'GT ro[0] ro[1] 2\n' \ + 'GT ro[0] ro[1] 2.2\n' \ 'GT ro[0] ro[1] ro[2]\n' \ - 'GE ro[0] ro[1] ro[2]\n' \ - 'LE ro[0] ro[1] ro[2]\n' \ - 'LT ro[0] ro[1] ro[2]\n' + 'LE ro[0] ro[1] 3\n' \ + 'LE ro[0] ro[1] 3.3\n' \ + 'LE ro[0] ro[1] ro[3]\n' \ + 'LT ro[0] ro[1] 4\n' \ + 'LT ro[0] ro[1] 4.4\n' \ + 'LT ro[0] ro[1] ro[4]\n' def test_measurement_calls():