Repository: rigetticomputing/pyquil
Pull request #611 (2018-10-25 20:47:47)

Update examples for pyQuil 2
Resolved issue #572.

I've set the default memory type in Program.declare to be 'BIT', which seems to make sense from updating these examples to use declared memory registers. 


Patch:
diff --git a/examples/forest2-simple-prog.py b/examples/forest2-simple-prog.py
old mode 100644
new mode 100755
index 2b27cb7..7ba0103
--- a/examples/forest2-simple-prog.py
+++ b/examples/forest2-simple-prog.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 import numpy as np
 
 from pyquil import Program, get_qc
@@ -16,15 +18,14 @@ def run_bell_high_level(n_shots=1000):
     )
 
     # Step 3. Run
-    bitstrings = qc.run_and_measure(program, trials=n_shots)
+    results = qc.run_and_measure(program, trials=n_shots)
 
     # Bincount bitstrings
-    basis = np.array([2 ** i for i in range(len(q))])
-    ints = np.sum(bitstrings * basis, axis=1)
+    ints = sum(results[q[i]] * 2 ** i for i in range(len(q)))
     print('bincounts', np.bincount(ints))
 
     # Check parity
-    parities = np.sum(bitstrings, axis=1) % 2
+    parities = (results[q[0]] + results[q[1]]) % 2
     print('avg parity', np.mean(parities))
 
 
@@ -88,7 +89,7 @@ def run_bell_low_level(n_shots=1000):
     bitstrings = qam.load(executable) \
         .run() \
         .wait() \
-        .read_from_memory_region(region_name="ro", offsets=True)
+        .read_from_memory_region(region_name="ro")
 
     # Bincount bitstrings
     basis = np.array([2 ** i for i in range(len(q))])
diff --git a/examples/hello_world.quil b/examples/hello_world.quil
index 70c7c4c..f7ac859 100644
--- a/examples/hello_world.quil
+++ b/examples/hello_world.quil
@@ -1,2 +1,3 @@
+DECLARE ro BIT[1]
 X 0
-MEASURE 0 [0]
+MEASURE 0 ro[0]
diff --git a/examples/meyer_penny_game.py b/examples/meyer_penny_game.py
old mode 100644
new mode 100755
index 6c6a8b2..25ce80b
--- a/examples/meyer_penny_game.py
+++ b/examples/meyer_penny_game.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 ##############################################################################
 # Copyright 2016-2017 Rigetti Computing
 #
@@ -28,31 +30,34 @@ def meyer_penny_program():
 
     :return: pyQuil Program
     """
-
-    picard_register = 1
-    answer_register = 0
+    prog = pq.Program()
+    ro = prog.declare('ro')
+    picard_register = ro[1]
+    answer_register = ro[0]
 
     then_branch = pq.Program(X(0))
     else_branch = pq.Program(I(0))
 
-    return (pq.Program()
-            # Prepare Qubits in Heads state or superposition, respectively
-            .inst(X(0), H(1))
-            # Q puts the coin into a superposition
-            .inst(H(0))
-            # Picard makes a decision and acts accordingly
-            .measure(1, picard_register)
-            .if_then(picard_register, then_branch, else_branch)
-            # Q undoes his superposition operation
-            .inst(H(0))
-            # The outcome is recorded into the answer register
-            .measure(0, answer_register))
+    # Prepare Qubits in Heads state or superposition, respectively
+    prog.inst(X(0), H(1))
+    # Q puts the coin into a superposition
+    prog.inst(H(0))
+    # Picard makes a decision and acts accordingly
+    prog.measure(1, picard_register)
+    prog.if_then(picard_register, then_branch, else_branch)
+    # Q undoes his superposition operation
+    prog.inst(H(0))
+    # The outcome is recorded into the answer register
+    prog.measure(0, answer_register)
+
+    return prog
 
 
 if __name__ == "__main__":
     n_trials = 10
     qvm = api.QVMConnection()
-    outcomes = np.asarray(qvm.run(meyer_penny_program(), [0, 1], trials=n_trials))
+    outcomes = np.asarray(qvm.run(meyer_penny_program(), [0, 1],
+                                  trials=n_trials))
 
     print("Number of games: {}".format(n_trials))
     print("Q's winning average: {}".format(outcomes[:, 0].mean()))
diff --git a/examples/old-website-script.py b/examples/old-website-script.py
new file mode 100755
index 0000000..97c082c
--- /dev/null
+++ b/examples/old-website-script.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+"""
+A script appropriate for featuring on rigetti.com to show how easy it is to get started!
+
+This version exists on the website as of September, 2018. It should continue working
+even if PyQuil changes.
+"""
+
+from pyquil.quil import Program
+from pyquil.gates import H, CNOT
+from pyquil.api import QVMConnection
+# construct a Bell State program
+p = Program(H(0), CNOT(0, 1))
+# run the program on a QVM
+qvm = QVMConnection()
+result = qvm.wavefunction(p)
+print(result)
diff --git a/examples/pointer.py b/examples/pointer.py
old mode 100644
new mode 100755
index e0885a8..bbd754b
--- a/examples/pointer.py
+++ b/examples/pointer.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+
 ##############################################################################
 # Copyright 2016-2017 Rigetti Computing
 #
@@ -45,7 +46,7 @@ def gray(num_bits):
     x = 0
     n = 1 << num_bits
     while n > x:
-        bit_string = bin(n + x ^ x//2)[3:]
+        bit_string = bin(n + x ^ x // 2)[3:]
         value = int(bit_string, 2)
         yield bit_string, value, changed_bit_pos(last, value)
         last = value
@@ -59,7 +60,7 @@ def controlled(num_ptr_bits, U):
     """
     d = 2 ** (1 + num_ptr_bits)
     m = np.eye(d)
