Пример #1
0
 def test_global_phase(self, num_ctrl_qubits):
     """test global phase"""
     mat1 = np.array([[0, 1], [1, 0]], dtype=float)
     mat2 = np.exp(1j) * mat1
     gate1 = UnitaryGate(mat1)
     gate2 = UnitaryGate(mat2)
     cgate1 = gate1.q_if(num_ctrl_qubits)
     cgate2 = gate2.q_if(num_ctrl_qubits)
     cop_mat1 = _compute_control_matrix(mat1, num_ctrl_qubits)
     cop_mat2 = _compute_control_matrix(mat2, num_ctrl_qubits, phase=1)
     self.assertTrue(is_unitary_matrix(mat1))
     self.assertTrue(is_unitary_matrix(mat2))
     self.assertTrue(is_unitary_matrix(cop_mat1))
     self.assertTrue(is_unitary_matrix(cop_mat2))
     self.assertTrue(Operator(cgate1).equiv(Operator(cgate2)))
     self.assertTrue(matrix_equal(cop_mat1, cop_mat2, ignore_phase=True))
Пример #2
0
def get_stinespring_unitary(choi, target_choi, target_unitary, n_qubits):
    """ get the Stinespring dilation unitary given the Choi matrix choi"""
    unitary = Stinespring(Choi(choi)).data

    # this fixes some numerical issues
    if np.linalg.norm(choi.data - target_choi) < 1e-8:
        unitary = target_unitary

    if type(unitary) == tuple:
        assert np.linalg.norm(unitary[0] - unitary[1]) < 1e-8
        unitary = unitary[0]
    assert unitary.shape[1] == 2**n_qubits

    # increase hilbert space size to be a power of two
    next_pow_2 = int(np.power(2,
                              np.ceil(np.log(unitary.shape[0]) / np.log(2))))
    if unitary.shape[0] != next_pow_2:
        diff = next_pow_2 - unitary.shape[0]
        unitary = np.vstack([unitary, np.zeros((diff, 2**n_qubits))])
        assert unitary.shape[0] == next_pow_2
        assert unitary.shape[1] == 2**n_qubits
    num_anc = int(np.log2(unitary.shape[0])) - n_qubits

    completed = complete_unitary(unitary)
    # due to numerical issues, the matrix is not always perfectly unitary
    if not is_unitary_matrix(completed, atol=1e-12):
        completed, _ = scipy.linalg.polar(completed)

    return completed, num_anc
