Repository: qutip/qutip

Issue:
QubitCircuit.add_1q_gate missing last qubit
Expected behavior: add_1q_gate applies the specified gate to the whole register.
Actual behavior: add_1q_gate sets end to N-1, and iterates with range(start, end) to apply the single qubit rotations. Since range() does not include the stop, the last qubit is not affected.


Discussion:

Resolving patch:
diff --git a/qutip/qip/circuit.py b/qutip/qip/circuit.py
--- a/qutip/qip/circuit.py
+++ b/qutip/qip/circuit.py
@@ -317,7 +317,7 @@ def add_1q_gate(self, name, start=0, end=None, qubits=None,
         else:
             if end is None:
                 end = self.N - 1
-            for i in range(start, end):
+            for i in range(start, end+1):
                 self.gates.append(Gate(name, targets=i, controls=None,
                                        arg_value=arg_value,
                                        arg_label=arg_label))


Test patch:
diff --git a/qutip/tests/test_qubitcircuit.py b/qutip/tests/test_qubitcircuit.py
--- a/qutip/tests/test_qubitcircuit.py
+++ b/qutip/tests/test_qubitcircuit.py
@@ -159,9 +159,9 @@ def testadjacentgates(self):
 
     def test_add_gate(self):
         """
-        Addition of a gate object directly to a `QubitCircuit` 
+        Addition of a gate object directly to a `QubitCircuit`
         """
-        qc = QubitCircuit(3)
+        qc = QubitCircuit(6)
         qc.add_gate("CNOT", targets=[1], controls=[0])
         test_gate = Gate("RZ", targets=[1], arg_value = 1.570796,
                          arg_label="P")
@@ -169,6 +169,7 @@ def test_add_gate(self):
         qc.add_gate("TOFFOLI", controls=[0, 1], targets=[2])
         qc.add_gate("SNOT", targets=[3])
         qc.add_gate(test_gate, index = [3])
+        qc.add_1q_gate("RY", start = 4, end = 5, arg_value = 1.570796)
 
         # Test explicit gate addition
         assert_(qc.gates[0].name == "CNOT")
@@ -185,6 +186,12 @@ def test_add_gate(self):
         assert_(qc.gates[3].targets == test_gate.targets)
         assert_(qc.gates[3].controls == test_gate.controls)
 
+        # Test adding 1 qubit gate on [start, end] qubits
+        assert_(qc.gates[5].name == "RY")
+        assert_(qc.gates[5].targets == [4])
+        assert_(qc.gates[6].name == "RY")
+        assert_(qc.gates[6].targets == [5])
+
     def test_add_state(self):
         """
         Addition of input and output states to a circuit.
@@ -207,14 +214,14 @@ def test_add_state(self):
         qc1.add_state("A", targets=[1,4,9], state_type="output")
         qc1.add_state("beta", targets=[0], state_type="output")
         assert_(qc1.input_states[0] == None)
-        
+
         assert_(qc1.input_states[2] == "0")
         assert_(qc1.input_states[3] == "0")
         assert_(qc1.input_states[6] == "0")
         assert_(qc1.input_states[1] == "+")
         assert_(qc1.input_states[4] == "+")
 
-        assert_(qc1.output_states[2] == None)        
+        assert_(qc1.output_states[2] == None)
         assert_(qc1.output_states[1] == "A")
         assert_(qc1.output_states[4] == "A")
         assert_(qc1.output_states[9] == "A")
@@ -247,6 +254,7 @@ def test_reverse(self):
         assert_(qc.input_states[2] == None)
         assert_(qc.output_states[1] == "+")
 
+
     def test_user_gate(self):
         """
         User defined gate for QubitCircuit
@@ -286,7 +294,7 @@ def controlled_mat3(arg_value):
             control_value = arg_value
             dim = mat3.dims[0][0]
             return (tensor(fock_dm(2, control_value), mat3) +
-                    tensor(fock_dm(2, 1 - control_value), identity(dim)))        
+                    tensor(fock_dm(2, 1 - control_value), identity(dim)))
 
         qc = QubitCircuit(2, dims=[3, 2])
         qc.user_gates = {"CTRLMAT3": controlled_mat3}