-    m[d-2:, d-2:] = U
+    m[d - 2:, d - 2:] = U
     return m
 
 
@@ -116,5 +117,5 @@ def pointer_gate(num_qubits, U):
 
 
 if __name__ == '__main__':
-    H = np.matrix([[1, 1], [1, -1]])/np.sqrt(2)
+    H = np.matrix([[1, 1], [1, -1]]) / np.sqrt(2)
     print(pointer_gate(11, H))
diff --git a/examples/qaoa_ansatz.py b/examples/qaoa_ansatz.py
old mode 100644
new mode 100755
index 521a0d0..8a76a81
--- a/examples/qaoa_ansatz.py
+++ b/examples/qaoa_ansatz.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 ##############################################################################
 # Copyright 2016-2018 Rigetti Computing
 #
@@ -20,6 +22,7 @@ implementation of QAOA, refer to the grove example and docs:
 http://grove-docs.readthedocs.io/en/latest/qaoa.html
 """
 
+from pyquil import get_qc
 from pyquil.quil import Program
 from pyquil.gates import H
 from pyquil.paulis import sI, sX, sZ, exponentiate_commuting_pauli_sum
@@ -40,6 +43,7 @@ h_cost = -0.5 * sum(sI(nodes[0]) - sZ(i) * sZ(j) for i, j in graph)
 # The driver Hamiltonian is the sum of the application of \sigma_x^i for all qubits i.
 h_driver = -1. * sum(sX(i) for i in nodes)
 
+
 def qaoa_ansatz(gammas, betas):
     """
     Function that returns a QAOA ansatz program for a list of angles betas and gammas. len(betas) ==
