diff --git a/SECURITY.md b/SECURITY.md index 1a8e9cea671e..45e6a9d6f516 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -15,13 +15,13 @@ We provide more detail on [the release and support schedule of Qiskit in our doc ## Reporting a Vulnerability To report vulnerabilities, you can privately report a potential security issue -via the Github security vulnerabilities feature. This can be done here: +via the GitHub security vulnerabilities feature. This can be done here: https://github.com/Qiskit/qiskit/security/advisories Please do **not** open a public issue about a potential security vulnerability. -You can find more details on the security vulnerability feature in the Github +You can find more details on the security vulnerability feature in the GitHub documentation here: https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability diff --git a/pyproject.toml b/pyproject.toml index 45725f51bb48..35a14a5524ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -217,8 +217,6 @@ disable = [ # remove from here and fix the issues. Else, move it above this section and add a comment # with the rationale "arguments-renamed", - "broad-exception-raised", - "consider-using-dict-items", "consider-using-enumerate", "consider-using-f-string", "no-member", @@ -228,7 +226,6 @@ disable = [ "unnecessary-dunder-call", "unnecessary-lambda-assignment", "unspecified-encoding", - "unsupported-assignment-operation", ] enable = [ diff --git a/qiskit/dagcircuit/collect_blocks.py b/qiskit/dagcircuit/collect_blocks.py index ea574536f45a..c5c7b49144f7 100644 --- a/qiskit/dagcircuit/collect_blocks.py +++ b/qiskit/dagcircuit/collect_blocks.py @@ -288,8 +288,8 @@ def run(self, block): self.group[self.find_leader(first)].append(node) blocks = [] - for index in self.leader: - if self.leader[index] == index: + for index, item in self.leader.items(): + if index == item: blocks.append(self.group[index]) return blocks diff --git a/qiskit/providers/basic_provider/basic_simulator.py b/qiskit/providers/basic_provider/basic_simulator.py index b03a8df7ae5a..978e1dad56fd 100644 --- a/qiskit/providers/basic_provider/basic_simulator.py +++ b/qiskit/providers/basic_provider/basic_simulator.py @@ -528,13 +528,13 @@ def run( from qiskit.compiler import assemble out_options = {} - for key in backend_options: + for key, value in backend_options.items(): if not hasattr(self.options, key): warnings.warn( "Option %s is not used by this backend" % key, UserWarning, stacklevel=2 ) else: - out_options[key] = backend_options[key] + out_options[key] = value qobj = assemble(run_input, self, **out_options) qobj_options = qobj.config self._set_options(qobj_config=qobj_options, backend_options=backend_options) diff --git a/qiskit/providers/models/backendconfiguration.py b/qiskit/providers/models/backendconfiguration.py index e346e293a38b..ebd0a6d9bbb6 100644 --- a/qiskit/providers/models/backendconfiguration.py +++ b/qiskit/providers/models/backendconfiguration.py @@ -892,9 +892,9 @@ def get_qubit_channels(self, qubit: Union[int, Iterable[int]]) -> List[Channel]: channels = set() try: if isinstance(qubit, int): - for key in self._qubit_channel_map.keys(): + for key, value in self._qubit_channel_map.items(): if qubit in key: - channels.update(self._qubit_channel_map[key]) + channels.update(value) if len(channels) == 0: raise KeyError elif isinstance(qubit, list): diff --git a/qiskit/providers/options.py b/qiskit/providers/options.py index 7a5b7a260354..659af518a2b7 100644 --- a/qiskit/providers/options.py +++ b/qiskit/providers/options.py @@ -229,28 +229,28 @@ def set_validator(self, field, validator_value): f"{type(validator_value)} is not a valid validator type, it " "must be a tuple, list, or class/type" ) - self.validator[field] = validator_value + self.validator[field] = validator_value # pylint: disable=unsupported-assignment-operation def update_options(self, **fields): """Update options with kwargs""" - for field in fields: - field_validator = self.validator.get(field, None) + for field_name, field in fields.items(): + field_validator = self.validator.get(field_name, None) if isinstance(field_validator, tuple): - if fields[field] > field_validator[1] or fields[field] < field_validator[0]: + if field > field_validator[1] or field < field_validator[0]: raise ValueError( - f"Specified value for '{field}' is not a valid value, " + f"Specified value for '{field_name}' is not a valid value, " f"must be >={field_validator[0]} or <={field_validator[1]}" ) elif isinstance(field_validator, list): - if fields[field] not in field_validator: + if field not in field_validator: raise ValueError( - f"Specified value for {field} is not a valid choice, " + f"Specified value for {field_name} is not a valid choice, " f"must be one of {field_validator}" ) elif isinstance(field_validator, type): - if not isinstance(fields[field], field_validator): + if not isinstance(field, field_validator): raise TypeError( - f"Specified value for {field} is not of required type {field_validator}" + f"Specified value for {field_name} is not of required type {field_validator}" ) self._fields.update(fields) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 9041dbc19510..b076bcf56cb0 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -677,8 +677,8 @@ def __eq__(self, other: object) -> bool: if not np.isclose(complex_amp1, complex_amp2): return False - for key in self.parameters: - if key not in ["amp", "angle"] and self.parameters[key] != other.parameters[key]: + for key, value in self.parameters.items(): + if key not in ["amp", "angle"] and value != other.parameters[key]: return False return True diff --git a/qiskit/quantum_info/operators/symplectic/pauli.py b/qiskit/quantum_info/operators/symplectic/pauli.py index 1ccecc04a6c0..8187c1ee41e6 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli.py +++ b/qiskit/quantum_info/operators/symplectic/pauli.py @@ -736,8 +736,11 @@ def apply_layout( n_qubits = num_qubits if layout is None: layout = list(range(self.num_qubits)) - elif any(x >= n_qubits for x in layout): - raise QiskitError("Provided layout contains indices outside the number of qubits.") + else: + if any(x < 0 or x >= n_qubits for x in layout): + raise QiskitError("Provided layout contains indices outside the number of qubits.") + if len(set(layout)) != len(layout): + raise QiskitError("Provided layout contains duplicate indices.") new_op = type(self)("I" * n_qubits) return new_op.compose(self, qargs=layout) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index dffe5b2396b2..cf51579bef8f 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -1139,7 +1139,6 @@ def apply_layout( specified will be applied without any expansion. If layout is None, the operator will be expanded to the given number of qubits. - Returns: A new :class:`.SparsePauliOp` with the provided layout applied """ @@ -1159,10 +1158,13 @@ def apply_layout( f"applied to a {n_qubits} qubit operator" ) n_qubits = num_qubits - if layout is not None and any(x >= n_qubits for x in layout): - raise QiskitError("Provided layout contains indices outside the number of qubits.") if layout is None: layout = list(range(self.num_qubits)) + else: + if any(x < 0 or x >= n_qubits for x in layout): + raise QiskitError("Provided layout contains indices outside the number of qubits.") + if len(set(layout)) != len(layout): + raise QiskitError("Provided layout contains duplicate indices.") new_op = type(self)("I" * n_qubits) return new_op.compose(self, qargs=layout) diff --git a/qiskit/quantum_info/quaternion.py b/qiskit/quantum_info/quaternion.py index 22508b5ceb1e..69b9b61c8f99 100644 --- a/qiskit/quantum_info/quaternion.py +++ b/qiskit/quantum_info/quaternion.py @@ -43,7 +43,7 @@ def __mul__(self, r): out_data[3] = r(0) * q(3) - r(1) * q(2) + r(2) * q(1) + r(3) * q(0) return Quaternion(out_data) else: - raise Exception("Multiplication by other not supported.") + return NotImplemented def norm(self): """Norm of quaternion.""" diff --git a/qiskit/result/models.py b/qiskit/result/models.py index 07286148f886..992810196713 100644 --- a/qiskit/result/models.py +++ b/qiskit/result/models.py @@ -171,11 +171,11 @@ def __repr__(self): out += ", seed=%s" % self.seed if hasattr(self, "meas_return"): out += ", meas_return=%s" % self.meas_return - for key in self._metadata: - if isinstance(self._metadata[key], str): - value_str = "'%s'" % self._metadata[key] + for key, value in self._metadata.items(): + if isinstance(value, str): + value_str = "'%s'" % value else: - value_str = repr(self._metadata[key]) + value_str = repr(value) out += f", {key}={value_str}" out += ")" return out diff --git a/qiskit/result/result.py b/qiskit/result/result.py index d99be996080e..c1792de56ae0 100644 --- a/qiskit/result/result.py +++ b/qiskit/result/result.py @@ -81,11 +81,11 @@ def __repr__(self): ) ) out += f", date={self.date}, status={self.status}, header={self.header}" - for key in self._metadata: - if isinstance(self._metadata[key], str): - value_str = "'%s'" % self._metadata[key] + for key, value in self._metadata.items(): + if isinstance(value, str): + value_str = "'%s'" % value else: - value_str = repr(self._metadata[key]) + value_str = repr(value) out += f", {key}={value_str}" out += ")" return out diff --git a/qiskit/synthesis/clifford/clifford_decompose_bm.py b/qiskit/synthesis/clifford/clifford_decompose_bm.py index 50ffcb743162..4800890a90d3 100644 --- a/qiskit/synthesis/clifford/clifford_decompose_bm.py +++ b/qiskit/synthesis/clifford/clifford_decompose_bm.py @@ -192,7 +192,7 @@ def _cx_cost(clifford): return _cx_cost2(clifford) if clifford.num_qubits == 3: return _cx_cost3(clifford) - raise Exception("No Clifford CX cost function for num_qubits > 3.") + raise RuntimeError("No Clifford CX cost function for num_qubits > 3.") def _rank2(a, b, c, d): diff --git a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py index 51b39d7e961b..e0dd61ff6cf4 100644 --- a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py +++ b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py @@ -218,8 +218,8 @@ def collect_key(x): prev = bit self.gate_groups[self.find_set(prev)].append(nd) # need to turn all groups that still exist into their own blocks - for index in self.parent: - if self.parent[index] == index and len(self.gate_groups[index]) != 0: + for index, item in self.parent.items(): + if item == index and len(self.gate_groups[index]) != 0: block_list.append(self.gate_groups[index][:]) self.property_set["block_list"] = block_list diff --git a/qiskit/visualization/bloch.py b/qiskit/visualization/bloch.py index bae0633a811c..513d2ddff85e 100644 --- a/qiskit/visualization/bloch.py +++ b/qiskit/visualization/bloch.py @@ -290,7 +290,7 @@ def set_label_convention(self, convention): self.zlabel = ["$\\circlearrowleft$", "$\\circlearrowright$"] self.xlabel = ["$\\leftrightarrow$", "$\\updownarrow$"] else: - raise Exception("No such convention.") + raise ValueError("No such convention.") def __str__(self): string = "" @@ -396,7 +396,7 @@ def add_annotation(self, state_or_vector, text, **kwargs): if isinstance(state_or_vector, (list, np.ndarray, tuple)) and len(state_or_vector) == 3: vec = state_or_vector else: - raise Exception("Position needs to be specified by a qubit " + "state or a 3D vector.") + raise TypeError("Position needs to be specified by a qubit state or a 3D vector.") self.annotations.append({"position": vec, "text": text, "opts": kwargs}) def make_sphere(self): diff --git a/qiskit/visualization/circuit/latex.py b/qiskit/visualization/circuit/latex.py index ad4b8e070e13..9341126bcd1a 100644 --- a/qiskit/visualization/circuit/latex.py +++ b/qiskit/visualization/circuit/latex.py @@ -213,17 +213,22 @@ def _initialize_latex_array(self): self._latex.append([" "] * (self._img_depth + 1)) # display the bit/register labels - for wire in self._wire_map: + for wire, index in self._wire_map.items(): if isinstance(wire, ClassicalRegister): register = wire - index = self._wire_map[wire] + wire_label = get_wire_label( + "latex", register, index, layout=self._layout, cregbundle=self._cregbundle + ) else: register, bit_index, reg_index = get_bit_reg_index(self._circuit, wire) - index = bit_index if register is None else reg_index + wire_label = get_wire_label( + "latex", + register, + bit_index if register is None else reg_index, + layout=self._layout, + cregbundle=self._cregbundle, + ) - wire_label = get_wire_label( - "latex", register, index, layout=self._layout, cregbundle=self._cregbundle - ) wire_label += " : " if self._initial_state: wire_label += "\\ket{{0}}" if isinstance(wire, Qubit) else "0" @@ -234,7 +239,7 @@ def _initialize_latex_array(self): self._latex[pos][1] = "\\lstick{/_{_{" + str(register.size) + "}}} \\cw" wire_label = f"\\mathrm{{{wire_label}}}" else: - pos = self._wire_map[wire] + pos = index self._latex[pos][0] = "\\nghost{" + wire_label + " & " + "\\lstick{" + wire_label def _get_image_depth(self): @@ -620,11 +625,11 @@ def _add_condition(self, op, wire_list, col): # First sort the val_bits in the order of the register bits in the circuit cond_wires = [] cond_bits = [] - for wire in self._wire_map: + for wire, index in self._wire_map.items(): reg, _, reg_index = get_bit_reg_index(self._circuit, wire) if reg == cond_reg: cond_bits.append(reg_index) - cond_wires.append(self._wire_map[wire]) + cond_wires.append(index) gap = cond_wires[0] - max(wire_list) prev_wire = cond_wires[0] diff --git a/qiskit/visualization/circuit/text.py b/qiskit/visualization/circuit/text.py index abefe5511775..c2846da0b758 100644 --- a/qiskit/visualization/circuit/text.py +++ b/qiskit/visualization/circuit/text.py @@ -894,10 +894,9 @@ def wire_names(self, with_initial_state=False): self._wire_map = get_wire_map(self._circuit, (self.qubits + self.clbits), self.cregbundle) wire_labels = [] - for wire in self._wire_map: + for wire, index in self._wire_map.items(): if isinstance(wire, ClassicalRegister): register = wire - index = self._wire_map[wire] else: register, bit_index, reg_index = get_bit_reg_index(self._circuit, wire) index = bit_index if register is None else reg_index diff --git a/qiskit/visualization/transition_visualization.py b/qiskit/visualization/transition_visualization.py index f322be64a4f5..a2ff74799999 100644 --- a/qiskit/visualization/transition_visualization.py +++ b/qiskit/visualization/transition_visualization.py @@ -72,10 +72,10 @@ def __mul__(self, b): return self._multiply_with_quaternion(b) elif isinstance(b, (list, tuple, np.ndarray)): if len(b) != 3: - raise Exception(f"Input vector has invalid length {len(b)}") + raise ValueError(f"Input vector has invalid length {len(b)}") return self._multiply_with_vector(b) else: - raise Exception(f"Multiplication with unknown type {type(b)}") + return NotImplemented def _multiply_with_quaternion(self, q_2): """Multiplication of quaternion with quaternion""" diff --git a/releasenotes/notes/fix-apply-layout-duplicate-negative-indices-cf5517921fe52706.yaml b/releasenotes/notes/fix-apply-layout-duplicate-negative-indices-cf5517921fe52706.yaml new file mode 100644 index 000000000000..9fbe0ffd9c79 --- /dev/null +++ b/releasenotes/notes/fix-apply-layout-duplicate-negative-indices-cf5517921fe52706.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed :meth:`.SparsePauliOp.apply_layout` and :meth:`.Pauli.apply_layout` + to raise :exc:`.QiskitError` if duplicate indices or negative indices are provided + as part of a layout. diff --git a/test/benchmarks/utils.py b/test/benchmarks/utils.py index af8f3318074a..d932e8d6a0c5 100644 --- a/test/benchmarks/utils.py +++ b/test/benchmarks/utils.py @@ -71,7 +71,7 @@ def random_circuit( Exception: when invalid options given """ if max_operands < 1 or max_operands > 3: - raise Exception("max_operands must be between 1 and 3") + raise ValueError("max_operands must be between 1 and 3") one_q_ops = [ IGate, diff --git a/test/python/primitives/test_backend_sampler_v2.py b/test/python/primitives/test_backend_sampler_v2.py index 9f6c007b1d5b..b03818846c82 100644 --- a/test/python/primitives/test_backend_sampler_v2.py +++ b/test/python/primitives/test_backend_sampler_v2.py @@ -640,9 +640,9 @@ def test_circuit_with_aliased_cregs(self, backend): self.assertEqual(len(result), 1) data = result[0].data self.assertEqual(len(data), 3) - for creg_name in target: + for creg_name, creg in target.items(): self.assertTrue(hasattr(data, creg_name)) - self._assert_allclose(getattr(data, creg_name), np.array(target[creg_name])) + self._assert_allclose(getattr(data, creg_name), np.array(creg)) @combine(backend=BACKENDS) def test_no_cregs(self, backend): diff --git a/test/python/primitives/test_statevector_sampler.py b/test/python/primitives/test_statevector_sampler.py index de17b2824075..c065871025d7 100644 --- a/test/python/primitives/test_statevector_sampler.py +++ b/test/python/primitives/test_statevector_sampler.py @@ -606,9 +606,9 @@ def test_circuit_with_aliased_cregs(self): self.assertEqual(len(result), 1) data = result[0].data self.assertEqual(len(data), 3) - for creg_name in target: + for creg_name, creg in target.items(): self.assertTrue(hasattr(data, creg_name)) - self._assert_allclose(getattr(data, creg_name), np.array(target[creg_name])) + self._assert_allclose(getattr(data, creg_name), np.array(creg)) def test_no_cregs(self): """Test that the sampler works when there are no classical register in the circuit.""" diff --git a/test/python/quantum_info/operators/symplectic/test_pauli.py b/test/python/quantum_info/operators/symplectic/test_pauli.py index 875dd9237810..89324e8212e1 100644 --- a/test/python/quantum_info/operators/symplectic/test_pauli.py +++ b/test/python/quantum_info/operators/symplectic/test_pauli.py @@ -606,6 +606,18 @@ def test_apply_layout_null_layout_invalid_num_qubits(self): with self.assertRaises(QiskitError): op.apply_layout(layout=None, num_qubits=1) + def test_apply_layout_negative_indices(self): + """Test apply_layout with negative indices""" + op = Pauli("IZ") + with self.assertRaises(QiskitError): + op.apply_layout(layout=[-1, 0], num_qubits=3) + + def test_apply_layout_duplicate_indices(self): + """Test apply_layout with duplicate indices""" + op = Pauli("IZ") + with self.assertRaises(QiskitError): + op.apply_layout(layout=[0, 0], num_qubits=3) + if __name__ == "__main__": unittest.main() diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py index 330fd53bc35d..1149ef1f3466 100644 --- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py +++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py @@ -1179,6 +1179,18 @@ def test_apply_layout_null_layout_invalid_num_qubits(self): with self.assertRaises(QiskitError): op.apply_layout(layout=None, num_qubits=1) + def test_apply_layout_negative_indices(self): + """Test apply_layout with negative indices""" + op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) + with self.assertRaises(QiskitError): + op.apply_layout(layout=[-1, 0], num_qubits=3) + + def test_apply_layout_duplicate_indices(self): + """Test apply_layout with duplicate indices""" + op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) + with self.assertRaises(QiskitError): + op.apply_layout(layout=[0, 0], num_qubits=3) + if __name__ == "__main__": unittest.main() diff --git a/test/python/quantum_info/test_quaternions.py b/test/python/quantum_info/test_quaternions.py index 48e2ead8b894..d838ee2d1b52 100644 --- a/test/python/quantum_info/test_quaternions.py +++ b/test/python/quantum_info/test_quaternions.py @@ -92,12 +92,14 @@ def test_mul_by_quat(self): def test_mul_by_array(self): """Quaternions cannot be multiplied with an array.""" other_array = np.array([0.1, 0.2, 0.3, 0.4]) - self.assertRaises(Exception, self.quat_unnormalized.__mul__, other_array) + with self.assertRaises(TypeError): + _ = self.quat_unnormalized * other_array def test_mul_by_scalar(self): """Quaternions cannot be multiplied with a scalar.""" other_scalar = 0.123456789 - self.assertRaises(Exception, self.quat_unnormalized.__mul__, other_scalar) + with self.assertRaises(TypeError): + _ = self.quat_unnormalized * other_scalar def test_rotation(self): """Multiplication by -1 should give the same rotation.""" diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 247aa82ec039..c00208a2ffaf 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -71,7 +71,7 @@ def mock_get_passmanager_stage( elif stage_name == "layout": return PassManager([]) else: - raise Exception("Failure, unexpected stage plugin combo for test") + raise RuntimeError("Failure, unexpected stage plugin combo for test") def emptycircuit(): diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index a9fec85be665..5315c4b8e018 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -1346,7 +1346,7 @@ def _visit_block(circuit, qubit_mapping=None): qargs = tuple(qubit_mapping[x] for x in instruction.qubits) if not isinstance(instruction.operation, ControlFlowOp): if len(qargs) > 2 or len(qargs) < 0: - raise Exception("Invalid number of qargs for instruction") + raise RuntimeError("Invalid number of qargs for instruction") if len(qargs) == 2: self.assertIn(qargs, self.coupling_edge_set) else: diff --git a/test/python/transpiler/test_stochastic_swap.py b/test/python/transpiler/test_stochastic_swap.py index df5948ed715b..5b924e590a54 100644 --- a/test/python/transpiler/test_stochastic_swap.py +++ b/test/python/transpiler/test_stochastic_swap.py @@ -1506,7 +1506,7 @@ def _visit_block(circuit, qubit_mapping=None): qargs = tuple(qubit_mapping[x] for x in instruction.qubits) if not isinstance(instruction.operation, ControlFlowOp): if len(qargs) > 2 or len(qargs) < 0: - raise Exception("Invalid number of qargs for instruction") + raise RuntimeError("Invalid number of qargs for instruction") if len(qargs) == 2: self.assertIn(qargs, self.coupling_edge_set) else: