def kraus_error(noise_ops, standard_gates=None, canonical_kraus=False): """ Return a Kraus quantum error channel. Args: noise_ops (list[matrix]): Kraus matrices. standard_gates (bool): DEPRECATED, Check if input matrices are standard gates. canonical_kraus (bool): Convert input Kraus matrices into the canonical Kraus representation (default: False) Returns: QuantumError: The quantum error object. Raises: NoiseError: if error parameters are invalid. """ if not isinstance(noise_ops, (list, tuple)): raise NoiseError("Invalid Kraus error input.") if not noise_ops: raise NoiseError("Kraus error noise_ops must not be empty.") if standard_gates is not None: warnings.warn( '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' ' and will be removed no earlier than 3 months from that release date.', DeprecationWarning, stacklevel=2) kraus = Kraus(noise_ops) if canonical_kraus: # Convert to Choi and back to get canonical Kraus kraus = Kraus(Choi(kraus)) return QuantumError(kraus)
def kraus_error(noise_ops, standard_gates=True, canonical_kraus=False): """ Return a Kraus quantum error channel. Args: noise_ops (list[matrix]): Kraus matrices. standard_gates (bool): Check if input matrices are standard gates. canonical_kraus (bool): Convert input Kraus matrices into the canonical Kraus representation (default: False) Returns: QuantumError: The quantum error object. Raises: NoiseError: if error parameters are invalid. """ if not isinstance(noise_ops, (list, tuple)): raise NoiseError("Invalid Kraus error input.") if not noise_ops: raise NoiseError("Kraus error noise_ops must not be empty.") kraus = Kraus(noise_ops) if canonical_kraus: # Convert to Choi and back to get canonical Kraus kraus = Kraus(Choi(kraus)) return QuantumError(kraus, standard_gates=standard_gates)
def test_is_cptp(self): """Test is_cptp method.""" self.assertTrue(Kraus(self.depol_kraus(0.5)).is_cptp()) self.assertTrue(Kraus(self.UX).is_cptp()) # Non-CPTP should return false self.assertFalse(Kraus(([self.UI], [self.UX])).is_cptp()) self.assertFalse(Kraus(([self.UI, self.UX])).is_cptp())
def test_init(self): """Test initialization""" # Initialize from unitary chan = Kraus(self.UI) self.assertAllClose(chan.data, [self.UI]) self.assertEqual(chan.dim, (2, 2)) # Initialize from Kraus chan = Kraus(self.depol_kraus(0.5)) self.assertAllClose(chan.data, self.depol_kraus(0.5)) self.assertEqual(chan.dim, (2, 2)) # Initialize from Non-CPTP kraus_l, kraus_r = [self.UI, self.UX], [self.UY, self.UZ] chan = Kraus((kraus_l, kraus_r)) self.assertAllClose(chan.data, (kraus_l, kraus_r)) self.assertEqual(chan.dim, (2, 2)) # Initialize with redundant second op chan = Kraus((kraus_l, kraus_l)) self.assertAllClose(chan.data, kraus_l) self.assertEqual(chan.dim, (2, 2)) # Initialize from rectangular kraus = [np.zeros((4, 2))] chan = Kraus(kraus) self.assertAllClose(chan.data, kraus) self.assertEqual(chan.dim, (2, 4)) # Wrong input or output dims should raise exception self.assertRaises(QiskitError, Kraus, kraus, input_dims=4, output_dims=4)
def test_transform(self): X = self.ops['X'] Y = self.ops['Y'] Z = self.ops['Z'] p = 0.34 theta = numpy.pi / 7 E0 = numpy.sqrt(1 - p) * numpy.array(numpy.eye(2)) E1 = numpy.sqrt(p) * (numpy.cos(theta) * numpy.array(X) + numpy.sin(theta) * numpy.array(Y)) results_dict = approximate_quantum_error(Kraus([E0, E1]), operator_dict={ "X": X, "Y": Y, "Z": Z }) results_string = approximate_quantum_error(Kraus([E0, E1]), operator_string='pauli') results_list = approximate_quantum_error(Kraus([E0, E1]), operator_list=[X, Y, Z]) results_tuple = approximate_quantum_error(Kraus([E0, E1]), operator_list=(X, Y, Z)) self.assertErrorsAlmostEqual(results_dict, results_string) self.assertErrorsAlmostEqual(results_string, results_list) self.assertErrorsAlmostEqual(results_list, results_tuple)
def test_copy(self): """Test copy method""" mat = np.eye(4) orig = Kraus(mat) cpy = orig.copy() cpy._data[0][0][0, 0] = 0.0 self.assertFalse(cpy == orig)
def test_approx_random_unitary_channel(self): # run without raising any error noise = Kraus(random_unitary(2, seed=123)) for opstr in ['pauli', 'reset', 'clifford']: approximate_quantum_error(noise, operator_string=opstr) noise = Kraus(random_unitary(4, seed=123)) for opstr in ['pauli', 'reset']: approximate_quantum_error(noise, operator_string=opstr)
def test_tensor(self): """Test tensor method.""" rho0, rho1 = np.diag([1, 0]), np.diag([0, 1]) rho_init = np.kron(rho0, rho0) chan1 = Kraus(self.UI) chan2 = Kraus(self.UX) # X \otimes I chan = chan2.tensor(chan1) rho_targ = np.kron(rho1, rho0) self.assertEqual(chan.dim, (4, 4)) self.assertAllClose(chan._evolve(rho_init), rho_targ) # I \otimes X chan = chan1.tensor(chan2) rho_targ = np.kron(rho0, rho1) self.assertEqual(chan.dim, (4, 4)) self.assertAllClose(chan._evolve(rho_init), rho_targ) # Completely depolarizing chan_dep = Kraus(self.depol_kraus(1)) chan = chan_dep.tensor(chan_dep) rho_targ = np.diag([1, 1, 1, 1]) / 4 self.assertEqual(chan.dim, (4, 4)) self.assertAllClose(chan._evolve(rho_init), rho_targ)
def test_expand(self): """Test expand method.""" rho0, rho1 = np.diag([1, 0]), np.diag([0, 1]) rho_init = DensityMatrix(np.kron(rho0, rho0)) chan1 = Kraus(self.UI) chan2 = Kraus(self.UX) # X \otimes I chan = chan1.expand(chan2) rho_targ = DensityMatrix(np.kron(rho1, rho0)) self.assertEqual(chan.dim, (4, 4)) self.assertEqual(rho_init @ chan, rho_targ) # I \otimes X chan = chan2.expand(chan1) rho_targ = DensityMatrix(np.kron(rho0, rho1)) self.assertEqual(chan.dim, (4, 4)) self.assertEqual(rho_init @ chan, rho_targ) # Completely depolarizing chan_dep = Kraus(self.depol_kraus(1)) chan = chan_dep.expand(chan_dep) rho_targ = DensityMatrix(np.diag([1, 1, 1, 1]) / 4) self.assertEqual(chan.dim, (4, 4)) self.assertEqual(rho_init @ chan, rho_targ)
def test_multiply(self): """Test multiply method.""" # Random initial state and Kraus ops rho = self.rand_rho(2) val = 0.5 kraus1, kraus2 = self.rand_kraus(2, 4, 4), self.rand_kraus(2, 4, 4) # Single Kraus set chan1 = Kraus(kraus1) targ = val * chan1._evolve(rho) chan = chan1.multiply(val) self.assertAllClose(chan._evolve(rho), targ) chan = val * chan1 self.assertAllClose(chan._evolve(rho), targ) chan = chan1 * val self.assertAllClose(chan._evolve(rho), targ) # Double Kraus set chan2 = Kraus((kraus1, kraus2)) targ = val * chan2._evolve(rho) chan = chan2.multiply(val) self.assertAllClose(chan._evolve(rho), targ) chan = val * chan2 self.assertAllClose(chan._evolve(rho), targ) chan = chan2 * val self.assertAllClose(chan._evolve(rho), targ)
def test_power(self): """Test power method.""" # 10% depolarizing channel rho = DensityMatrix(np.diag([1, 0])) p_id = 0.9 chan = Kraus(self.depol_kraus(1 - p_id)) # Compose 3 times p_id3 = p_id**3 chan3 = chan.power(3) targ3a = rho @ chan @ chan @ chan self.assertEqual(rho @ chan3, targ3a) targ3b = rho @ Kraus(self.depol_kraus(1 - p_id3)) self.assertEqual(rho @ chan3, targ3b)
def test_multiply_except(self): """Test multiply method raises exceptions.""" chan = Kraus(self.depol_kraus(1)) self.assertRaises(QiskitError, chan._multiply, 's') self.assertRaises(QiskitError, chan.__rmul__, 's') self.assertRaises(QiskitError, chan._multiply, chan) self.assertRaises(QiskitError, chan.__rmul__, chan)
def test_transpose_inplace(self): """Test inplace transpose method.""" kraus_l, kraus_r = self.rand_kraus(2, 4, 4), self.rand_kraus(2, 4, 4) # Single Kraus list targ = Kraus([np.transpose(k) for k in kraus_l]) chan = Kraus(kraus_l) chan.transpose(inplace=True) self.assertEqual(chan, targ) self.assertEqual(chan.dims, (4, 2)) # Double Kraus list targ = Kraus(([np.transpose(k) for k in kraus_l], [np.transpose(k) for k in kraus_r])) chan = Kraus((kraus_l, kraus_r)) chan.transpose(inplace=True) self.assertEqual(chan, targ) self.assertEqual(chan.dims, (4, 2))
def ideal(self): """Return True if this error object is composed only of identity operations. Note that the identity check is best effort and up to global phase.""" for circ in self.circuits: try: # Circuit-level identity check for clifford Circuits clifford = Clifford(circ) if clifford != Clifford(np.eye(2 * circ.num_qubits, dtype=bool)): return False except QiskitError: pass # Component-wise check for non-Clifford circuits for op, _, _ in circ: if isinstance(op, IGate): continue if isinstance(op, PauliGate): if op.params[0].replace('I', ''): return False else: # Convert to Kraus and check if identity kmats = Kraus(op).data if len(kmats) > 1: return False if not is_identity_matrix(kmats[0], ignore_phase=True, atol=self.atol, rtol=self.rtol): return False return True
def transform_by_given_channel(self, channel_matrices, const_channel_matrix): """ Transform by by quantum channels. This method creates objective function representing the Hilbert-Schmidt norm of the matrix (A-B) obtained as the difference of the input noise channel and the output channel we wish to determine. This function is represented by a matrix P and a vector q, such that f(x) = 1/2(x*P*x)+q*x where x is the vector we wish to minimize, where x represents probabilities for the noise operators that construct the output channel Args: channel_matrices (list): A list of 4x4 symbolic matrices const_channel_matrix (matrix): a 4x4 constant matrix Returns: list: a list of the optimal probabilities for the channel matrices, determined by the quadratic program solver """ target_channel = SuperOp(Kraus(self.noise_kraus_operators)) target_channel_matrix = target_channel._data.T const_matrix = const_channel_matrix - target_channel_matrix P = self.compute_P(channel_matrices) q = self.compute_q(channel_matrices, const_matrix) return self.solve_quadratic_program(P, q)
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 test_adjoint_inplace(self): """Test inplace adjoint method.""" kraus_l = [self.rand_matrix(4, 2) for _ in range(4)] kraus_r = [self.rand_matrix(4, 2) for _ in range(4)] # Single Kraus list targ = Kraus([np.transpose(k).conj() for k in kraus_l]) chan = Kraus(kraus_l) chan.adjoint(inplace=True) self.assertEqual(chan, targ) self.assertEqual(chan.dims, (4, 2)) # Double Kraus list targ = Kraus(([np.transpose(k).conj() for k in kraus_l], [np.transpose(k).conj() for k in kraus_r])) chan = Kraus((kraus_l, kraus_r)) chan.adjoint(inplace=True) self.assertEqual(chan, targ) self.assertEqual(chan.dims, (4, 2))
def test_power_inplace(self): """Test inplace power method.""" # 10% depolarizing channel rho = np.diag([1, 0]) p_id = 0.9 chan = Kraus(self.depol_kraus(1 - p_id)) # Compose 3 times p_id3 = p_id ** 3 targ3a = chan._evolve(chan._evolve(chan._evolve(rho))) targ3b = Kraus(self.depol_kraus(1 - p_id3))._evolve(rho) chan.power(3, inplace=True) self.assertAllClose(chan._evolve(rho), targ3a) self.assertAllClose(chan._evolve(rho), targ3b)
def test_subtract(self): """Test subtract method.""" # Random input test state rho = DensityMatrix(self.rand_rho(2)) kraus1, kraus2 = self.rand_kraus(2, 4, 4), self.rand_kraus(2, 4, 4) # Random Single-Kraus maps chan1 = Kraus(kraus1) chan2 = Kraus(kraus2) targ = (rho @ chan1) - (rho @ chan2) chan = chan1 - chan2 self.assertEqual(rho @ chan, targ) # Random Single-Kraus maps chan = Kraus((kraus1, kraus2)) targ = 0 * (rho @ chan) chan = chan - chan self.assertEqual(rho @ chan, targ)
def test_power_except(self): """Test power method raises exceptions.""" chan = Kraus(self.depol_kraus(0.9)) # 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 old_approximate_quantum_error(error, *, operator_string=None, operator_dict=None, operator_list=None): if not isinstance(error, QuantumError): error = QuantumError(error) if error.number_of_qubits > 2: raise NoiseError( "Only 1-qubit and 2-qubit noises can be converted, {}-qubit " "noise found in model".format(error.number_of_qubits)) error_kraus_operators = Kraus(error.to_quantumchannel()).data transformer = NoiseTransformer() if operator_string is not None: no_info_error = "No information about noise type {}".format( operator_string) operator_string = operator_string.lower() if operator_string not in transformer.named_operators.keys(): raise RuntimeError(no_info_error) operator_lists = transformer.named_operators[operator_string] if len(operator_lists) < error.number_of_qubits: raise RuntimeError( no_info_error + " for {} qubits".format(error.number_of_qubits)) operator_dict = operator_lists[error.number_of_qubits - 1] if operator_dict is not None: _, operator_list = zip(*operator_dict.items()) if operator_list is not None: op_matrix_list = [ transformer.operator_matrix(operator) for operator in operator_list ] probabilities = transformer.transform_by_operator_list( op_matrix_list, error_kraus_operators) identity_prob = numpy.round(1 - sum(probabilities), 9) if identity_prob < 0 or identity_prob > 1: raise RuntimeError( "Channel probabilities sum to {}".format(1 - identity_prob)) quantum_error_spec = [([{ 'name': 'id', 'qubits': [0] }], identity_prob)] op_circuit_list = [ transformer.operator_circuit(operator) for operator in operator_list ] for (operator, probability) in zip(op_circuit_list, probabilities): quantum_error_spec.append((operator, probability)) return QuantumError(quantum_error_spec) raise NoiseError( "Quantum error approximation failed - no approximating operators detected" )
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(self): """Test multiply method.""" # Random initial state and Kraus ops rho = DensityMatrix(self.rand_rho(2)) val = 0.5 kraus1, kraus2 = self.rand_kraus(2, 4, 4), self.rand_kraus(2, 4, 4) # Single Kraus set chan1 = Kraus(kraus1) targ = val * (rho @ chan1) chan = chan1.multiply(val) self.assertEqual(rho @ chan, targ) chan = val * chan1 self.assertEqual(rho @ chan, targ) chan = chan1 * val self.assertEqual(rho @ chan, targ) # Double Kraus set chan2 = Kraus((kraus1, kraus2)) targ = val * (rho @ chan2) chan = chan2.multiply(val) self.assertEqual(rho @ chan, targ) chan = val * chan2 self.assertEqual(rho @ chan, targ) chan = chan2 * val self.assertEqual(rho @ chan, targ)
def transform_by_given_channel(self, channel_matrices, const_channel_matrix): # This method creates the quadratic programming instance for # minimizing the Hilbert-Schmidt norm of the matrix (A-B) obtained # as the difference of the input noise channel and the output # channel we wish to determine. target_channel = SuperOp(Kraus(self.noise_kraus_operators)) target_channel_matrix = target_channel._data.T const_matrix = const_channel_matrix - target_channel_matrix P = self.compute_P(channel_matrices) q = self.compute_q(channel_matrices, const_matrix) return self.solve_quadratic_program(P, q)
def test_transformation_by_kraus(self): gamma = 0.23 error = amplitude_damping_error(gamma) reset_to_0 = [ numpy.array([[1, 0], [0, 0]]), numpy.array([[0, 1], [0, 0]]) ] reset_to_1 = [ numpy.array([[0, 0], [1, 0]]), numpy.array([[0, 0], [0, 1]]) ] reset_kraus = [Kraus(reset_to_0), Kraus(reset_to_1)] actual = approximate_quantum_error(error, operator_list=reset_kraus) p = (1 + gamma - numpy.sqrt(1 - gamma)) / 2 expected_probs = [1 - p, p, 0] self.assertListAlmostEqual(expected_probs, actual.probabilities) with self.assertWarns(DeprecationWarning): approximate_quantum_error(error, operator_list=[reset_to_0, reset_to_1])
def test_from_myqlm_kraus(self): """ Test all combinations of kraus quantum channel between myqlm and qiskit """ arr = np.arange(8 * 8, dtype=complex).reshape((8, 8)) kraus_ops = [array_to_matrix(arr)] qchannel = QuantumChannel(representation=RepresentationType.KRAUS, arity=3, kraus_ops=kraus_ops) qiskit_qchannel = Kraus(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_adjoint(self): """Test adjoint method.""" kraus_l, kraus_r = self.rand_kraus(2, 4, 4), self.rand_kraus(2, 4, 4) # Single Kraus list targ = Kraus([np.transpose(k).conj() for k in kraus_l]) chan1 = Kraus(kraus_l) chan = chan1.adjoint() self.assertEqual(chan, targ) self.assertEqual(chan.dims, (4, 2)) # Double Kraus list targ = Kraus(([np.transpose(k).conj() for k in kraus_l], [np.transpose(k).conj() for k in kraus_r])) chan1 = Kraus((kraus_l, kraus_r)) chan = chan1.adjoint() self.assertEqual(chan, targ) self.assertEqual(chan.dims, (4, 2))
def test_conjugate(self): """Test conjugate method.""" kraus_l, kraus_r = self.rand_kraus(2, 4, 4), self.rand_kraus(2, 4, 4) # Single Kraus list targ = Kraus([np.conjugate(k) for k in kraus_l]) chan1 = Kraus(kraus_l) chan = chan1.conjugate() self.assertEqual(chan, targ) self.assertEqual(chan.dim, (2, 4)) # Double Kraus list targ = Kraus(([np.conjugate(k) for k in kraus_l], [np.conjugate(k) for k in kraus_r])) chan1 = Kraus((kraus_l, kraus_r)) chan = chan1.conjugate() self.assertEqual(chan, targ) self.assertEqual(chan.dim, (2, 4))
def test_reset_2_qubit(self): # approximating amplitude damping using relaxation operators gamma = 0.23 p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2 A0 = [[1, 0], [0, numpy.sqrt(1 - gamma)]] A1 = [[0, numpy.sqrt(gamma)], [0, 0]] error_1 = QuantumError([([(Kraus([A0, A1]), [0]), (IGate(), [1])], 1)]) error_2 = QuantumError([([(Kraus([A0, A1]), [1]), (IGate(), [0])], 1)]) expected_results_1 = QuantumError([([(IGate(), [0]), (IGate(), [1])], 1 - p), ([(Reset(), [0]), (IGate(), [1])], p)]) expected_results_2 = QuantumError([([(IGate(), [1]), (IGate(), [0])], 1 - p), ([(Reset(), [1]), (IGate(), [0])], p)]) results_1 = approximate_quantum_error(error_1, operator_string="reset") results_2 = approximate_quantum_error(error_2, operator_string="reset") self.assertErrorsAlmostEqual(results_1, expected_results_1) self.assertErrorsAlmostEqual(results_2, expected_results_2)
def operator_matrix(self, operator): """Converts an operator representation to Kraus matrix representation Args: operator (operator): operator representation. Can be a noise circuit or a matrix or a list of matrices. Returns: Kraus: the operator, converted to Kraus representation. """ if isinstance(operator, list) and isinstance(operator[0], dict): operator_error = QuantumError([(operator, 1)]) kraus_rep = Kraus(operator_error.to_quantumchannel()).data return kraus_rep return operator