@@ -50,23 +54,15 @@ def qaoa_ansatz(gammas, betas):
     :return: The QAOA ansatz program.
     :rtype: Program.
     """
-    return Program([exponentiate_commuting_pauli_sum(h_cost)(g) + exponentiate_commuting_pauli_sum(h_driver)(b) \
-        for g, b in zip(gammas, betas)])
+    return Program([exponentiate_commuting_pauli_sum(h_cost)(g)
+                    + exponentiate_commuting_pauli_sum(h_driver)(b)
+                    for g, b in zip(gammas, betas)])
+
 
 # Create a program, the state initialization plus a QAOA ansatz program, for P = 2.
 program = init_state_prog + qaoa_ansatz([0., 0.5], [0.75, 1.])
 
 # Initialize the QVM and run the program.
+qc = get_qc('9q-generic-qvm')
 
-# NOTE: We patch out the qvm.run_and_measure method, since our automated testing environment may not
-# have privileges to run on the QVM. To run this on your own machine, remove this patch, and replace
-# it with:
-# qvm = QVMConnection()
-from unittest.mock import patch
-with patch("pyquil.api.QVMConnection") as qvm:
-    qvm.run_and_measure.return_value = [
-        [1, 0, 1, 0],
-        [1, 0, 1, 0]
-    ]
-
-results = qvm.run_and_measure(program, qubits=nodes, trials=2)
+results = qc.run_and_measure(program, trials=2)
diff --git a/examples/quantum_die.py b/examples/quantum_die.py
old mode 100644
new mode 100755
index 0805bde..c3ad738
--- a/examples/quantum_die.py
+++ b/examples/quantum_die.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+
 ##############################################################################
 # Copyright 2016-2017 Rigetti Computing
 #
@@ -22,44 +23,48 @@ from pyquil import api
 from pyquil.gates import H
 from six.moves import range
 
+
 def qubits_needed(n):
     """
     The number of qubits needed for a die of n faces.
     """
     return int(math.ceil(math.log(n, 2)))
 
+
 def die_program(n):
     """
     Generate a quantum program to roll a die of n faces.
     """
     prog = pq.Program()
+    ro = prog.declare('ro')
     qubits = qubits_needed(n)
     # Hadamard initialize.
     for q in range(qubits):
         prog.inst(H(q))
     # Measure everything.
     for q in range(qubits):
-        prog.measure(q, [q])
+        prog.measure(q, ro[q])
     return prog
 
+
 def process_result(r):
     """
     Convert a list of measurements to a die value.
     """
-    return reduce(lambda s, x: 2*s + x, r, 0)
+    return reduce(lambda s, x: 2 * s + x, r, 0)
 
-BATCH_SIZE = 10
 
+BATCH_SIZE = 10
 dice = {}
-
 qvm = api.QVMConnection()
 
+
 def roll_die(n):
     """
     Roll an n-sided quantum die.
     """
     addresses = list(range(qubits_needed(n)))
-    if not n in dice:
+    if n not in dice:
         dice[n] = die_program(n)
     die = dice[n]
     # Generate results and do rejection sampling.
@@ -70,7 +75,7 @@ def roll_die(n):
             if 0 < x <= n:
                 return x
 
+
 if __name__ == '__main__':
     number_of_sides = int(input("Please enter number of sides: "))
-    while True:
-        print(roll_die(number_of_sides))
+    print('Quantum die roll:', roll_die(number_of_sides))
diff --git a/examples/run_quil.py b/examples/run_quil.py
old mode 100644
new mode 100755
index 522b4a2..aae8674
--- a/examples/run_quil.py
+++ b/examples/run_quil.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 """
 This module runs basic Quil text files against the Forest QVM API.
 """
@@ -22,6 +24,7 @@ def parse():
     args = parser.parse_args()
     main(args.filepath, args.classical_register_num)
 
+
 def main(filepath, classical_register_num):
     with open(filepath) as file:
         quil_prog = file.read()
@@ -32,5 +35,6 @@ def main(filepath, classical_register_num):
     print("Output: ")
     print(qvm.run(program, list(range(classical_register_num))))
 
+
 if __name__ == '__main__':
     parse()
diff --git a/examples/teleportation.py b/examples/teleportation.py
old mode 100644
new mode 100755
index 38a92ab..21b81a7
--- a/examples/teleportation.py
+++ b/examples/teleportation.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+
 ##############################################################################
 # Copyright 2016-2017 Rigetti Computing
 #
@@ -31,19 +32,22 @@ def teleport(start_index, end_index, ancilla_index):
     """
     program = make_bell_pair(end_index, ancilla_index)
 
+    ro = program.declare('ro')
+
     # do the teleportation
     program.inst(CNOT(start_index, ancilla_index))
     program.inst(H(start_index))
 
     # measure the results and store them in classical registers [0] and [1]
-    program.measure(start_index, 0)
-    program.measure(ancilla_index, 1)
+    program.measure(start_index, ro[0])
+    program.measure(ancilla_index, ro[1])
 
-    program.if_then(1, X(2))
-    program.if_then(0, Z(2))
+    program.if_then(ro[1], X(2))
+    program.if_then(ro[0], Z(2))
 
-    program.measure(end_index, 2)
+    program.measure(end_index, ro[2])
 
+    print(program)
     return program
 
 
@@ -64,5 +68,3 @@ if __name__ == '__main__':
     teleport_demo = Program(H(0))
     teleport_demo += teleport(0, 2, 1)
     print("Teleporting |+> state: {}".format(qvm.run(teleport_demo, [2], 10)))