Пример #3
0
def make_unitary_instruction(mat, qubits, standard_gates=True):
    """Return a qobj instruction for a unitary matrix gate.
    Args:
        mat (matrix): A square or diagonal unitary matrix.
        qubits (list[int]): The qubits the matrix is applied to.
        standard_gates (bool): Check if the matrix instruction is a
                               standard instruction.
    Returns:
        dict: The qobj instruction object.
    Raises:
        NoiseError: if the input is not a unitary matrix.
    """
    warnings.warn(
        'make_unitary_instruction 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)

    if not is_unitary_matrix(mat):
        raise NoiseError("Input matrix is not unitary.")

    if isinstance(qubits, int):
        qubits = [qubits]

    instruction = {"name": "unitary", "qubits": qubits, "params": [mat]}
    if standard_gates:
        return standard_gate_instruction(instruction)
    else:
        return [instruction]
Пример #4
0
    def __init__(self, data, label=None):
        """Create a gate from a numeric unitary matrix.

        Args:
            data (matrix or Operator): unitary operator.
            label (str): unitary name for backend [Default: None].

        Raises:
            ExtensionError: if input data is not an N-qubit unitary operator.
        """
        if hasattr(data, 'to_matrix'):
            # If input is Gate subclass or some other class object that has
            # a to_matrix method this will call that method.
            data = data.to_matrix()
        elif hasattr(data, 'to_operator'):
            # If input is a BaseOperator subclass this attempts to convert
            # the object to an Operator so that we can extract the underlying
            # numpy matrix from `Operator.data`.
            data = data.to_operator().data
        # Convert to numpy array in case not already an array
        data = numpy.array(data, dtype=complex)
        # Check input is unitary
        if not is_unitary_matrix(data):
            raise ExtensionError("Input matrix is not unitary.")
        # Check input is N-qubit matrix
        input_dim, output_dim = data.shape
        num_qubits = int(numpy.log2(input_dim))
        if input_dim != output_dim or 2**num_qubits != input_dim:
            raise ExtensionError("Input matrix is not an N-qubit operator.")

        self._qasm_name = None
        self._qasm_definition = None
        self._qasm_def_written = False
        # Store instruction params
        super().__init__('unitary', num_qubits, [data], label=label)
Пример #5
0
 def is_unitary(self, atol=None, rtol=None):
     """Return True if operator is a unitary matrix."""
     if atol is None:
         atol = self.atol
     if rtol is None:
         rtol = self.rtol
     return is_unitary_matrix(self._data, rtol=rtol, atol=atol)
Пример #6
0
def make_unitary_instruction(mat, qubits, standard_gates=True):
    """Return a qobj instruction for a unitary matrix gate.

    Args:
        mat (matrix): A square or diagonal unitary matrix.
        qubits (list[int]): The qubits the matrix is applied to.
        standard_gates (bool): Check if the matrix instruction is a
                               standard instruction.

    Returns:
        dict: The qobj instruction object.

    Raises:
        NoiseError: if the input is not a unitary matrix.
    """
    if not is_unitary_matrix(mat):
        raise NoiseError("Input matrix is not unitary.")

    if isinstance(qubits, int):
        qubits = [qubits]

    instruction = {"name": "unitary", "qubits": qubits, "params": [mat]}
    if standard_gates:
        return standard_gate_instruction(instruction)
    else:
        return [instruction]
Пример #7
0
    def __call__(self, unitary, simplify=True, atol=DEFAULT_ATOL):
        """Decompose single qubit gate into a circuit.

        Args:
            unitary (Operator or Gate or array): 1-qubit unitary matrix
            simplify (bool): reduce gate count in decomposition [Default: True].
            atol (float): absolute tolerance for checking angles when simplifying
                         returned circuit [Default: 1e-12].
        Returns:
            QuantumCircuit: the decomposed single-qubit gate circuit

        Raises:
            QiskitError: if input is invalid or synthesis fails.
        """
        if hasattr(unitary, "to_operator"):
            # If input is a BaseOperator subclass this attempts to convert
            # the object to an Operator so that we can extract the underlying
            # numpy matrix from `Operator.data`.
            unitary = unitary.to_operator().data
        elif hasattr(unitary, "to_matrix"):
            # If input is Gate subclass or some other class object that has
            # a to_matrix method this will call that method.
            unitary = unitary.to_matrix()
        # Convert to numpy array in case not already an array
        unitary = np.asarray(unitary, dtype=complex)

        # Check input is a 2-qubit unitary
        if unitary.shape != (2, 2):
            raise QiskitError(
                "OneQubitEulerDecomposer: expected 2x2 input matrix")
        if not is_unitary_matrix(unitary):
            raise QiskitError(
                "OneQubitEulerDecomposer: input matrix is not unitary.")
        return self._decompose(unitary, simplify=simplify, atol=atol)
Пример #8
0
def addCustomGates():
    receivedDictionary=request.get_json()
    matrix=receivedDictionary["matrix"]
    matrix=f.strToComplex(matrix)
    isUnitary=is_unitary_matrix(matrix)
    if isUnitary:
        matrix=c.reversedMatrix(matrix,int(log2(len(matrix))))
        c.gatesObjects[receivedDictionary["gateName"]]=f.matrixToGateObject(matrix,receivedDictionary["gateName"])
    return jsonify({"isUnitary":isUnitary})
    def __call__(self, target, basis_fidelity=None):
        """Decompose a two-qubit unitary over fixed basis + SU(2) using the best approximation given
        that each basis application has a finite fidelity.
        """
        basis_fidelity = basis_fidelity or self.basis_fidelity
        if hasattr(target, 'to_operator'):
            # If input is a BaseOperator subclass this attempts to convert
            # the object to an Operator so that we can extract the underlying
            # numpy matrix from `Operator.data`.
            target = target.to_operator().data
        if hasattr(target, 'to_matrix'):
            # If input is Gate subclass or some other class object that has
            # a to_matrix method this will call that method.
            target = target.to_matrix()
        # Convert to numpy array incase not already an array
        target = np.asarray(target, dtype=complex)
        # Check input is a 2-qubit unitary
        if target.shape != (4, 4):
            raise QiskitError(
                "TwoQubitBasisDecomposer: expected 4x4 matrix for target")
        if not is_unitary_matrix(target):
            raise QiskitError(
                "TwoQubitBasisDecomposer: target matrix is not unitary.")

        target_decomposed = TwoQubitWeylDecomposition(target)
        traces = self.traces(target_decomposed)
        expected_fidelities = [
            trace_to_fid(traces[i]) * basis_fidelity**i for i in range(4)
        ]

        best_nbasis = np.argmax(expected_fidelities)
        decomposition = self.decomposition_fns[best_nbasis](target_decomposed)
        decomposition_euler = [
            self._decomposer1q._decompose(x) for x in decomposition
        ]

        q = QuantumRegister(2)
        return_circuit = QuantumCircuit(q)
        return_circuit.global_phase = target_decomposed.global_phase
        return_circuit.global_phase -= best_nbasis * self.basis.global_phase
        if best_nbasis == 2:
            return_circuit.global_phase += np.pi
        for i in range(best_nbasis):
            return_circuit.compose(decomposition_euler[2 * i], [q[0]],
                                   inplace=True)
            return_circuit.compose(decomposition_euler[2 * i + 1], [q[1]],
                                   inplace=True)
            return_circuit.append(self.gate, [q[0], q[1]])
        return_circuit.compose(decomposition_euler[2 * best_nbasis], [q[0]],
                               inplace=True)
        return_circuit.compose(decomposition_euler[2 * best_nbasis + 1],
                               [q[1]],
                               inplace=True)

        return return_circuit
Пример #10
0
 def __init__(self, u, mode="ZYZ", up_to_diagonal=False):
     if mode != "ZYZ":
         raise QiskitError("The decomposition mode is not known.")
     # Check if the matrix u has the right dimensions and if it is a unitary
     if not u.shape == (2, 2):
         raise QiskitError("The dimension of the input matrix is not equal to (2,2).")
     if not is_unitary_matrix(u):
         raise QiskitError("The 2*2 matrix is not unitary.")
     self.mode = mode
     self.up_to_diagonal = up_to_diagonal
     # Create new gate
     super().__init__("unitary", 1, [u])
Пример #11
0
def mixed_unitary_error(noise_ops, standard_gates=True):
    """
    Mixed unitary quantum error channel.

    The input should be a list of pairs (U[j], p[j]), where `U[j]` is a
    unitary matrix and `p[j]` is a probability. All probabilities must
    sum to 1 for the input ops to be valid.

    Args:
        noise_ops (list[pair[matrix, double]]): unitary error matricies.
        standard_gates (bool): Check if input matrices are standard gates.

    Returns:
        QuantumError: The quantum error object.

    Raises:
        NoiseError: if error parameters are invalid.
    """

    # Error checking
    if not isinstance(noise_ops, (list, tuple, zip)):
        raise NoiseError("Input noise ops is not a list.")

    # Convert to numpy arrays
    noise_ops = [(np.array(op, dtype=complex), p) for op, p in noise_ops]
    if not noise_ops:
        raise NoiseError("Input noise list is empty.")

    # Check for identity unitaries
    prob_identity = 0.
    instructions = []
    instructions_probs = []
    num_qubits = qubits_from_mat(noise_ops[0][0])
    qubits = list(range(num_qubits))
    for unitary, prob in noise_ops:
        # Check unitary
        if qubits_from_mat(unitary) != num_qubits:
            raise NoiseError("Input matrices different size.")
        if not is_unitary_matrix(unitary):
            raise NoiseError("Input matrix is not unitary.")
        if is_identity_matrix(unitary):
            prob_identity += prob
        else:
            instr = make_unitary_instruction(unitary,
                                             qubits,
                                             standard_gates=standard_gates)
            instructions.append(instr)
            instructions_probs.append(prob)
    if prob_identity > 0:
        instructions.append([{"name": "id", "qubits": [0]}])
        instructions_probs.append(prob_identity)
    return QuantumError(zip(instructions, instructions_probs))
Пример #12
0
    def __init__(self, unitary_matrix, mode='ZYZ', up_to_diagonal=False, u=None):
        """Create a new single qubit gate based on the unitary ``u``."""
        if mode not in ['ZYZ']:
            raise QiskitError("The decomposition mode is not known.")
        # Check if the matrix u has the right dimensions and if it is a unitary
        if unitary_matrix.shape != (2, 2):
            raise QiskitError("The dimension of the input matrix is not equal to (2,2).")
        if not is_unitary_matrix(unitary_matrix):
            raise QiskitError("The 2*2 matrix is not unitary.")

        self.mode = mode
        self.up_to_diagonal = up_to_diagonal
        self._diag = None

        # Create new gate
        super().__init__("unitary", 1, [unitary_matrix])
Пример #13
0
def unitary_from_hermitian(hermitian):
    """Generates a unitary matrix from a hermitian matrix.
        The formula is U = e^(i*H).

    Args:
        hermitian: A hermitian matrix.

    Returns:
        unitary: The resulting unitarian matrix.

    Raises:
        AssertionError: If the resulting matrix is not unitarian.
    """
    unitary = np.matrix(expm(1j * hermitian))
    assert is_unitary_matrix(unitary)
    return unitary
Пример #14
0
 def test_controlled_unitary(self, num_ctrl_qubits):
     """test controlled unitary"""
     num_target = 1
     q_target = QuantumRegister(num_target)
     qc1 = QuantumCircuit(q_target)
     # for h-rx(pi/2)
     theta, phi, lamb = 1.57079632679490, 0.0, 4.71238898038469
     qc1.u3(theta, phi, lamb, q_target[0])
     base_gate = qc1.to_gate()
     # get UnitaryGate version of circuit
     base_op = Operator(qc1)
     base_mat = base_op.data
     cgate = base_gate.control(num_ctrl_qubits)
     test_op = Operator(cgate)
     cop_mat = _compute_control_matrix(base_mat, num_ctrl_qubits)
     self.assertTrue(is_unitary_matrix(base_mat))
     self.assertTrue(matrix_equal(cop_mat, test_op.data, ignore_phase=True))
Пример #15
0
def subCircuitCustomGate():
    if request.method=='POST':
        receivedDictionary=request.get_json()
        c2=Circuit()
        c2.gatesObjects=c.gatesObjects
        c2.subCircuitSetter(receivedDictionary)
        try:
            circuit=c2.createDraggableCircuit()
        except Exception as e:
            return jsonify({"conditionalLoopError":str(e)})
        r=Results(circuit)
        matrix=r.matrixRepresentation()
        complexMatrix=f.strToComplex(matrix)
        isUnitary=is_unitary_matrix(complexMatrix)
        if isUnitary:
            c.gatesObjects[receivedDictionary["gateName"]]=f.matrixToGateObject(complexMatrix,receivedDictionary["gateName"])
        return  jsonify({"isUnitary":isUnitary})
Пример #16
0
    def __init__(self, gate_list, up_to_diagonal=False):
        """UCGate Gate initializer.

        Args:
            gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}],
                where each single-qubit unitary U_i is given as a 2*2 numpy array.

            up_to_diagonal (bool): determines if the gate is implemented up to a diagonal.
                or if it is decomposed completely (default: False).
                If the UCGate u is decomposed up to a diagonal d, this means that the circuit
                implements a unitary u' such that d.u'=u.

        Raises:
            QiskitError: in case of bad input to the constructor
        """
        # check input format
        if not isinstance(gate_list, list):
            raise QiskitError(
                "The single-qubit unitaries are not provided in a list.")
        for gate in gate_list:
            if not gate.shape == (2, 2):
                raise QiskitError(
                    "The dimension of a controlled gate is not equal to (2,2)."
                )
        if not gate_list:
            raise QiskitError("The gate list cannot be empty.")

        # Check if number of gates in gate_list is a positive power of two
        num_contr = math.log2(len(gate_list))
        if num_contr < 0 or not num_contr.is_integer():
            raise QiskitError(
                "The number of controlled single-qubit gates is not a "
                "non-negative power of 2.")

        # Check if the single-qubit gates are unitaries
        for gate in gate_list:
            if not is_unitary_matrix(gate, _EPS):
                raise QiskitError("A controlled gate is not unitary.")

        # Create new gate.
        super().__init__("multiplexer", int(num_contr) + 1, gate_list)
        self.up_to_diagonal = up_to_diagonal
Пример #17
0
    def __call__(self, unitary_mat, simplify=True, atol=DEFAULT_ATOL):
        """Decompose single qubit gate into a circuit.

        Args:
            unitary_mat (array_like): 1-qubit unitary matrix
            simplify (bool): remove zero-angle rotations [Default: True]
            atol (float): absolute tolerance for checking angles zero.

        Returns:
            QuantumCircuit: the decomposed single-qubit gate circuit

        Raises:
            QiskitError: if input is invalid or synthesis fails.
        """
        if hasattr(unitary_mat, 'to_operator'):
            # If input is a BaseOperator subclass this attempts to convert
            # the object to an Operator so that we can extract the underlying
            # numpy matrix from `Operator.data`.
            unitary_mat = unitary_mat.to_operator().data
        if hasattr(unitary_mat, 'to_matrix'):
            # If input is Gate subclass or some other class object that has
            # a to_matrix method this will call that method.
            unitary_mat = unitary_mat.to_matrix()
        # Convert to numpy array incase not already an array
        unitary_mat = np.asarray(unitary_mat, dtype=complex)

        # Check input is a 2-qubit unitary
        if unitary_mat.shape != (2, 2):
            raise QiskitError("OneQubitEulerDecomposer: "
                              "expected 2x2 input matrix")
        if not is_unitary_matrix(unitary_mat):
            raise QiskitError("OneQubitEulerDecomposer: "
                              "input matrix is not unitary.")
        circuit = self._circuit(unitary_mat, simplify=simplify, atol=atol)
        # Check circuit is correct
        if not Operator(circuit).equiv(Operator(unitary_mat)):
            raise QiskitError("OneQubitEulerDecomposer: "
                              "synthesis failed within required accuracy.")
        return circuit
Пример #18
0
def _make_unitary_instruction(mat, qubits, standard_gates=True):
    """Temporary function to create Instruction objects from a unitary matrix,
    which is necessary for creating a new QuantumError with the deprecated
    standard_gates option. Note that the type of returned object is different from
    the deprecated make_unitary_instruction.
    TODO: to be removed after deprecation period.

    Args:
        mat (matrix): A square or diagonal unitary matrix.
        qubits (list[int]): The qubits the matrix is applied to.
        standard_gates (bool): Check if the matrix instruction is a
                               standard instruction.

    Returns:
        list: The list of instructions.

    Raises:
        NoiseError: if the input is not a unitary matrix.
    """
    if not is_unitary_matrix(mat):
        raise NoiseError("Input matrix is not unitary.")

    if isinstance(qubits, int):
        qubits = [qubits]

    instruction = {"name": "unitary", "qubits": qubits, "params": [mat]}
    if standard_gates:
        if isinstance(instruction, dict):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore",
                                        category=DeprecationWarning,
                                        module="qiskit.providers.aer.noise.errors.errorutils")
                res = _standard_gate_instruction(instruction)
        else:
            res = [instruction]
        return res
    else:
        return [instruction]
