def test_power_except(self): """Test power method raises exceptions.""" chan = Chi(self.depol_chi(1)) # Negative power raises error self.assertRaises(QiskitError, chan.power, -1) # 0 power raises error self.assertRaises(QiskitError, chan.power, 0) # Non-integer power raises error self.assertRaises(QiskitError, chan.power, 0.5)
def test_init(self): """Test initialization""" mat4 = np.eye(4) / 2.0 chan = Chi(mat4) self.assertAllClose(chan.data, mat4) self.assertEqual(chan.dims, (2, 2)) mat16 = np.eye(16) / 4 chan = Chi(mat16) self.assertAllClose(chan.data, mat16) self.assertEqual(chan.dims, (4, 4)) # Wrong input or output dims should raise exception self.assertRaises(QiskitError, Chi, mat16, input_dim=2, output_dim=4) # Non multi-qubit dimensions should raise exception self.assertRaises( QiskitError, Chi, np.eye(6) / 2, input_dim=3, output_dim=2)
def test_subtract_inplace(self): """Test inplace subtract method.""" mat1 = 0.5 * self.chiI mat2 = 0.5 * self.depol_chi(1) targ = Chi(mat1 - mat2) chan1 = Chi(mat1) chan2 = Chi(mat2) chan1.subtract(chan2, inplace=True) self.assertEqual(chan1, targ) chan1 = Chi(mat1) chan2 = Chi(mat2) chan1 -= chan2 self.assertEqual(chan1, targ)
def test_add_inplace(self): """Test inplace add method.""" mat1 = 0.5 * self.chiI mat2 = 0.5 * self.depol_chi(1) targ = Chi(mat1 + mat2) chan1 = Chi(mat1) chan2 = Chi(mat2) chan1.add(chan2, inplace=True) self.assertEqual(chan1, targ) chan1 = Chi(mat1) chan2 = Chi(mat2) chan1 += chan2 self.assertEqual(chan1, targ)
def test_from_qiskit(self): """ Test quantum channels created from qiskit are equals to quantum channels created by qiskit -> to myqlm -> to qiskit """ circuit = QuantumCircuit(3) circuit.h(0) circuit.cx(0, 1) circuit.x(2) self.assertEqual( Kraus(circuit), qchannel_to_qiskit(qiskit_to_qchannel(Kraus(circuit)))) self.assertEqual(Chi(circuit), qchannel_to_qiskit(qiskit_to_qchannel(Chi(circuit)))) self.assertEqual(Choi(circuit), qchannel_to_qiskit(qiskit_to_qchannel(Choi(circuit)))) self.assertEqual( SuperOp(circuit), qchannel_to_qiskit(qiskit_to_qchannel(SuperOp(circuit)))) self.assertEqual(PTM(circuit), qchannel_to_qiskit(qiskit_to_qchannel(PTM(circuit))))
def test_multiply_inplace(self): """Test inplace multiply method.""" chan = Chi(self.chiI) val = 0.5 targ = Chi(val * self.chiI) chan.multiply(val, inplace=True) self.assertEqual(chan, targ) chan = Chi(self.chiI) chan *= val self.assertEqual(chan, targ)
def test_from_myqlm_chi(self): """ Test all combinations of chi quantum channel between myqlm and qiskit """ arr = np.arange(64 * 64, dtype=complex).reshape((64, 64)) basis = [array_to_matrix(arr)] qchannel = QuantumChannel(representation=RepresentationType.CHI, arity=3, basis=basis) qiskit_qchannel = Chi(arr) self.assertEqual(qchannel, qiskit_to_qchannel(qchannel_to_qiskit(qchannel))) self.assertEqual(qiskit_qchannel, qchannel_to_qiskit(qchannel)) self.assertEqual(qchannel, qiskit_to_qchannel(qiskit_qchannel))
def test_dot(self): """Test dot method.""" # Random input test state rho = DensityMatrix(self.rand_rho(2)) # UnitaryChannel evolution chan1 = Chi(self.chiX) chan2 = Chi(self.chiY) target = rho.evolve(Chi(self.chiZ)) output = rho.evolve(chan2.dot(chan1)) self.assertEqual(output, target) # Compose random chi1 = self.rand_matrix(4, 4, real=True) chi2 = self.rand_matrix(4, 4, real=True) chan1 = Chi(chi1, input_dims=2, output_dims=2) chan2 = Chi(chi2, input_dims=2, output_dims=2) target = rho.evolve(chan1).evolve(chan2) output = rho.evolve(chan2.dot(chan1)) self.assertEqual(output, target)
def test_add(self): """Test add method.""" mat1 = 0.5 * self.chiI mat2 = 0.5 * self.depol_chi(1) chan1 = Chi(mat1) chan2 = Chi(mat2) targ = Chi(mat1 + mat2) self.assertEqual(chan1._add(chan2), targ) self.assertEqual(chan1 + chan2, targ) targ = Chi(mat1 - mat2) self.assertEqual(chan1 - chan2, targ)
def test_expand(self): """Test expand method.""" # Pauli channels paulis = [self.chiI, self.chiX, self.chiY, self.chiZ] targs = 4 * np.eye(16) # diagonals of Pauli channel Chi mats for i, chi1 in enumerate(paulis): for j, chi2 in enumerate(paulis): chan1 = Chi(chi1) chan2 = Chi(chi2) chan = chan1.expand(chan2) # Target for diagonal Pauli channel targ = Chi(np.diag(targs[i + 4 * j])) self.assertEqual(chan.dim, (4, 4)) self.assertEqual(chan, targ) # Completely depolarizing rho = np.diag([1, 0, 0, 0]) chan_dep = Chi(self.depol_chi(1)) chan = chan_dep.expand(chan_dep) targ = np.diag([1, 1, 1, 1]) / 4 self.assertEqual(chan.dim, (4, 4)) self.assertAllClose(chan._evolve(rho), targ)
def test_negate(self): """Test negate method""" chan = Chi(self.chiI) targ = Chi(-self.chiI) self.assertEqual(-chan, targ)
def test_multiply_except(self): """Test multiply method raises exceptions.""" chan = Chi(self.chiI) self.assertRaises(QiskitError, chan.multiply, 's') self.assertRaises(QiskitError, chan.multiply, chan)
def test_subtract_except(self): """Test subtract method raises exceptions.""" chan1 = Chi(self.chiI) chan2 = Chi(np.eye(16)) self.assertRaises(QiskitError, chan1.subtract, chan2) self.assertRaises(QiskitError, chan1.subtract, 5)
def test_add_except(self): """Test add method raises exceptions.""" chan1 = Chi(self.chiI) chan2 = Chi(np.eye(16)) self.assertRaises(QiskitError, chan1.add, chan2) self.assertRaises(QiskitError, chan1.add, 5)
def test_compose_front(self): """Test front compose method.""" # Random input test state rho = self.rand_rho(2) # UnitaryChannel evolution chan1 = Chi(self.chiX) chan2 = Chi(self.chiY) chan = chan2.compose(chan1, front=True) targ = Chi(self.chiZ)._evolve(rho) self.assertAllClose(chan._evolve(rho), targ) # Compose random chi1 = self.rand_matrix(4, 4, real=True) chi2 = self.rand_matrix(4, 4, real=True) chan1 = Chi(chi1, input_dims=2, output_dims=2) chan2 = Chi(chi2, input_dims=2, output_dims=2) targ = chan2._evolve(chan1._evolve(rho)) chan = chan2.compose(chan1, front=True) self.assertEqual(chan.dim, (2, 2)) self.assertAllClose(chan._evolve(rho), targ)
def test_compose_except(self): """Test compose different dimension exception""" self.assertRaises(QiskitError, Chi(np.eye(4)).compose, Chi(np.eye(16))) self.assertRaises(QiskitError, Chi(np.eye(4)).compose, 2)
def test_compose(self): """Test compose method.""" # Random input test state rho = self.rand_rho(2) # UnitaryChannel evolution chan1 = Chi(self.chiX) chan2 = Chi(self.chiY) chan = chan1.compose(chan2) targ = Chi(self.chiZ)._evolve(rho) self.assertAllClose(chan._evolve(rho), targ) # 50% depolarizing channel chan1 = Chi(self.depol_chi(0.5)) chan = chan1.compose(chan1) targ = Chi(self.depol_chi(0.75))._evolve(rho) self.assertAllClose(chan._evolve(rho), targ) # Compose random chi1 = self.rand_matrix(4, 4, real=True) chi2 = self.rand_matrix(4, 4, real=True) chan1 = Chi(chi1, input_dims=2, output_dims=2) chan2 = Chi(chi2, input_dims=2, output_dims=2) targ = chan2._evolve(chan1._evolve(rho)) chan = chan1.compose(chan2) self.assertEqual(chan.dim, (2, 2)) self.assertAllClose(chan._evolve(rho), targ) chan = chan1 @ chan2 self.assertEqual(chan.dim, (2, 2)) self.assertAllClose(chan._evolve(rho), targ)
def test_tensor_inplace(self): """Test inplace tensor method.""" # Pauli channels paulis = [self.chiI, self.chiX, self.chiY, self.chiZ] targs = 4 * np.eye(16) # diagonals of Pauli channel Chi mats for i, chi1 in enumerate(paulis): for j, chi2 in enumerate(paulis): chan1 = Chi(chi1) chan2 = Chi(chi2) chan2.tensor(chan1, inplace=True) # Target for diagonal Pauli channel targ = Chi(np.diag(targs[i + 4 * j])) self.assertEqual(chan2.dims, (4, 4)) self.assertEqual(chan2, targ) # Test operator overload chan1 = Chi(chi1) chan2 = Chi(chi2) chan2 ^= chan1 self.assertEqual(chan2.dims, (4, 4)) self.assertEqual(chan2, targ) # Completely depolarizing rho = np.diag([1, 0, 0, 0]) chan_dep = Chi(self.depol_chi(1)) chan_dep.tensor(chan_dep, inplace=True) targ = np.diag([1, 1, 1, 1]) / 4 self.assertEqual(chan_dep.dims, (4, 4)) self.assertAllClose(chan_dep._evolve(rho), targ) # Test operator overload chan_dep = Chi(self.depol_chi(1)) chan_dep ^= chan_dep self.assertEqual(chan_dep.dims, (4, 4)) self.assertAllClose(chan_dep._evolve(rho), targ)
def test_evolve(self): """Test evolve method.""" input_psi = [0, 1] input_rho = [[0, 0], [0, 1]] # Identity channel chan = Chi(self.chiI) target_rho = np.array([[0, 0], [0, 1]]) self.assertAllClose(chan._evolve(input_psi), target_rho) self.assertAllClose(chan._evolve(np.array(input_psi)), target_rho) self.assertAllClose(chan._evolve(input_rho), target_rho) self.assertAllClose(chan._evolve(np.array(input_rho)), target_rho) # Hadamard channel chan = Chi(self.chiH) target_rho = np.array([[1, -1], [-1, 1]]) / 2 self.assertAllClose(chan._evolve(input_psi), target_rho) self.assertAllClose(chan._evolve(np.array(input_psi)), target_rho) self.assertAllClose(chan._evolve(input_rho), target_rho) self.assertAllClose(chan._evolve(np.array(input_rho)), target_rho) # Completely depolarizing channel chan = Chi(self.depol_chi(1)) target_rho = np.eye(2) / 2 self.assertAllClose(chan._evolve(input_psi), target_rho) self.assertAllClose(chan._evolve(np.array(input_psi)), target_rho) self.assertAllClose(chan._evolve(input_rho), target_rho) self.assertAllClose(chan._evolve(np.array(input_rho)), target_rho)
def test_circuit_init(self): """Test initialization from a circuit.""" circuit, target = self.simple_circuit_no_measure() op = Chi(circuit) target = Chi(target) self.assertEqual(op, target)
def qchannel_to_qiskit(representation): """ Create a qiskit representation of quantum channel from a myqlm representation of a quantum channel. Args: representation: (QuantumChannel) myqlm representation of a quantum channel. Returns: (Kraus|Choi|Chi|SuperOp|PTM): qiskit representation of a quantum channel. """ rep = representation.representation # Find what representation it is. # Then create the corresponding matrix and shape it like qiskit is expecting it. # Finally, create the qiskit representation from that matrix. if rep in (RepresentationType.PTM, RepresentationType.CHOI): matri = representation.matrix data_re = [] data_im = [] for i in range(matri.nRows): for j in range(matri.nCols): data_re.append(matri.data[i * matri.nRows + j].re + 0.j) data_im.append(matri.data[i * matri.nRows + j].im) data = np.array(data_re) data.imag = np.array(data_im) data = data.reshape((matri.nRows, matri.nCols)) return PTM(data) if (rep == RepresentationType.PTM) else Choi(data) if rep in (RepresentationType.CHI, RepresentationType.SUPEROP): final_data = [] for matri in representation.basis: data_re = [] data_im = [] for i in range(matri.nRows): for j in range(matri.nCols): data_re.append(matri.data[i * matri.nRows + j].re + 0.j) data_im.append(matri.data[i * matri.nRows + j].im) data = np.array(data_re) data.imag = np.array(data_im) data = data.reshape((matri.nRows, matri.nCols)) final_data.append(data) if rep == RepresentationType.CHI: return Chi(final_data) if len(final_data) > 1 else Chi( final_data[0]) return SuperOp(final_data) if len(final_data) > 1 else SuperOp( final_data[0]) if rep == RepresentationType.KRAUS: final_data = [] for matri in representation.kraus_ops: data_re = [] data_im = [] for i in range(matri.nRows): for j in range(matri.nCols): data_re.append(matri.data[i * matri.nRows + j].re + 0.j) data_im.append(matri.data[i * matri.nRows + j].im) data = np.array(data_re) data.imag = np.array(data_im) data = data.reshape((matri.nRows, matri.nCols)) final_data.append(data) return Kraus(final_data) return None
def _transform_by_operator_list( basis_ops: Sequence[Union[QuantumChannel, QuantumError]], target: Union[QuantumChannel, QuantumError]) -> List[float]: r""" Transform (or approximate) the target quantum channel into a mixture of basis operators (channels) and return the mixing probabilities. The approximate channel is found by minimizing the Hilbert-Schmidt distance between the process matrix of the target channel (:math:`T`) and the process matrix of the output channel (:math:`S = \sum_i{p_i S_i}`), i.e. :math:`Tr[(T-S)^\dagger (T-S)]`, where :math:`[p_1, p_2, ..., p_n]` denote probabilities and :math:`[S_1, S_2, ..., S_n]` denote basis operators (channels). Such an optimization can represented as a quadratic program: Minimize :math:`p^T A p + b^T p`, subject to :math:`p \ge 0`, `\sum_i{p_i} = 1`, `\sum_i{p_i} = 1`. The last constraint is for making the approximation conservative by forcing the output channel have more error than the target channel in the sense that a "fidelity" against identity channel should be worse. See `arXiv:1207.0046 <http://arxiv.org/abs/1207.0046>`_ for the details. Args: target: Quantum channel to be transformed. basis_ops: a list of channel objects constructing the output channel. Returns: list: A list of amplitudes (probabilities) of basis that define the output channel. Raises: MissingOptionalLibraryError: if cvxpy is not installed. """ # pylint: disable=invalid-name N = 2**basis_ops[0].num_qubits # add identity channel in front basis_ops = [Kraus(np.eye(N))] + basis_ops # create objective function coefficients basis_ops_mats = [Chi(op).data for op in basis_ops] T = Chi(target).data n = len(basis_ops_mats) A = np.zeros((n, n)) for i, S_i in enumerate(basis_ops_mats): for j, S_j in enumerate(basis_ops_mats): # A[i][j] = 1/2 * {Tr(S_i^† S_j) - Tr(S_j^† S_i)} = Re[Tr(S_i^† S_j)] if i < j: A[i][j] = _hs_inner_prod_real(S_i, S_j) elif i > j: A[i][j] = A[j][i] else: # i==j A[i][i] = _hs_norm(S_i) b = -2 * np.array([_hs_inner_prod_real(T, S_i) for S_i in basis_ops_mats]) # c = _hs_norm(T) # create honesty constraint coefficients def fidelity(channel): # fidelity w.r.t. identity omitting the N^-2 factor return float(np.abs(np.trace(SuperOp(channel)))) source_fids = np.array([fidelity(op) for op in basis_ops]) target_fid = fidelity(target) try: import cvxpy except ImportError as err: logger.error("cvxpy module needs to be installed to use this feature.") raise MissingOptionalLibraryError( libname="cvxpy", name="Transformation/Approximation of noise", pip_install="pip install cvxpy", msg="CVXPY is required to solve an optimization problem of" " approximating a noise channel.") from err # create quadratic program x = cvxpy.Variable(n) prob = cvxpy.Problem(cvxpy.Minimize(cvxpy.quad_form(x, A) + b.T @ x), constraints=[ cvxpy.sum(x) <= 1, x >= 0, source_fids.T @ x <= target_fid ]) # solve quadratic program prob.solve() probabilities = x.value return probabilities
def test_equal(self): """Test __eq__ method""" mat = self.rand_matrix(4, 4, real=True) self.assertEqual(Chi(mat), Chi(mat))
def test_sub_qargs(self): """Test subtract method with qargs.""" mat = self.rand_matrix(8**2, 8**2) mat0 = self.rand_matrix(4, 4) mat1 = self.rand_matrix(4, 4) op = Chi(mat) op0 = Chi(mat0) op1 = Chi(mat1) op01 = op1.tensor(op0) eye = Chi(self.chiI) with self.subTest(msg='qargs=[0]'): value = op - op0([0]) target = op - eye.tensor(eye).tensor(op0) self.assertEqual(value, target) with self.subTest(msg='qargs=[1]'): value = op - op0([1]) target = op - eye.tensor(op0).tensor(eye) self.assertEqual(value, target) with self.subTest(msg='qargs=[2]'): value = op - op0([2]) target = op - op0.tensor(eye).tensor(eye) self.assertEqual(value, target) with self.subTest(msg='qargs=[0, 1]'): value = op - op01([0, 1]) target = op - eye.tensor(op1).tensor(op0) self.assertEqual(value, target) with self.subTest(msg='qargs=[1, 0]'): value = op - op01([1, 0]) target = op - eye.tensor(op0).tensor(op1) self.assertEqual(value, target) with self.subTest(msg='qargs=[0, 2]'): value = op - op01([0, 2]) target = op - op1.tensor(eye).tensor(op0) self.assertEqual(value, target) with self.subTest(msg='qargs=[2, 0]'): value = op - op01([2, 0]) target = op - op0.tensor(eye).tensor(op1) self.assertEqual(value, target)
def test_is_cptp(self): """Test is_cptp method.""" self.assertTrue(Chi(self.depol_chi(0.25)).is_cptp()) # Non-CPTP should return false self.assertFalse( Chi(1.25 * self.chiI - 0.25 * self.depol_chi(1)).is_cptp())
def test_compose(self): """Test compose method.""" # Random input test state rho = DensityMatrix(self.rand_rho(2)) # UnitaryChannel evolution chan1 = Chi(self.chiX) chan2 = Chi(self.chiY) chan = chan1.compose(chan2) target = rho.evolve(Chi(self.chiZ)) output = rho.evolve(chan) self.assertEqual(output, target) # 50% depolarizing channel chan1 = Chi(self.depol_chi(0.5)) chan = chan1.compose(chan1) target = rho.evolve(Chi(self.depol_chi(0.75))) output = rho.evolve(chan) self.assertEqual(output, target) # Compose random chi1 = self.rand_matrix(4, 4, real=True) chi2 = self.rand_matrix(4, 4, real=True) chan1 = Chi(chi1, input_dims=2, output_dims=2) chan2 = Chi(chi2, input_dims=2, output_dims=2) target = rho.evolve(chan1).evolve(chan2) chan = chan1.compose(chan2) output = rho.evolve(chan) self.assertEqual(chan.dim, (2, 2)) self.assertEqual(output, target) chan = chan1 @ chan2 output = rho.evolve(chan) self.assertEqual(chan.dim, (2, 2)) self.assertEqual(output, target)