-
-    print(Program(X(0)).measure(0, 0).if_then(0, Program(X(1))))
diff --git a/examples/website-script.py b/examples/website-script.py
old mode 100644
new mode 100755
index 69b052f..eed6551
--- a/examples/website-script.py
+++ b/examples/website-script.py
@@ -1,16 +1,19 @@
+#!/usr/bin/env python
+
 """
 A script appropriate for featuring on rigetti.com to show how easy it is to get started!
 
-This version exists on the website as of September, 2018. It should continue working
-even if PyQuil changes.
+The website should be updated to use this idiomatic program once Forest 2 is released.
+
+ - new imports
+ - don't use a wavefunction-based method, as it won't work on a QPU
 """
 
-from pyquil.quil import Program
-from pyquil.gates import H, CNOT
-from pyquil.api import QVMConnection
+from pyquil import Program, get_qc
+from pyquil.gates import *
 # construct a Bell State program
 p = Program(H(0), CNOT(0, 1))
 # run the program on a QVM
-qvm = QVMConnection()
-result = qvm.wavefunction(p)
+qvm = get_qc('9q-generic-qvm')
+result = qvm.run_and_measure(p, trials=10)
 print(result)
diff --git a/examples/website-script2.py b/examples/website-script2.py
deleted file mode 100644
index 7aac6b1..0000000
--- a/examples/website-script2.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""
-A script appropriate for featuring on rigetti.com to show how easy it is to get started!
-
-The website should be updated to use this idiomatic program once Forest 2 is released.
-
- - new imports
- - don't use a wavefunction-based method, as it won't work on a QPU
-"""
-
-from pyquil import Program, get_qc
-from pyquil.gates import *
-# construct a Bell State program
-p = Program(H(0), CNOT(0, 1))
-# run the program on a QVM
-qvm = get_qc('9q-generic-qvm')
-result = qvm.run_and_measure(p, trials=10)
-print(result)
diff --git a/pyquil/__init__.py b/pyquil/__init__.py
index 8ec8cf1..f53ba4e 100644
--- a/pyquil/__init__.py
+++ b/pyquil/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "2.0.0b7.dev"
+__version__ = "2.0.0b8.dev0"
 
 from pyquil.quil import Program
 from pyquil.api import list_quantum_computers, get_qc
diff --git a/pyquil/api/_qam.py b/pyquil/api/_qam.py
index 37c43f9..32ebf90 100644
--- a/pyquil/api/_qam.py
+++ b/pyquil/api/_qam.py
@@ -13,6 +13,8 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 ##############################################################################
+import warnings
+
 from abc import ABC, abstractmethod
 
 from rpcq.messages import ParameterAref
@@ -52,7 +54,9 @@ class QAM(ABC):
 
         :param executable: Load a compiled executable onto the QAM.
         """
-        assert self.status in ['connected', 'done']
+        if self.status == 'loaded':
+            warnings.warn("Overwriting previously loaded executable.")
+        assert self.status in ['connected', 'done', 'loaded']
 
         self._variables_shim = {}
         self._executable = executable
diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py
index 0dbbc04..ffe71d7 100644
--- a/pyquil/api/_qpu.py
+++ b/pyquil/api/_qpu.py
@@ -14,6 +14,7 @@
 #    limitations under the License.
 ##############################################################################
 import uuid
+import warnings
 from typing import Dict, List, Optional, Tuple
 
 import numpy as np
@@ -110,6 +111,11 @@ class QPU(QAM):
 
         if results:
             bitstrings = _extract_bitstrings(ro_sources, results)
+        elif not ro_sources:
+            warnings.warn("You are running a QPU program with no MEASURE instructions. "
+                          "The result of this program will always be an empty array. Are "
+                          "you sure you didn't mean to measure some of your qubits?")
+            bitstrings = np.zeros((0, 0), dtype=np.int64)
         else:
             bitstrings = None
 
diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py
index 57704e2..1e2a965 100644
--- a/pyquil/api/_quantum_computer.py
+++ b/pyquil/api/_quantum_computer.py
@@ -16,13 +16,13 @@
 import re
 import warnings
 from math import pi
-from typing import List, Dict, Tuple, Iterator
+from typing import List, Dict, Tuple, Iterator, Union
 import subprocess
 from contextlib import contextmanager
 
 import networkx as nx
 import numpy as np
