Repository: rigetticomputing/pyquil
Pull request #678 (2018-11-08 00:49:44)

Move "ParametricProgram" section from advanced use
Parametric Programs have been deprecated, this section is about migration for anyone who used it, so it is being moved to migration. Otherwise, this feature is documented in Programs and Gates


Patch:
diff --git a/docs/source/advanced_usage.rst b/docs/source/advanced_usage.rst
index e60d18f..8254fdd 100644
--- a/docs/source/advanced_usage.rst
+++ b/docs/source/advanced_usage.rst
@@ -80,11 +80,15 @@ where
 Using Qubit Placeholders
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
+.. note::
+    The functionality provided inline by ``QubitPlaceholders`` is similar to writing a function which returns a
+    ``Program``, with qubit indices taken as arguments to the function.
+
 In pyQuil, we typically use integers to identify qubits
 
 .. code:: python
 
-    from pyquil.quil import Program
+    from pyquil import Program
     from pyquil.gates import CNOT, H
     print(Program(H(0), CNOT(0, 1)))
 
@@ -103,8 +107,8 @@ where using ``QubitPlaceholder``\ s comes in.
     from pyquil.quilatom import QubitPlaceholder
     q0 = QubitPlaceholder()
     q1 = QubitPlaceholder()
-    prog = Program(H(q0), CNOT(q0, q1))
-    print(prog)
+    p = Program(H(q0), CNOT(q0, q1))
+    print(p)
 
 .. parsed-literal::
 
@@ -115,27 +119,10 @@ If you try to use this program directly, it will not work
 
 .. code:: python
 
-    print(prog.out())
+    print(p.out())
 
 ::
 
-    ---------------------------------------------------------------------------
-
-    RuntimeError                              Traceback (most recent call last)
-
-    <ipython-input-3-da474d3af403> in <module>()
-    ----> 1 print(prog.out())
-
-    ...
-
-    pyquil/pyquil/quilatom.py in out(self)
-         53 class QubitPlaceholder(QuilAtom):
-         54     def out(self):
-    ---> 55         raise RuntimeError("Qubit {} has not been assigned an index".format(self))
-         56
-         57     def __str__(self):
-
-
     RuntimeError: Qubit q4402789176 has not been assigned an index
 
 
@@ -146,14 +133,14 @@ N.
 .. code:: python
 
     from pyquil.quil import address_qubits
-    print(address_qubits(prog))
+    print(address_qubits(p))
 
 .. parsed-literal::
 
     H 0
     CNOT 0 1
 
-The real power comes into play when you provide an explicit mapping
+The real power comes into play when you provide an explicit mapping:
 
 .. code:: python
 
@@ -178,8 +165,8 @@ list of qubits to build your program.
 .. code:: python
 
     qbyte = QubitPlaceholder.register(8)
-    prog2 = Program(H(q) for q in qbyte)
-    print(address_qubits(prog2, {q: i*2 for i, q in enumerate(qbyte)}))
+    p_evens = Program(H(q) for q in qbyte)
+    print(address_qubits(p_evens, {q: i*2 for i, q in enumerate(qbyte)}))
 
 
 .. parsed-literal::
@@ -196,102 +183,121 @@ list of qubits to build your program.
 Classical Control Flow
 ~~~~~~~~~~~~~~~~~~~~~~
 
-Here are a couple quick examples that show how much richer the classical
-control of a Quil program can be. In this first example, we have a
-register called ``classical_flag_register`` which we use for looping.
-Then we construct the loop in the following steps:
+.. note::
+
+    Classical control flow is not yet supported on the QPU.
+
+
+Here are a couple quick examples that show how much richer a Quil program
+can be with classical control flow. In this first example, we create a while
+loop by following these steps:
+
+1. Declare a register called ``flag_register`` to use as a boolean test for looping.
 
-1. We first initialize this register to ``1`` with the ``init_register``
-   program so our while loop will execute. This is often called the
+2. Initialize this register to ``1`` program so our while loop will execute. This is often called the
    *loop preamble* or *loop initialization*.
 
