diff --git a/qiskit/quantum_info/operators/mixins/multiply.py b/qiskit/quantum_info/operators/mixins/multiply.py index 2ae3ec3e2f4d..ad3de1f1c6a5 100644 --- a/qiskit/quantum_info/operators/mixins/multiply.py +++ b/qiskit/quantum_info/operators/mixins/multiply.py @@ -35,6 +35,9 @@ class MultiplyMixin(ABC): def __rmul__(self, other): return self._multiply(other) + def __mul__(self, other): + return self._multiply(other) + def __truediv__(self, other): return self._multiply(1 / other) diff --git a/releasenotes/notes/scaler-multiplication-left-side-7bea0d73f9afabe2.yaml b/releasenotes/notes/scaler-multiplication-left-side-7bea0d73f9afabe2.yaml new file mode 100644 index 000000000000..11727a657785 --- /dev/null +++ b/releasenotes/notes/scaler-multiplication-left-side-7bea0d73f9afabe2.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Classes in the :mod:`.quantum_info` module that support scalar multiplication + can now be multiplied by a scalar from either the left or the right. + Previously, they would only accept scalar multipliers from the left. diff --git a/test/python/quantum_info/operators/channel/test_chi.py b/test/python/quantum_info/operators/channel/test_chi.py index 66240f7ce4a3..2bd5613510e1 100644 --- a/test/python/quantum_info/operators/channel/test_chi.py +++ b/test/python/quantum_info/operators/channel/test_chi.py @@ -363,6 +363,8 @@ def test_multiply(self): targ = Chi(val * self.chiI) self.assertEqual(chan._multiply(val), targ) self.assertEqual(val * chan, targ) + targ = Chi(self.chiI * val) + self.assertEqual(chan * val, targ) def test_multiply_except(self): """Test multiply method raises exceptions.""" diff --git a/test/python/quantum_info/operators/channel/test_choi.py b/test/python/quantum_info/operators/channel/test_choi.py index 1ef859a9a0f1..daa1a1cd18ac 100644 --- a/test/python/quantum_info/operators/channel/test_choi.py +++ b/test/python/quantum_info/operators/channel/test_choi.py @@ -450,6 +450,8 @@ def test_multiply(self): targ = Choi(val * self.choiI) self.assertEqual(chan._multiply(val), targ) self.assertEqual(val * chan, targ) + targ = Choi(self.choiI * val) + self.assertEqual(chan * val, targ) def test_multiply_except(self): """Test multiply method raises exceptions.""" diff --git a/test/python/quantum_info/operators/channel/test_kraus.py b/test/python/quantum_info/operators/channel/test_kraus.py index 5e589878d0bf..5d50ee9b4759 100644 --- a/test/python/quantum_info/operators/channel/test_kraus.py +++ b/test/python/quantum_info/operators/channel/test_kraus.py @@ -419,6 +419,9 @@ def test_multiply(self): self.assertEqual(rho & chan, targ) chan = val * chan1 self.assertEqual(rho & chan, targ) + targ = (rho & chan1) * val + chan = chan1 * val + self.assertEqual(rho & chan, targ) # Double Kraus set chan2 = Kraus((kraus1, kraus2)) diff --git a/test/python/quantum_info/operators/channel/test_ptm.py b/test/python/quantum_info/operators/channel/test_ptm.py index 51660e495a0c..bca1d4173071 100644 --- a/test/python/quantum_info/operators/channel/test_ptm.py +++ b/test/python/quantum_info/operators/channel/test_ptm.py @@ -356,6 +356,8 @@ def test_multiply(self): targ = PTM(val * self.ptmI) self.assertEqual(chan._multiply(val), targ) self.assertEqual(val * chan, targ) + targ = PTM(self.ptmI * val) + self.assertEqual(chan * val, targ) def test_multiply_except(self): """Test multiply method raises exceptions.""" diff --git a/test/python/quantum_info/operators/channel/test_stinespring.py b/test/python/quantum_info/operators/channel/test_stinespring.py index d809009c23cf..9bcc886a026c 100644 --- a/test/python/quantum_info/operators/channel/test_stinespring.py +++ b/test/python/quantum_info/operators/channel/test_stinespring.py @@ -411,6 +411,9 @@ def test_multiply(self): self.assertEqual(rho_init.evolve(chan), rho_targ) chan = val * chan1 self.assertEqual(rho_init.evolve(chan), rho_targ) + rho_targ = (rho_init & chan1) * val + chan = chan1 * val + self.assertEqual(rho_init.evolve(chan), rho_targ) # Double Stinespring set chan2 = Stinespring((stine1, stine2), input_dims=2, output_dims=4) diff --git a/test/python/quantum_info/operators/channel/test_superop.py b/test/python/quantum_info/operators/channel/test_superop.py index 50297eaf73bf..0d79af03d931 100644 --- a/test/python/quantum_info/operators/channel/test_superop.py +++ b/test/python/quantum_info/operators/channel/test_superop.py @@ -647,6 +647,8 @@ def test_multiply(self): targ = SuperOp(val * self.sopI) self.assertEqual(chan._multiply(val), targ) self.assertEqual(val * chan, targ) + targ = SuperOp(self.sopI * val) + self.assertEqual(chan * val, targ) def test_multiply_except(self): """Test multiply method raises exceptions.""" diff --git a/test/python/quantum_info/operators/symplectic/test_pauli.py b/test/python/quantum_info/operators/symplectic/test_pauli.py index ba08b453c6a4..c952a3a8fb93 100644 --- a/test/python/quantum_info/operators/symplectic/test_pauli.py +++ b/test/python/quantum_info/operators/symplectic/test_pauli.py @@ -336,6 +336,9 @@ def test_multiply(self, val): op = val * Pauli(([True, True], [False, False], 0)) phase = (-1j) ** op.phase self.assertEqual(phase, val) + op = Pauli(([True, True], [False, False], 0)) * val + phase = (-1j) ** op.phase + self.assertEqual(phase, val) def test_multiply_except(self): """Test multiply method raises exceptions.""" 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 81a9a0d103c6..4cb9d50b9d72 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 @@ -450,8 +450,13 @@ def test_mul(self, num_qubits, value): spp_op = self.random_spp_op(num_qubits, 2**num_qubits) target = value * Operator(spp_op) op = value * spp_op - value = op.to_operator() - self.assertEqual(value, target) + value_mat = op.to_operator() + self.assertEqual(value_mat, target) + np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) + target = Operator(spp_op) * value + op = spp_op * value + value_mat = op.to_operator() + self.assertEqual(value_mat, target) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @combine(num_qubits=[1, 2, 3], value=[1, 1j, -3 + 4.4j]) diff --git a/test/python/quantum_info/operators/test_operator.py b/test/python/quantum_info/operators/test_operator.py index 847e816385c6..365b33f1ac9a 100644 --- a/test/python/quantum_info/operators/test_operator.py +++ b/test/python/quantum_info/operators/test_operator.py @@ -635,6 +635,7 @@ def test_multiply(self): op = Operator(mat) self.assertEqual(op._multiply(val), Operator(val * mat)) self.assertEqual(val * op, Operator(val * mat)) + self.assertEqual(op * val, Operator(mat * val)) def test_multiply_except(self): """Test multiply method raises exceptions.""" diff --git a/test/python/quantum_info/operators/test_scalar_op.py b/test/python/quantum_info/operators/test_scalar_op.py index e6a2171b78e9..45079f8d0cc6 100644 --- a/test/python/quantum_info/operators/test_scalar_op.py +++ b/test/python/quantum_info/operators/test_scalar_op.py @@ -152,6 +152,9 @@ def test_multiply(self, coeff1, coeff2): val = coeff2 * ScalarOp(dims, coeff=coeff1) target = coeff1 * coeff2 self.assertScalarOp(val, dims, target) + val = ScalarOp(dims, coeff=coeff1) * coeff2 + target = coeff2 * coeff1 + self.assertScalarOp(val, dims, target) @combine(coeff1=[0, 1, -3.1, 1 + 3j], coeff2=[-1, -5.1 - 2j]) def test_add(self, coeff1, coeff2):