Пример #19
0
def kraus2instructions(kraus_ops, standard_gates, atol=ATOL_DEFAULT):
    """
    Convert a list of Kraus matrices into qobj circuits.

    If any Kraus operators are a unitary matrix they will be converted
    into unitary qobj instructions. Identity unitary matrices will also be
    converted into identity qobj instructions.

    Args:
        kraus_ops (list[matrix]): A list of Kraus matrices for a CPTP map.
        standard_gates (bool): Check if the matrix instruction is a
                               standard instruction (default: True).
        atol (double): Threshold for testing if probabilities are zero.


    Returns:
        list: A list of pairs (p, circuit) where `circuit` is a list of qobj
        instructions, and `p` is the probability of that circuit for the
        given error.

    Raises:
        NoiseError: If the input Kraus channel is not CPTP.
    """
    # Check threshold
    if atol < 0:
        raise NoiseError("atol cannot be negative")
    if atol > 1e-5:
        raise NoiseError(
            "atol value is too large. It should be close to zero.")

    # Check CPTP
    if not Kraus(kraus_ops).is_cptp(atol=atol):
        raise NoiseError("Input Kraus channel is not CPTP.")

    # Get number of qubits
    num_qubits = int(np.log2(len(kraus_ops[0])))
    if len(kraus_ops[0]) != 2**num_qubits:
        raise NoiseError("Input Kraus channel is not a multi-qubit channel.")

    # Check if each matrix is a:
    # 1. scaled identity matrix
    # 2. scaled non-identity unitary matrix
    # 3. a non-unitary Kraus operator

    # Probabilities
    prob_identity = 0
    prob_unitary = 0  # total probability of all unitary ops (including id)
    prob_kraus = 0  # total probability of non-unitary ops
    probabilities = []  # initialize with probability of Identity

    # Matrices
    unitaries = []  # non-identity unitaries
    non_unitaries = []  # non-unitary Kraus matrices

    for mat in kraus_ops:
        # Get the value of the maximum diagonal element
        # of op.H * op for rescaling
        prob = abs(max(np.diag(np.conj(np.transpose(mat)).dot(mat))))
        if prob > 0.0:
            if abs(prob - 1) > 0.0:
                # Rescale the operator by square root of prob
                rescaled_mat = np.array(mat) / np.sqrt(prob)
            else:
                rescaled_mat = mat
            # Check if identity operator
            if is_identity_matrix(rescaled_mat, ignore_phase=True):
                prob_identity += prob
                prob_unitary += prob
            # Check if unitary
            elif is_unitary_matrix(rescaled_mat):
                probabilities.append(prob)
                prob_unitary += prob
                unitaries.append(rescaled_mat)
            # Non-unitary op
            else:
                non_unitaries.append(mat)

    # Check probabilities
    prob_kraus = 1 - prob_unitary
    if prob_unitary - 1 > atol:
        raise NoiseError("Invalid kraus matrices: unitary probability "
                         "{} > 1".format(prob_unitary))
    if prob_unitary < -atol:
        raise NoiseError("Invalid kraus matrices: unitary probability "
                         "{} < 1".format(prob_unitary))
    if prob_identity - 1 > atol:
        raise NoiseError("Invalid kraus matrices: identity probability "
                         "{} > 1".format(prob_identity))
    if prob_identity < -atol:
        raise NoiseError("Invalid kraus matrices: identity probability "
                         "{} < 1".format(prob_identity))
    if prob_kraus - 1 > atol:
        raise NoiseError("Invalid kraus matrices: non-unitary probability "
                         "{} > 1".format(prob_kraus))
    if prob_kraus < -atol:
        raise NoiseError("Invalid kraus matrices: non-unitary probability "
                         "{} < 1".format(prob_kraus))

    # Build qobj instructions
    instructions = []
    qubits = list(range(num_qubits))

    # Add unitary instructions
    for unitary in unitaries:
        instructions.append(
            make_unitary_instruction(
                unitary, qubits, standard_gates=standard_gates))

    # Add identity instruction
    if prob_identity > atol:
        if abs(prob_identity - 1) < atol:
            probabilities.append(1)
        else:
            probabilities.append(prob_identity)
        instructions.append([{"name": "id", "qubits": [0]}])

    # Add Kraus
    if prob_kraus < atol:
        # No Kraus operators
        return zip(instructions, probabilities)
    if prob_kraus < 1:
        # Rescale kraus operators by probabilities
        non_unitaries = [
            np.array(op) / np.sqrt(prob_kraus) for op in non_unitaries
        ]
    instructions.append(make_kraus_instruction(non_unitaries, qubits))
    probabilities.append(prob_kraus)
    # Normalize probabilities to account for any rounding errors
    probabilities = list(np.array(probabilities) / np.sum(probabilities))
    return zip(instructions, probabilities)