-2. Next, we write body of the loop in a program itself. This will be a
-   program that computes an :math:`X` followed by an :math:`H` on our
+3. Write the body of the loop in its own :py:class:`~pyquil.quil.Program`. This will be a
+   program that applies an :math:`X` gate followed by a :math:`H` gate on our
    qubit.
 
-3. Lastly, we put it all together using the ``while_do`` method.
+4. Using the :py:func:`~pyquil.quil.Program.while_do` method to add control flow.
 
 .. code:: python
 
-    # Name our classical registers:
-    classical_flag_register = 2
+    from pyquil import Program
+    from pyquil.gates import *
 
-    # Write out the loop initialization and body programs:
-    init_register = Program(TRUE([classical_flag_register]))
-    loop_body = Program(X(0), H(0)).measure(0, classical_flag_register)
+    # Initialize the Program and declare a 1 bit memory space for our boolean flag
+    outer_loop = Program()
+    flag_register = outer_loop.declare('flag_register', 'BIT')
 
-    # Put it all together in a loop program:
-    loop_prog = init_register.while_do(classical_flag_register, loop_body)
+    # Set the initial flag value to 1
+    outer_loop += MOVE(flag_register, 1)
 
-    print(loop_prog)
+    # Define the body of the loop with a new Program
+    inner_loop = Program()
+    inner_loop += Program(X(0), H(0))
+    inner_loop += MEASURE(0, flag_register)
+
+    # Run inner_loop in a loop until flag_register is 0
+    outer_loop.while_do(flag_register, inner_loop)
+
+    print(outer_loop)
 
 .. parsed-literal::
 
-    TRUE [2]
+    DECLARE flag_register BIT[1]
+    MOVE flag_register 1
     LABEL @START1
-    JUMP-UNLESS @END2 [2]
+    JUMP-UNLESS @END2 flag_register
     X 0
     H 0
-    MEASURE 0 [2]
+    MEASURE 0 flag_register
     JUMP @START1
     LABEL @END2
 
-Notice that the ``init_register`` program applied a Quil instruction directly to a
+Notice that the ``outer_loop`` program applied a Quil instruction directly to a
 classical register.  There are several classical commands that can be used in this fashion:
 
-- ``TRUE`` which sets a single classical bit to be 1
-- ``FALSE`` which sets a single classical bit to be 0
 - ``NOT`` which flips a classical bit
 - ``AND`` which operates on two classical bits
-- ``OR`` which operates on two classical bits
+- ``IOR`` which operates on two classical bits
 - ``MOVE`` which moves the value of a classical bit at one classical address into another
 - ``EXCHANGE`` which swaps the value of two classical bits
 
 In this next example, we show how to do conditional branching in the
 form of the traditional ``if`` construct as in many programming
 languages. Much like the last example, we construct programs for each
-branch of the ``if``, and put it all together by using the ``if_then``
+branch of the ``if``, and put it all together by using the :py:func:`~pyquil.quil.Program.if_then`
 method.
 
 .. code:: python
 
-    # Name our classical registers:
-    test_register = 1
-    answer_register = 0
+    # Declare our memory spaces
+    branching_prog = Program()
+    test_register = branching_prog.declare('test_register', 'BIT')
+    ro = branching_prog.declare('ro', 'BIT')
 
     # Construct each branch of our if-statement. We can have empty branches
     # simply by having empty programs.
     then_branch = Program(X(0))
     else_branch = Program()
 
-    # Make a program that will put a 0 or 1 in test_register with 50% probability:
-    branching_prog = Program(H(1)).measure(1, test_register)
+    # Construct our program so that the result in test_register is equally likely to be a 0 or 1
+    branching_prog += H(1)
+    branching_prog += MEASURE(1, test_register)
 
-    # Add the conditional branching:
+    # Add the conditional branching
     branching_prog.if_then(test_register, then_branch, else_branch)
 
-    # Measure qubit 0 into our answer register:
-    branching_prog.measure(0, answer_register)
+    # Measure qubit 0 into our readout register
+    branching_prog += MEASURE(0, ro)
 
     print(branching_prog)
 
 .. parsed-literal::
 
+    DECLARE test_register BIT[1]
+    DECLARE ro BIT[1]
     H 1
