Repository: quantumlib/Cirq

Issue:
Should Circuit#append be chainable?
It seems like a common pattern to want to do
circuit = Circuit().append(o1).append([o2, o3])
?


Discussion:
err well
circuit = Circuit().append(op tree)
(easy to combine first and second for op trees)
Making it chainable makes sense to me, but I find chainable APIs in python to be somewhat non-idiomatic because you have to use explicit line-continuation or an ugly brace style if your chain goes longer than a single line.
I think the "give any iterable and its fine" is sufficient to cover this use case, and I agree with Matthew that chaining is kind of ugly in python.

I vote close.
I agree about chainable in python ugh.

Maybe a static method
circuit = Circuit.from_op_tree(...)?

+1 for static method. Factory functions like that are very useful.
I would call it from_operations. OP_TREE is a concept we want to avoid explaining if possible, and instead say "look it just works!".

Resolving patch:
diff --git a/cirq/sim/google/xmon_simulator.py b/cirq/sim/google/xmon_simulator.py
--- a/cirq/sim/google/xmon_simulator.py
+++ b/cirq/sim/google/xmon_simulator.py
@@ -213,8 +213,10 @@ def simulator_iterator(
                             gate.axis_half_turns))
                 elif isinstance(gate, xmon_gates.XmonMeasurementGate):
                     index = qubit_map[op.qubits[0]]
-                    results = stepper.simulate_measurement(index)
-                    measurements[gate.key].append(results)
+                    result = stepper.simulate_measurement(index)
+                    if gate.invert_result:
+                        result = not result
+                    measurements[gate.key].append(result)
                 else:
                     raise TypeError(
                         'Gate %s is not a gate supported by the xmon simulator.'
@@ -311,7 +313,7 @@ def set_state(self, state: Union[int, np.ndarray]):
         self._stepper.reset_state(state)
 
     def merge_measurements_with(self,
-        last_result: 'TrialResult') -> 'TrialResult':
+                                last_result: 'StepResult') -> 'StepResult':
         """Merges measurement results of last_result into a new Result.
 
         The measurement results are merges such that measurements with duplicate
@@ -327,9 +329,9 @@ def merge_measurements_with(self,
             A new Result, but with merged measurements.
         """
         new_measurements = defaultdict(list)
-        new_measurements.update(last_result.measurements)
-        for key, result_list in self.measurements.items():
-            new_measurements[key].extend(result_list)
+        for d in [self.measurements, last_result.measurements]:
+            for key, result_list in d.items():
+                new_measurements[key].extend(result_list)
         return StepResult(self._stepper, self.qubit_map, new_measurements)
 
 


Test patch:
diff --git a/cirq/sim/google/xmon_simulator_test.py b/cirq/sim/google/xmon_simulator_test.py
--- a/cirq/sim/google/xmon_simulator_test.py
+++ b/cirq/sim/google/xmon_simulator_test.py
@@ -118,26 +118,26 @@ def test_run_state_different_order_of_qubits():
     assert_empty_context(context)
 
 
-def test_run_sharded():
+def test_consistent_seeded_run_sharded():
     circuit = large_circuit()
 
     simulator = xmon_simulator.Simulator()
     context, result = simulator.run(circuit)
     assert result.measurements == {
-        'meas': [False, False, False, True, False, False, True, False, False,
-                 True]}
+        'meas': [True, False, False, True, False, False, True, False, False,
+                 False]}
     assert_empty_context(context)
 
 
-def test_run_no_sharding():
+def test_consistent_seeded_run_no_sharding():
     circuit = large_circuit()
 
     simulator = xmon_simulator.Simulator()
-    context, result = simulator.run(circuit, xmon_simulator.Options(num_shards=1))
+    context, result = simulator.run(circuit,
+                                    xmon_simulator.Options(num_shards=1))
     assert result.measurements == {
-        'meas': [False, False, False, True, False, False, True, False, False,
-                 True]}
-
+        'meas': [True, False, False, True, False, False, True, False, False,
+                 False]}
 
 def test_run_no_sharing_few_qubits():
     np.random.seed(0)
@@ -317,3 +317,26 @@ def test_composite_gates():
     simulator = xmon_simulator.Simulator()
     context, result = simulator.run(circuit)
     assert result.measurements['a'] == [True, True]
+
+
+def test_measurement_order():
+    circuit = Circuit.from_ops(
+        XmonMeasurementGate().on(Q1),
+        X(Q1),
+        XmonMeasurementGate().on(Q1),
+    )
+    _, result = xmon_simulator.Simulator().run(circuit)
+    assert result.measurements[''] == [False, True]
+
+
+def test_inverted_measurement():
+    circuit = Circuit.from_ops(
+        XmonMeasurementGate(invert_result=False)(Q1),
+        X(Q1),
+        XmonMeasurementGate(invert_result=False)(Q1),
+        XmonMeasurementGate(invert_result=True)(Q1),
+        X(Q1),
+        XmonMeasurementGate(invert_result=True)(Q1))
+
+    _, result = xmon_simulator.Simulator().run(circuit)
+    assert result.measurements[''] == [False, True, False, True]