-from rpcq.messages import BinaryExecutableResponse, Message
+from rpcq.messages import BinaryExecutableResponse, Message, PyQuilExecutableResponse
 
 from pyquil.api._compiler import QVMCompiler, QPUCompiler, LocalQVMCompiler
 from pyquil.api._config import PyquilConfig
@@ -40,6 +40,8 @@ from pyquil.quilbase import Measurement, Pragma, Gate, Reset
 
 pyquil_config = PyquilConfig()
 
+Executable = Union[BinaryExecutableResponse, PyQuilExecutableResponse]
+
 
 def _get_flipped_protoquil_program(program: Program) -> Program:
     """For symmetrization, generate a program where X gates are added before measurement.
@@ -121,7 +123,7 @@ class QuantumComputer:
         return self.device.get_isa(oneq_type=oneq_type, twoq_type=twoq_type)
 
     @_record_call
-    def run(self, executable: BinaryExecutableResponse) -> np.ndarray:
+    def run(self, executable: Executable) -> np.ndarray:
         """
         Run a quil executable.
 
@@ -330,7 +332,9 @@ def _get_9q_square_qvm(connection: ForestConnection, noisy: bool) -> QuantumComp
 
     name = '9q-square-noisy-qvm' if noisy else '9q-square-qvm'
     return QuantumComputer(name=name,
-                           qam=QVM(connection=connection, noise_model=noise_model),
+                           qam=QVM(connection=connection,
+                                   noise_model=noise_model,
+                                   requires_executable=True),
                            device=nineq_device,
                            compiler=_get_qvm_compiler_based_on_endpoint(
                                device=nineq_device,
@@ -398,6 +402,11 @@ def get_qc(name: str, *, as_qvm: bool = None, noisy: bool = None,
 
         >>> qc = get_qc("5q-qvm") # or "6q-qvm", or "34q-qvm", ...
 
+    These less-realistic, fully-connected QVMs will also be more lenient on what types of programs
+    they will ``run``. Specifically, you do not need to do any compilation. For the other, realistic
+    QVMs you must use :py:func:`qc.compile` or :py:func:`qc.compiler.native_quil_to_executable`
+    prior to :py:func:`qc.run`.
+
     Redundant flags are acceptable, but conflicting flags will raise an exception::
 
         >>> qc = get_qc("9q-square-qvm") # qc is fully specified by its name
@@ -469,7 +478,9 @@ def get_qc(name: str, *, as_qvm: bool = None, noisy: bool = None,
         noise_model = None
 
     return QuantumComputer(name=full_name,
-                           qam=QVM(connection=connection, noise_model=noise_model),
+                           qam=QVM(connection=connection,
+                                   noise_model=noise_model,
+                                   requires_executable=True),
                            device=device,
                            compiler=_get_qvm_compiler_based_on_endpoint(
                                device=device,
@@ -494,7 +505,7 @@ def local_qvm() -> Iterator[Tuple[subprocess.Popen, subprocess.Popen]]:
     >>> from pyquil.gates import CNOT, Z
     >>> from pyquil.api import local_qvm
     >>>
-    >>> qvm = get_qc('9q-generic-qvm')
+    >>> qvm = get_qc('9q-square-qvm')
     >>> prog = Program(Z(0), CNOT(0, 1))
     >>>
     >>> with local_qvm():
diff --git a/pyquil/api/_qvm.py b/pyquil/api/_qvm.py
index c83ca1d..0868a8f 100644
--- a/pyquil/api/_qvm.py
+++ b/pyquil/api/_qvm.py
@@ -14,6 +14,7 @@
 #    limitations under the License.
 ##############################################################################
 import warnings
+import numpy as np
 from typing import List
 
 from rpcq.messages import PyQuilExecutableResponse
@@ -353,12 +354,12 @@ class QVM(QAM):
     @_record_call
     def __init__(self,
                  connection: ForestConnection,
-                 *args,
                  noise_model=None,
                  gate_noise=None,
                  measurement_noise=None,
                  random_seed=None,
-                 **kwargs) -> None:
+                 requires_executable=False,
+                 ) -> None:
         """
         A virtual machine that classically emulates the execution of Quil programs.
 
@@ -373,8 +374,11 @@ class QVM(QAM):
             None indicates no noise.
         :param random_seed: A seed for the QVM's random number generators. Either None (for an
             automatically generated seed) or a non-negative integer.
+        :param requires_executable: Whether this QVM will refuse to run a :py:class:`Program` and
+            only accept the result of :py:func:`compiler.native_quil_to_executable`. Setting this
+            to True better emulates the behavior of a QPU.
         """
-        super().__init__(*args, **kwargs)
+        super().__init__()
 
         if (noise_model is not None) and (gate_noise is not None or measurement_noise is not None):
             raise ValueError("""
@@ -400,6 +404,8 @@ To read more about supplying noise to the QVM, see http://pyquil.readthedocs.io/
         else:
             raise TypeError("random_seed should be None or a non-negative int")
 
+        self.requires_executable = requires_executable
+
     @_record_call
     def get_version_info(self):
         """
@@ -409,6 +415,39 @@ To read more about supplying noise to the QVM, see http://pyquil.readthedocs.io/
         """
         return self.connection._qvm_get_version_info()
 
+    @_record_call
+    def load(self, executable):
+        """
+        Initialize a QAM and load a program to be executed with a call to :py:func:`run`.
+
+        If ``QVM.requires_executable`` is set to ``True``, this function will only load
+        :py:class:`PyQuilExecutableResponse` executables. This more closely follows the behavior
+        of :py:class:`QPU`. However, the quantum simulator doesn't *actually* need a compiled
+        binary executable, so if this flag is set to ``False`` we also accept :py:class:`Program`
+        objects.
+
+        :param executable: An executable. See the above note for acceptable types.
+        """
+        if self.requires_executable:
+            if isinstance(executable, PyQuilExecutableResponse):
+                executable = _extract_program_from_pyquil_executable_response(executable)
+            else:
+                raise TypeError("`executable` argument must be a `PyQuilExecutableResponse`. Make "
+                                "sure you have explicitly compiled your program via `qc.compile` "
+                                "or `qc.compiler.native_quil_to_executable(...)` for more "
+                                "fine-grained control. This explicit step is required for running "
+                                "on a QPU.")
+        else:
+            if isinstance(executable, PyQuilExecutableResponse):
+                executable = _extract_program_from_pyquil_executable_response(executable)
+            elif isinstance(executable, Program):
+                pass
+            else:
+                raise TypeError("`executable` argument must be a `PyQuilExecutableResponse` or a "
+                                "`Program`. You provided {}".format(type(executable)))
+
+        return super().load(executable)
+
     @_record_call
     def run(self):
         """
@@ -420,16 +459,12 @@ To read more about supplying noise to the QVM, see http://pyquil.readthedocs.io/
 
         super().run()
 
-        if isinstance(self._executable, PyQuilExecutableResponse):
-            quil_program = _extract_program_from_pyquil_executable_response(self._executable)
-        elif isinstance(self._executable, Program):
-            quil_program = self._executable
-        else:
-            raise TypeError("quil_binary argument must be a PyQuilExecutableResponse or a Program."
-                            "This error is typically triggered by forgetting to pass (nativized)"
-                            "Quil to native_quil_to_executable or by using a compiler meant to be"
-                            "used for jobs bound for a QPU.")
+        if not isinstance(self._executable, Program):
+            # This should really never happen
+            # unless a user monkeys with `self.status` and `self._executable`.
+            raise ValueError("Please `load` an appropriate executable.")
 
+        quil_program = self._executable
         trials = quil_program.num_shots
         classical_addresses = get_classical_addresses_from_program(quil_program)
 
@@ -437,12 +472,18 @@ To read more about supplying noise to the QVM, see http://pyquil.readthedocs.io/
             quil_program = apply_noise_model(quil_program, self.noise_model)
 
         quil_program = self.augment_program_with_memory_values(quil_program)
-        self._bitstrings = self.connection._qvm_run(quil_program=quil_program,
-                                                    classical_addresses=classical_addresses,
-                                                    trials=trials,
-                                                    measurement_noise=self.measurement_noise,
-                                                    gate_noise=self.gate_noise,
-                                                    random_seed=self.random_seed)['ro']
+        try:
+            self._bitstrings = self.connection._qvm_run(quil_program=quil_program,
+                                                        classical_addresses=classical_addresses,
+                                                        trials=trials,
+                                                        measurement_noise=self.measurement_noise,
+                                                        gate_noise=self.gate_noise,
+                                                        random_seed=self.random_seed)['ro']
+        except KeyError:
+            warnings.warn("You are running a QVM program with no MEASURE instructions. "
+                          "The result of this program will always be an empty array. Are "
+                          "you sure you didn't mean to measure some of your qubits?")
+            self._bitstrings = np.zeros((trials, 0), dtype=np.int64)
 
         return self
 
diff --git a/pyquil/quil.py b/pyquil/quil.py
index 2afe45f..548b57b 100644
--- a/pyquil/quil.py
+++ b/pyquil/quil.py
@@ -408,7 +408,7 @@ class Program(object):
                       "Please create a `QubitPlaceholder` directly", DeprecationWarning)
         return QubitPlaceholder()
 
-    def declare(self, name, memory_type, memory_size=1, shared_region=None, offsets=None):
+    def declare(self, name, memory_type='BIT', memory_size=1, shared_region=None, offsets=None):
         """DECLARE a quil variable
 
         This adds the declaration to the current program and returns a MemoryReference to the
diff --git a/requirements.txt b/requirements.txt
index b2515ff..aa03603 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,7 +8,7 @@ networkx
 dataclasses
 
 # rigetti packages
-rpcq == 2.0.0
+rpcq == 2.1.0
 
 # test deps
 pytest
diff --git a/tox.ini b/tox.ini
index 0efe485..34317ec 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,7 +8,6 @@ deps=
     -rrequirements.txt
 commands=
     py.test -v pyquil/tests
-    python examples/qaoa_ansatz.py
 
 [testenv:docs]
 whitelist_externals = make


Test patch:
diff --git a/pyquil/tests/test_examples.py b/pyquil/tests/test_examples.py
new file mode 100644
index 0000000..6ae699a
--- /dev/null
+++ b/pyquil/tests/test_examples.py
@@ -0,0 +1,62 @@
+
+import os
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+
+import pytest
+
+
+@pytest.fixture
+def examples_dir(scope="module"):
+    path = os.path.dirname(__file__)
+    path = os.path.join(path, '..', '..', 'examples')
+    path = os.path.abspath(path)
+    return path
+
+
+def _call_script(path, script, *args):
+    # As bare minimum test that all example scripts run without error
+    rval = subprocess.call([os.path.join(path, script), *args])
+    return rval
+
+
+def test_meyer_penny_game(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'meyer_penny_game.py')
+
+
+def test_run_quil(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'run_quil.py',
+                             os.path.join(examples_dir, 'hello_world.quil'))
+
+
+def test_forest2_simple_prog(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'forest2-simple-prog.py')
+
+
+def test_pointer(examples_dir):
+    assert 0 == _call_script(examples_dir, 'pointer.py')
+
+
+def test_qaoa_ansatz(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'qaoa_ansatz.py')
+
+
+def test_quantum_die(forest, examples_dir):
+    p = Popen(['examples/quantum_die.py'],
+              stdout=PIPE, stdin=PIPE, stderr=STDOUT,
+              universal_newlines=True,
+              )
+    p.communicate('6')
+    assert p.returncode == 0
+
+
+def test_teleportation(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'teleportation.py')
+
+
+def test_website_old(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'old-website-script.py')
+
+
+def test_website(forest, examples_dir):
+    assert 0 == _call_script(examples_dir, 'website-script.py')
diff --git a/pyquil/tests/test_qpu.py b/pyquil/tests/test_qpu.py
index 81f56b6..860d6c5 100644
--- a/pyquil/tests/test_qpu.py
+++ b/pyquil/tests/test_qpu.py
@@ -25,11 +25,13 @@ def test_qpu_run():
                              compiler=QPUCompiler(endpoint=config.compiler_url,
                                                   device=device))
         bitstrings = qc.run_and_measure(
-            quil_program=Program(X(0)),
+            program=Program(X(0)),
             trials=1000,
         )
-        assert bitstrings.shape == (1000, 1)
-        assert np.mean(bitstrings) > 0.8
+        assert bitstrings[0].shape == (1000,)
+        assert np.mean(bitstrings[0]) > 0.8
+        bitstrings = qc.run(qc.compile(Program(X(0))))
+        assert bitstrings.shape == (0, 0)
     else:
         pytest.skip("QPU or compiler-server not available; skipping QPU run test.")
 
diff --git a/pyquil/tests/test_quantum_computer.py b/pyquil/tests/test_quantum_computer.py
index adcbd6a..a97b1b3 100644
--- a/pyquil/tests/test_quantum_computer.py
+++ b/pyquil/tests/test_quantum_computer.py
@@ -11,6 +11,7 @@ from pyquil.api._quantum_computer import _get_flipped_protoquil_program, _parse_
 from pyquil.device import NxDevice, gates_in_isa
 from pyquil.gates import *
 from pyquil.noise import decoherence_noise_with_asymmetric_ro
+from rpcq.messages import PyQuilExecutableResponse
 
 
 class DummyCompiler(AbstractCompiler):
@@ -297,3 +298,24 @@ def test_run_symmetrized_readout_error(local_qvm_quilc):
     # Trials not even
     with pytest.raises(ValueError):
         bitstrings = qc.run_symmetrized_readout(prog, trials)
+
+
+def test_qvm_compile_pickiness(forest):
+    p = Program(X(0), MEASURE(0, 0))
+    p.wrap_in_numshots_loop(1000)
+    nq = PyQuilExecutableResponse(program=p.out(), attributes={'num_shots': 1000})
+
+    # Ok, non-realistic
+    qc = get_qc('9q-qvm')
+    qc.run(p)
+
+    # Also ok
+    qc.run(nq)
+
+    # Not ok
+    qc = get_qc('9q-square-qvm')
+    with pytest.raises(TypeError):
+        qc.run(p)
+
+    # Yot ok
+    qc.run(nq)
diff --git a/pyquil/tests/test_qvm.py b/pyquil/tests/test_qvm.py
index 1f18509..3383007 100644
--- a/pyquil/tests/test_qvm.py
+++ b/pyquil/tests/test_qvm.py
@@ -1,5 +1,6 @@
 import networkx as nx
 import numpy as np
+import pytest
 
 from rpcq.messages import PyQuilExecutableResponse
 
@@ -10,7 +11,7 @@ from pyquil.device import NxDevice
 from pyquil.gates import MEASURE, X, CNOT, H
 
 
-def test_qvm_run(forest: ForestConnection):
+def test_qvm_run_pqer(forest: ForestConnection):
     qvm = QVM(connection=forest, gate_noise=[0.01] * 3)
     p = Program(X(0), MEASURE(0, 0))
     p.wrap_in_numshots_loop(1000)
@@ -23,6 +24,47 @@ def test_qvm_run(forest: ForestConnection):
     assert np.mean(bitstrings) > 0.8
 
 
+def test_qvm_run_just_program(forest: ForestConnection):
+    qvm = QVM(connection=forest, gate_noise=[0.01] * 3)
+    p = Program(X(0), MEASURE(0, 0))
+    p.wrap_in_numshots_loop(1000)
+    qvm.load(p)
+    qvm.run()
+    qvm.wait()
+    bitstrings = qvm.read_from_memory_region(region_name="ro")
+    assert bitstrings.shape == (1000, 1)
+    assert np.mean(bitstrings) > 0.8
+
+
+def test_qvm_run_only_pqer(forest: ForestConnection):
+    qvm = QVM(connection=forest, gate_noise=[0.01] * 3, requires_executable=True)
+    p = Program(X(0), MEASURE(0, 0))
+    p.wrap_in_numshots_loop(1000)
+
+    with pytest.raises(TypeError) as e:
+        qvm.load(p)
+        qvm.run()
+        qvm.wait()
+    assert e.match(r'.*Make sure you have explicitly compiled your program.*')
+
+    nq = PyQuilExecutableResponse(program=p.out(), attributes={'num_shots': 1000})
+    qvm.load(nq)
+    qvm.run()
+    qvm.wait()
+    bitstrings = qvm.read_from_memory_region(region_name="ro")
+    assert bitstrings.shape == (1000, 1)
+    assert np.mean(bitstrings) > 0.8
+
+
+def test_qvm_run_no_measure(forest: ForestConnection):
+    qvm = QVM(connection=forest)
+    p = Program(X(0))
+    nq = PyQuilExecutableResponse(program=p.out(), attributes={'num_shots': 100})
+    qvm.load(nq).run().wait()
+    bitstrings = qvm.read_from_memory_region(region_name="ro")
+    assert bitstrings.shape == (100, 0)
+
+
 def test_roundtrip_pyquilexecutableresponse():
     p = Program(H(10), CNOT(10, 11))
     lcqvm = LocalQVMCompiler(endpoint=None, device=NxDevice(nx.complete_graph(3)))