Repository: rigetticomputing/pyquil
Pull request #393 (2018-04-16 01:10:38)

doc improvements for expectation, and an added warning
@mpharrigan I've updated the docs in qvm.py to try to help set users `expectation`s. 

I'm not sure standard practice for warnings, but I just spent some time trying to debug why I was getting the wrong answer, when I was giving it a program, and it was happily iterating over it and shipping off the line-by-line subprograms.

e.g. 
`qvm.expectation(Program("X 0\nY 1"))`
When what I really wanted was
`qvm.expectation([Program("X 0\nY 1"))`

Of course you can also tell from the length of the returned list, but I think warning the user is a good idea.


Patch:
diff --git a/pyquil/api/qvm.py b/pyquil/api/qvm.py
index e93eadb..e436b17 100644
--- a/pyquil/api/qvm.py
+++ b/pyquil/api/qvm.py
@@ -320,14 +320,19 @@ programs run on this QVM.
             generally be different*.
 
         :param Program prep_prog: Quil program for state preparation.
-        :param list operator_programs: A list of PauliTerms. Default is Identity operator.
+        :param list operator_programs: A list of Programs, each specifying an operator whose expectation to compute.
+         Default is a list containing only the empty Program.
         :param bool needs_compilation: If True, preprocesses the job with the compiler.
         :param ISA isa: If set, compiles to this target ISA.
         :returns: Expectation value of the operators.
         :rtype: float
         """
+        if isinstance(operator_programs, Program):
+            warnings.warn("You have provided a Program rather than a list of Programs. The results from expectation "
+                          "will be line-wise expectation values of the operator_programs.", SyntaxWarning)
         if needs_compilation:
-            raise TypeError("Expectation QVM programs do not support compilation preprocessing.  Make a separate CompilerConnection job first.")
+            raise TypeError("Expectation QVM programs do not support compilation preprocessing."
+                            "  Make a separate CompilerConnection job first.")
         if self.use_queue:
             payload = self._expectation_payload(prep_prog, operator_programs)
             response = post_json(self.session, self.async_endpoint + "/job", {"machine": "QVM", "program": payload})
diff --git a/pyquil/quil.py b/pyquil/quil.py
index 546537b..a9c055e 100644
--- a/pyquil/quil.py
+++ b/pyquil/quil.py
@@ -433,6 +433,8 @@ class Program(object):
 
         for gate in self._defined_gates:
             if inv_dict is None or gate.name not in inv_dict:
+                if gate.parameters:
+                    raise TypeError("Cannot auto define daggered version of parameterized gates")
                 daggered.defgate(gate.name + suffix, gate.matrix.T.conj())
 
         for gate in reversed(self._instructions):
@@ -452,7 +454,7 @@ class Program(object):
                 else:
                     gate_inv_name = inv_dict[gate.name]
 
-                daggered.inst(tuple([gate_inv_name] + gate.qubits))
+                daggered.inst(Gate(gate_inv_name, gate.params, gate.qubits))
 
         return daggered
 


Test patch:
diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py
index 3180427..4edddaf 100644
--- a/pyquil/tests/test_quil.py
+++ b/pyquil/tests/test_quil.py
@@ -275,6 +275,20 @@ def test_dagger():
     p = Program().defgate("G", G).inst(("G", 0))
     assert p.dagger(inv_dict=inv_dict).out() == 'J 0\n'
 
+    # defined parameterized gates cannot auto generate daggered version https://github.com/rigetticomputing/pyquil/issues/304
+    theta = Parameter('theta')
+    gparam_matrix = np.array([[quil_cos(theta / 2), -1j * quil_sin(theta / 2)],
+                             [-1j * quil_sin(theta / 2), quil_cos(theta / 2)]])
+    g_param_def = DefGate('GPARAM', gparam_matrix, [theta])
+    p = Program(g_param_def)
+    with pytest.raises(TypeError):
+        p.dagger()
+
+    # defined parameterized gates should passback parameters https://github.com/rigetticomputing/pyquil/issues/304
+    GPARAM = g_param_def.get_constructor()
+    p = Program(GPARAM(pi)(1, 2))
+    assert p.dagger().out() == 'GPARAM-INV(pi) 1 2\n'
+
 
 def test_construction_syntax():
     p = Program().inst(X(0), Y(1), Z(0)).measure(0, 1)