Пример #20
0
def mixed_unitary_error(noise_ops, standard_gates=None):
    """
    Return a mixed unitary quantum error channel.

    The input should be a list of pairs ``(U[j], p[j])``, where
    ``U[j]`` is a unitary matrix and ``p[j]`` is a probability. All
    probabilities must sum to 1 for the input ops to be valid.

    Args:
        noise_ops (list[pair[matrix, double]]): unitary error matrices.
        standard_gates (bool): DEPRECATED, Check if input matrices are standard gates.

    Returns:
        QuantumError: The quantum error object.

    Raises:
        NoiseError: if error parameters are invalid.
    """
    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.'
            ' Use directly init e.g. QuantumError([(IGate(), prob1), (ZGate(), prob2)]) instead.',
            DeprecationWarning,
            stacklevel=2)

    # Error checking
    if not isinstance(noise_ops, (list, tuple, zip)):
        raise NoiseError("Input noise ops is not a list.")

    # Convert to numpy arrays
    noise_ops = [(np.array(op, dtype=complex), p) for op, p in noise_ops]
    if not noise_ops:
        raise NoiseError("Input noise list is empty.")

    # Check for identity unitaries
    prob_identity = 0.
    instructions = []
    instructions_probs = []
    num_qubits = int(np.log2(noise_ops[0][0].shape[0]))
    if noise_ops[0][0].shape != (2**num_qubits, 2**num_qubits):
        raise NoiseError(
            "A unitary matrix in input noise_ops is not a multi-qubit matrix.")
    for unitary, prob in noise_ops:
        # Check unitary
        if unitary.shape != noise_ops[0][0].shape:
            raise NoiseError("Input matrices different size.")
        if not is_unitary_matrix(unitary):
            raise NoiseError("Input matrix is not unitary.")
        if is_identity_matrix(unitary):
            prob_identity += prob
        else:
            if standard_gates:  # TODO: to be removed after deprecation period
                qubits = list(range(num_qubits))
                instr = _make_unitary_instruction(
                    unitary, qubits, standard_gates=standard_gates)
            else:
                instr = UnitaryGate(unitary)
            instructions.append(instr)
            instructions_probs.append(prob)
    if prob_identity > 0:
        if standard_gates:  # TODO: to be removed after deprecation period
            instructions.append([{"name": "id", "qubits": [0]}])
        else:
            instructions.append(IGate())
        instructions_probs.append(prob_identity)
    return QuantumError(zip(instructions, instructions_probs))