-    MEASURE 1 [1]
-    JUMP-WHEN @THEN3 [1]
-    JUMP @END4
-    LABEL @THEN3
+    MEASURE 1 test_register
+    JUMP-WHEN @THEN1 test_register
+    JUMP @END2
+    LABEL @THEN1
     X 0
-    LABEL @END4
-    MEASURE 0 [0]
+    LABEL @END2
+    MEASURE 0 ro
 
-We can run this program a few times to see what we get in the
-``answer_register``.
+We can run this program a few times to see what we get in the readout register ``ro``.
 
 .. code:: python
 
-    qvm.run(branching_prog, [answer_register], 10)
+    from pyquil import get_qc
+
+    qc = get_qc("2q-qvm")
+    branching_prog.wrap_in_numshots_loop(10)
+    qc.run(branching_prog)
 
 .. parsed-literal::
 
@@ -340,29 +346,6 @@ we should always measure ``1``.
     Without Noise: [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
     With Noise   : [[0], [0], [0], [0], [0], [1], [1], [1], [1], [0]]
 
-Parametric Programs
-~~~~~~~~~~~~~~~~~~~
-
-In PyQuil 1.x, there was an object named ``ParametricProgram``::
-
-    # This function returns a quantum circuit with different rotation angles on a gate on qubit 0
-    def rotator(angle):
-        return Program(RX(angle, 0))
-
-    from pyquil.parametric import ParametricProgram
-    par_p = ParametricProgram(rotator) # This produces a new type of parameterized program object
-
-This object has been removed from PyQuil 2. Please consider simply using a Python function for
-the above functionality::
-
-    par_p = rotator
-
-Or using declared classical memory::
-
-    p = Program()
-    angle = p.declare('angle', 'REAL')
-    p += RX(angle, 0)
-
 Pauli Operator Algebra
 ~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/docs/source/migration.rst b/docs/source/migration.rst
index 75c28d8..0bbf5b7 100644
--- a/docs/source/migration.rst
+++ b/docs/source/migration.rst
@@ -632,3 +632,26 @@ location:
 
 Please attach such a logfile to any request for support.
 
+Parametric Programs
+~~~~~~~~~~~~~~~~~~~
+
+In PyQuil 1.x, there was an object named ``ParametricProgram``::
+
+    # This function returns a quantum circuit with different rotation angles on a gate on qubit 0
+    def rotator(angle):
+        return Program(RX(angle, 0))
+
+    from pyquil.parametric import ParametricProgram
+    par_p = ParametricProgram(rotator) # This produces a new type of parameterized program object
+
+This object has been removed from PyQuil 2. Please consider simply using a Python function for
+the above functionality::
+
+    par_p = rotator
+
+Or using declared classical memory::
+
+    p = Program()
+    angle = p.declare('angle', 'REAL')
+    p += RX(angle, 0)
+
diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py
index e1c1b4b..e0e691f 100644
--- a/pyquil/api/_quantum_computer.py
+++ b/pyquil/api/_quantum_computer.py
@@ -212,7 +212,7 @@ class QuantumComputer:
         into a 2d numpy array of bitstrings, consider::
 
             bitstrings = qc.run_and_measure(...)
-            bitstring_array = np.vstack(bitstrings[q] for q in sorted(qc.qubits())).T
+            bitstring_array = np.vstack(bitstrings[q] for q in qc.qubits()).T
             bitstring_array.shape  # (trials, len(qc.qubits()))
 
         .. note::
diff --git a/pyquil/quil.py b/pyquil/quil.py
index 76a8d58..bf28031 100644
--- a/pyquil/quil.py
+++ b/pyquil/quil.py
@@ -351,7 +351,7 @@ class Program(object):
         label_start = LabelPlaceholder("START")
         label_end = LabelPlaceholder("END")
         self.inst(JumpTarget(label_start))
-        self.inst(JumpUnless(target=label_end, condition=Addr(classical_reg)))
+        self.inst(JumpUnless(target=label_end, condition=classical_reg))
         self.inst(q_program)
         self.inst(Jump(label_start))
         self.inst(JumpTarget(label_end))
@@ -632,7 +632,7 @@ class Program(object):
         return not self.__eq__(other)
 
     def __len__(self):
-        return len(self._instructions)
+        return len(self.instructions)
 
     def __str__(self):
         """
diff --git a/pyquil/quilbase.py b/pyquil/quilbase.py
index 45fda3f..f099cfb 100644
--- a/pyquil/quilbase.py
+++ b/pyquil/quilbase.py
@@ -304,6 +304,7 @@ class JumpConditional(AbstractInstruction):
     """
     Abstract representation of an conditional jump instruction.
     """
+    op = NotImplemented
 
     def __init__(self, target, condition):
         if not isinstance(target, (Label, LabelPlaceholder)):
@@ -400,6 +401,7 @@ class LogicalBinaryOp(AbstractInstruction):
     """
     The abstract class for binary logical classical instructions.
     """
+    op = NotImplemented
 
     def __init__(self, left, right):
         if not isinstance(left, MemoryReference):


Test patch:
diff --git a/pyquil/tests/test_quantum_computer.py b/pyquil/tests/test_quantum_computer.py
index 8b89ecf..fcab654 100644
--- a/pyquil/tests/test_quantum_computer.py
+++ b/pyquil/tests/test_quantum_computer.py
@@ -285,9 +285,11 @@ def test_run_and_measure(local_qvm_quilc):
     qc = get_qc("9q-generic-qvm")
     prog = Program(I(8))
     trials = 11
-    with local_qvm():   # Redundant with test fixture.
+    # note to devs: this is included as an example in the run_and_measure docstrings
+    # so if you change it here ... change it there!
+    with local_qvm():  # Redundant with test fixture.
         bitstrings = qc.run_and_measure(prog, trials)
-    bitstring_array = np.vstack(bitstrings[q] for q in sorted(qc.qubits())).T
+    bitstring_array = np.vstack(bitstrings[q] for q in qc.qubits()).T
     assert bitstring_array.shape == (trials, len(qc.qubits()))
 
 
diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py
index 53017fe..145d869 100644
--- a/pyquil/tests/test_quil.py
+++ b/pyquil/tests/test_quil.py
@@ -115,7 +115,7 @@ def test_len_nested():
     p = Program(H(0)).measure(0, 0)
     q = Program(H(0), CNOT(0, 1))
     p.if_then(MemoryReference("ro", 0), q)
-    assert len(p) == 8
+    assert len(p) == 9
 
 
 def test_plus_operator():
@@ -456,22 +456,28 @@ def test_define_qft():
 
 
 def test_control_flows():
-    classical_flag_register = 2
-    p = Program(X(0), H(0)).measure(0, classical_flag_register)
-
-    # run p in a loop until classical_flag_register is 0
-    loop_prog = Program(X(0)).measure(0, classical_flag_register)
-    loop_prog.while_do(classical_flag_register, p)
-    assert loop_prog.out() == ('DECLARE ro BIT[3]\n'
-                               'X 0\n'
-                               'MEASURE 0 ro[2]\n'
-                               'LABEL @START1\n'
-                               'JUMP-UNLESS @END2 ro[2]\n'
-                               'X 0\n'
-                               'H 0\n'
-                               'MEASURE 0 ro[2]\n'
-                               'JUMP @START1\n'
-                               'LABEL @END2\n')
+    outer_loop = Program()
+    classical_flag_register = outer_loop.declare('classical_flag_register', 'BIT')
+    outer_loop += MOVE(classical_flag_register, 1)  # initialize
+
+    inner_loop = Program()
+    inner_loop += Program(X(0), H(0))
+    inner_loop += MEASURE(0, classical_flag_register)
+
+    # run inner_loop in a loop until classical_flag_register is 0
+    outer_loop.while_do(classical_flag_register, inner_loop)
+    assert outer_loop.out() == '\n'.join([
+        "DECLARE classical_flag_register BIT[1]",
+        "MOVE classical_flag_register 1",
+        "LABEL @START1",
+        "JUMP-UNLESS @END2 classical_flag_register",
+        "X 0",
+        "H 0",
+        "MEASURE 0 classical_flag_register",
+        "JUMP @START1",
+        "LABEL @END2",
+        ""
+    ])
 
 
 def test_control_flows_2():