Пример #21
0
 def is_unitary(self):
     """Return True if operator is a unitary matrix."""
     return is_unitary_matrix(self._data, rtol=self._rtol, atol=self._atol)
Пример #22
0
def two_qubit_kak(unitary):
    """Decompose a two-qubit gate over SU(2)+CNOT using the KAK decomposition.

    Args:
        unitary (Operator): a 4x4 unitary operator to decompose.

    Returns:
        QuantumCircuit: a circuit implementing the unitary over SU(2)+CNOT

    Raises:
        QiskitError: input not a unitary, or error in KAK decomposition.
    """
    if hasattr(unitary, 'to_operator'):
        # If input is a BaseOperator subclass this attempts to convert
        # the object to an Operator so that we can extract the underlying
        # numpy matrix from `Operator.data`.
        unitary = unitary.to_operator().data
    if hasattr(unitary, 'to_matrix'):
        # If input is Gate subclass or some other class object that has
        # a to_matrix method this will call that method.
        unitary = unitary.to_matrix()
    # Convert to numpy array incase not already an array
    unitary_matrix = np.array(unitary, dtype=complex)
    # Check input is a 2-qubit unitary
    if unitary_matrix.shape != (4, 4):
        raise QiskitError("two_qubit_kak: Expected 4x4 matrix")
    if not is_unitary_matrix(unitary_matrix):
        raise QiskitError("Input matrix is not unitary.")
    phase = la.det(unitary_matrix)**(-1.0 / 4.0)
    # Make it in SU(4), correct phase at the end
    U = phase * unitary_matrix
    # B changes to the Bell basis
    B = (1.0 / math.sqrt(2)) * np.array(
        [[1, 1j, 0, 0], [0, 0, 1j, 1], [0, 0, 1j, -1], [1, -1j, 0, 0]],
        dtype=complex)

    # We also need B.conj().T below
    Bdag = B.conj().T
    # U' = Bdag . U . B
    Uprime = Bdag.dot(U.dot(B))
    # M^2 = trans(U') . U'
    M2 = Uprime.T.dot(Uprime)

    # Diagonalize M2
    # Must use diagonalization routine which finds a real orthogonal matrix P
    # when M2 is real.
    D, P = la.eig(M2)
    D = np.diag(D)
    # If det(P) == -1 then in O(4), apply a swap to make P in SO(4)
    if abs(la.det(P) + 1) < 1e-5:
        swap = np.array(
            [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]],
            dtype=complex)
        P = P.dot(swap)
        D = swap.dot(D.dot(swap))

    Q = np.sqrt(D)  # array from elementwise sqrt
    # Want to take square root so that Q has determinant 1
    if abs(la.det(Q) + 1) < 1e-5:
        Q[0, 0] = -Q[0, 0]

    # Q^-1*P.T = P' -> QP' = P.T (solve for P' using Ax=b)
    Pprime = la.solve(Q, P.T)
    # K' now just U' * P * P'
    Kprime = Uprime.dot(P.dot(Pprime))

    K1 = B.dot(Kprime.dot(P.dot(Bdag)))
    A = B.dot(Q.dot(Bdag))
    K2 = B.dot(P.T.dot(Bdag))
    # KAK = K1 * A * K2
    KAK = K1.dot(A.dot(K2))

    # Verify decomp matches input unitary.
    if la.norm(KAK - U) > 1e-6:
        raise QiskitError("two_qubit_kak: KAK decomposition " +
                          "does not return input unitary.")

    # Compute parameters alpha, beta, gamma so that
    # A = exp(i * (alpha * XX + beta * YY + gamma * ZZ))
    xx = np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]],
                  dtype=complex)

    yy = np.array([[0, 0, 0, -1], [0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0]],
                  dtype=complex)

    zz = np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]],
                  dtype=complex)

    A_real_tr = A.real.trace()
    alpha = math.atan2(A.dot(xx).imag.trace(), A_real_tr)
    beta = math.atan2(A.dot(yy).imag.trace(), A_real_tr)
    gamma = math.atan2(A.dot(zz).imag.trace(), A_real_tr)

    # K1 = kron(U1, U2) and K2 = kron(V1, V2)
    # Find the matrices U1, U2, V1, V2

    # Find a block in K1 where U1_ij * [U2] is not zero
    L = K1[0:2, 0:2]
    if la.norm(L) < 1e-9:
        L = K1[0:2, 2:4]
        if la.norm(L) < 1e-9:
            L = K1[2:4, 2:4]
    # Remove the U1_ij prefactor
    Q = L.dot(L.conj().T)
    U2 = L / math.sqrt(Q[0, 0].real)

    # Now grab U1 given we know U2
    R = K1.dot(np.kron(np.identity(2), U2.conj().T))
    U1 = np.zeros((2, 2), dtype=complex)
    U1[0, 0] = R[0, 0]
    U1[0, 1] = R[0, 2]
    U1[1, 0] = R[2, 0]
    U1[1, 1] = R[2, 2]

    # Repeat K1 routine for K2
    L = K2[0:2, 0:2]
    if la.norm(L) < 1e-9:
        L = K2[0:2, 2:4]
        if la.norm(L) < 1e-9:
            L = K2[2:4, 2:4]
    Q = np.dot(L, np.transpose(L.conjugate()))
    V2 = L / np.sqrt(Q[0, 0])
    R = np.dot(K2, np.kron(np.identity(2), np.transpose(V2.conjugate())))

    V1 = np.zeros_like(U1)
    V1[0, 0] = R[0, 0]
    V1[0, 1] = R[0, 2]
    V1[1, 0] = R[2, 0]
    V1[1, 1] = R[2, 2]

    if la.norm(np.kron(U1, U2) - K1) > 1e-4:
        raise QiskitError("two_qubit_kak: K1 != U1 x U2")
    if la.norm(np.kron(V1, V2) - K2) > 1e-4:
        raise QiskitError("two_qubit_kak: K2 != V1 x V2")

    test = la.expm(1j * (alpha * xx + beta * yy + gamma * zz))
    if la.norm(A - test) > 1e-4:
        raise QiskitError("two_qubit_kak: " +
                          "Matrix A does not match xx,yy,zz decomposition.")

    # Circuit that implements K1 * A * K2 (up to phase), using
    # Vatan and Williams Fig. 6 of quant-ph/0308006v3
    # Include prefix and suffix single-qubit gates into U2, V1 respectively.

    V2 = np.array([[np.exp(1j * np.pi / 4), 0], [0, np.exp(-1j * np.pi / 4)]],
                  dtype=complex).dot(V2)
    U1 = U1.dot(
        np.array([[np.exp(-1j * np.pi / 4), 0], [0, np.exp(1j * np.pi / 4)]],
                 dtype=complex))

    # Corrects global phase: exp(ipi/4)*phase'
    U1 = U1.dot(
        np.array([[np.exp(1j * np.pi / 4), 0], [0, np.exp(1j * np.pi / 4)]],
                 dtype=complex))
    U1 = phase.conjugate() * U1

    # Test
    g1 = np.kron(V1, V2)
    g2 = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]],
                  dtype=complex)

    theta = 2 * gamma - np.pi / 2

    Ztheta = np.array(
        [[np.exp(1j * theta / 2), 0], [0, np.exp(-1j * theta / 2)]],
        dtype=complex)

    kappa = np.pi / 2 - 2 * alpha
    Ykappa = np.array(
        [[math.cos(kappa / 2), math.sin(kappa / 2)],
         [-math.sin(kappa / 2), math.cos(kappa / 2)]],
        dtype=complex)
    g3 = np.kron(Ztheta, Ykappa)
    g4 = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]],
                  dtype=complex)

    zeta = 2 * beta - np.pi / 2
    Yzeta = np.array(
        [[math.cos(zeta / 2), math.sin(zeta / 2)],
         [-math.sin(zeta / 2), math.cos(zeta / 2)]],
        dtype=complex)
    g5 = np.kron(np.identity(2), Yzeta)
    g6 = g2
    g7 = np.kron(U1, U2)

    V = g2.dot(g1)
    V = g3.dot(V)
    V = g4.dot(V)
    V = g5.dot(V)
    V = g6.dot(V)
    V = g7.dot(V)

    if la.norm(V - U * phase.conjugate()) > 1e-6:
        raise QiskitError("two_qubit_kak: " +
                          "sequence incorrect, unknown error")

    v1_param = euler_angles_1q(V1)
    v2_param = euler_angles_1q(V2)
    u1_param = euler_angles_1q(U1)
    u2_param = euler_angles_1q(U2)

    v1_gate = U3Gate(v1_param[0], v1_param[1], v1_param[2])
    v2_gate = U3Gate(v2_param[0], v2_param[1], v2_param[2])
    u1_gate = U3Gate(u1_param[0], u1_param[1], u1_param[2])
    u2_gate = U3Gate(u2_param[0], u2_param[1], u2_param[2])

    q = QuantumRegister(2)
    return_circuit = QuantumCircuit(q)

    return_circuit.append(v1_gate, [q[1]])

    return_circuit.append(v2_gate, [q[0]])

    return_circuit.append(CnotGate(), [q[0], q[1]])

    gate = U3Gate(0.0, 0.0, -2.0 * gamma + np.pi / 2.0)
    return_circuit.append(gate, [q[1]])

    gate = U3Gate(-np.pi / 2.0 + 2.0 * alpha, 0.0, 0.0)
    return_circuit.append(gate, [q[0]])

    return_circuit.append(CnotGate(), [q[1], q[0]])

    gate = U3Gate(-2.0 * beta + np.pi / 2.0, 0.0, 0.0)
    return_circuit.append(gate, [q[0]])

    return_circuit.append(CnotGate(), [q[0], q[1]])

    return_circuit.append(u1_gate, [q[1]])

    return_circuit.append(u2_gate, [q[0]])

    return return_circuit
Пример #23
0
 def is_cptp(self):
     """Return True if completely-positive trace-preserving."""
     # If the matrix is a unitary matrix the channel is CPTP
     return is_unitary_matrix(self._data, rtol=self.rtol, atol=self.atol)