Ejemplo n.º 1
0
 def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL):
     gphase = phase - (phi + lam) / 2
     qr = QuantumRegister(1, "qr")
     circuit = QuantumCircuit(qr)
     if not simplify:
         atol = -1.0
     if abs(theta) < atol:
         tot = _mod_2pi(phi + lam, atol)
         if abs(tot) > atol:
             circuit._append(RXGate(tot), [qr[0]], [])
             gphase += tot / 2
         circuit.global_phase = gphase
         return circuit
     if abs(theta - np.pi) < atol:
         gphase += phi
         lam, phi = lam - phi, 0
     lam = _mod_2pi(lam, atol)
     if abs(lam) > atol:
         gphase += lam / 2
         circuit._append(RXGate(lam), [qr[0]], [])
     circuit._append(RYGate(theta), [qr[0]], [])
     phi = _mod_2pi(phi, atol)
     if abs(phi) > atol:
         gphase += phi / 2
         circuit._append(RXGate(phi), [qr[0]], [])
     circuit.global_phase = gphase
     return circuit
Ejemplo n.º 2
0
 def _circuit_u1x(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL):
     # Shift theta and phi so decomposition is
     # U1(phi).X90.U1(theta).X90.U1(lam)
     theta += np.pi
     phi += np.pi
     # Check for decomposition into minimimal number required X90 pulses
     if simplify and np.isclose(abs(theta), np.pi, atol=atol):
         # Zero X90 gate decomposition
         circuit = QuantumCircuit(1, global_phase=phase)
         circuit.append(U1Gate(lam + phi + theta), [0])
         return circuit
     if simplify and np.isclose(abs(theta), np.pi / 2, atol=atol):
         # Single X90 gate decomposition
         circuit = QuantumCircuit(1, global_phase=phase)
         circuit.append(U1Gate(lam + theta), [0])
         circuit.append(RXGate(np.pi / 2), [0])
         circuit.append(U1Gate(phi + theta), [0])
         return circuit
     # General two-X90 gate decomposition
     circuit = QuantumCircuit(1, global_phase=phase)
     circuit.append(U1Gate(lam), [0])
     circuit.append(RXGate(np.pi / 2), [0])
     circuit.append(U1Gate(theta), [0])
     circuit.append(RXGate(np.pi / 2), [0])
     circuit.append(U1Gate(phi), [0])
     return circuit
Ejemplo n.º 3
0
 def specialize(self):
     self.b = self.c = (self.b + self.c) / 2
     k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l)
     self.global_phase += k2lphase
     self.K1r = self.K1r @ np.asarray(RXGate(k2lphi))
     self.K1l = self.K1l @ np.asarray(RXGate(k2lphi))
     self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda))
     self.K2r = np.asarray(RXGate(-k2lphi)) @ self.K2r
Ejemplo n.º 4
0
 def __init__(self):
     super().__init__(
         None,
         name="FakeV2",
         description="A fake BackendV2 example",
         online_date=datetime.datetime.utcnow(),
         backend_version="0.0.1",
     )
     self._target = Target()
     self._theta = Parameter("theta")
     self._phi = Parameter("phi")
     self._lam = Parameter("lambda")
     rx_props = {
         (0, ): InstructionProperties(duration=5.23e-8, error=0.00038115),
         (1, ): InstructionProperties(duration=4.52e-8, error=0.00032115),
     }
     self._target.add_instruction(RXGate(self._theta), rx_props)
     rx_30_props = {
         (0, ): InstructionProperties(duration=1.23e-8, error=0.00018115),
         (1, ): InstructionProperties(duration=1.52e-8, error=0.00012115),
     }
     self._target.add_instruction(RXGate(np.pi / 6),
                                  rx_30_props,
                                  name="rx_30")
     u_props = {
         (0, ): InstructionProperties(duration=5.23e-8, error=0.00038115),
         (1, ): InstructionProperties(duration=4.52e-8, error=0.00032115),
     }
     self._target.add_instruction(UGate(self._theta, self._phi, self._lam),
                                  u_props)
     cx_props = {
         (0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115),
     }
     self._target.add_instruction(CXGate(), cx_props)
     measure_props = {
         (0, ): InstructionProperties(duration=6e-6, error=5e-6),
         (1, ): InstructionProperties(duration=1e-6, error=9e-6),
     }
     self._target.add_instruction(Measure(), measure_props)
     ecr_props = {
         (1, 0): InstructionProperties(duration=4.52e-9,
                                       error=0.0000132115),
     }
     self._target.add_instruction(ECRGate(), ecr_props)
     self.options.set_validator("shots", (1, 4096))
     self._qubit_properties = {
         0:
         QubitProperties(t1=63.48783e-6,
                         t2=112.23246e-6,
                         frequency=5.17538e9),
         1:
         QubitProperties(t1=73.09352e-6,
                         t2=126.83382e-6,
                         frequency=5.26722e9),
     }
Ejemplo n.º 5
0
 def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL):
     circuit = QuantumCircuit(1, global_phase=phase)
     if simplify and np.isclose(theta, 0.0, atol=atol):
         circuit.append(RXGate(phi + lam), [0])
         return circuit
     if not simplify or not np.isclose(lam, 0.0, atol=atol):
         circuit.append(RXGate(lam), [0])
     if not simplify or not np.isclose(theta, 0.0, atol=atol):
         circuit.append(RYGate(theta), [0])
     if not simplify or not np.isclose(phi, 0.0, atol=atol):
         circuit.append(RXGate(phi), [0])
     return circuit
 def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL):
     qr = QuantumRegister(1, 'qr')
     circuit = QuantumCircuit(qr, global_phase=phase)
     if simplify and math.isclose(theta, 0.0, abs_tol=atol):
         circuit._append(RXGate(phi + lam), [qr[0]], [])
         return circuit
     if not simplify or not math.isclose(lam, 0.0, abs_tol=atol):
         circuit._append(RXGate(lam), [qr[0]], [])
     if not simplify or not math.isclose(theta, 0.0, abs_tol=atol):
         circuit._append(RYGate(theta), [qr[0]], [])
     if not simplify or not math.isclose(phi, 0.0, abs_tol=atol):
         circuit._append(RXGate(phi), [qr[0]], [])
     return circuit
    def run(self, dag):

        runs = dag.collect_runs([self.gate])
        runs = _split_runs_on_parameters(runs)

        for group in runs:
            angle = sum([float(node.op.params[0]) for node in group])
            angle = sign(angle) * (abs(angle) % (2 * pi))

            # for angles multiple of 2*pi (numerically close),
            #  the gates act as an identity (up to a global phase).
            if abs(angle) < self.eps:
                dag.remove_op_node(group[0])
            else:
                if self.gate == 'rx':
                    new_op = RXGate(angle)
                elif self.gate == 'rz':
                    new_op = RZGate(angle)

                dag.substitute_node(group[0], new_op,
                                    inplace=True)
            for node in group[1:]:
                dag.remove_op_node(node)

        return dag
Ejemplo n.º 8
0
def makeCircuit6(graph, n, edges, cedges, gammasbetas, p):
    cgraph = nx.complement(graph)
    ans = ClassicalRegister(n)
    sol = QuantumRegister(n)
    QAOA = QuantumCircuit(sol, ans)
    r = 2

    for j in range(p):

        for i in range(n):
            QAOA.u1(-gammasbetas[j], i)

        for l in range(r):
            for i in range(n):
                nbrs = 0
                nbs = []
                for nbr in cgraph[i]:
                    QAOA.x(nbr)
                    nbrs += 1
                    nbs.append(nbr)

                nbs.append(i)
                if (nbrs != 0):
                    #gate = MCMT(RXGate(gammasbetas[p+j]/r),nbrs ,1)
                    gate = MCMT(RXGate(2 * gammasbetas[l + j] / r), nbrs, 1)
                    QAOA.append(gate, nbs)
                else:
                    #QAOA.rx(gammasbetas[p+j]/r,i)
                    QAOA.rx(2 * gammasbetas[l + j] / r, i)
                for nbr in cgraph[i]:
                    QAOA.x(nbr)

    return QAOA
Ejemplo n.º 9
0
def makeCircuit9(graph, n, edges, cedges, gammasbetas, p, orderings):
    cgraph = nx.complement(graph)
    ans = ClassicalRegister(n)
    sol = QuantumRegister(n)
    QAOA = QuantumCircuit(sol, ans)
    for j in range(p):
        if (j != 0):

            for i in range(n):

                QAOA.u1(-gammasbetas[j - 1], i)
                QAOA.barrier()

        for i in orderings[j]:

            nbrs = 0
            nbs = []
            for nbr in cgraph[i]:
                QAOA.x(nbr)
                nbrs += 1
                nbs.append(nbr)

            nbs.append(i)
            if (nbrs != 0):
                gate = MCMT(RXGate(2 * gammasbetas[p - 1 + j]), nbrs, 1)
                QAOA.append(gate, nbs)
            else:
                QAOA.rx(2 * gammasbetas[p - 1 + j], i)
            for nbr in cgraph[i]:
                QAOA.x(nbr)
            QAOA.barrier()
    return QAOA
Ejemplo n.º 10
0
 def _circuit_zxz(theta, phi, lam, simplify=False, atol=DEFAULT_ATOL):
     if simplify and np.isclose(theta, 0.0, atol=atol):
         circuit = QuantumCircuit(1)
         circuit.append(RZGate(phi + lam), [0])
         return circuit
     circuit = QuantumCircuit(1)
     if not simplify or not np.isclose(lam, 0.0, atol=atol):
         circuit.append(RZGate(lam), [0])
     if not simplify or not np.isclose(theta, 0.0, atol=atol):
         circuit.append(RXGate(theta), [0])
     if not simplify or not np.isclose(phi, 0.0, atol=atol):
         circuit.append(RZGate(phi), [0])
     return circuit
Ejemplo n.º 11
0
def makeCircuit8(graph, n, edges, cedges, gammasbetas, p, orderings):
    cgraph = nx.complement(graph)
    ans = ClassicalRegister(n)
    sol = QuantumRegister(n)
    QAOA = QuantumCircuit(sol, ans)

    k = 1
    #dickestate
    for i in range(n - 1, n - k - 1, -1):
        QAOA.x(i)
    for l in range(n, k, -1):
        scs(l, k, QAOA, n)
    for l in range(k, 1, -1):
        scs(l, l - 1, QAOA, n)

    for j in range(p):
        for i in range(n):
            QAOA.u1(-gammasbetas[j], i)

        for i in orderings[j]:

            nbrs = 0
            nbs = []
            for nbr in cgraph[i]:
                QAOA.x(nbr)
                nbrs += 1
                nbs.append(nbr)

            nbs.append(i)
            if (nbrs != 0):
                gate = MCMT(RXGate(gammasbetas[p + j]), nbrs, 1)
                QAOA.append(gate, nbs)
            else:
                QAOA.rx(gammasbetas[p + j], i)
            for nbr in cgraph[i]:
                QAOA.x(nbr)

    return QAOA
Ejemplo n.º 12
0
def makeCircuit5(graph, n, edges, cedges, gammasbetas, p):
    cgraph = nx.complement(graph)
    ans = ClassicalRegister(n)
    sol = QuantumRegister(n)
    QAOA = QuantumCircuit(sol, ans)
    k = 1
    #dickestate
    for i in range(n - 1, n - k - 1, -1):
        QAOA.x(i)
    for l in range(n, k, -1):
        scs(l, k, QAOA, n)
    for l in range(k, 1, -1):
        scs(l, l - 1, QAOA, n)
    #mixer and phaseseparation
    for j in range(p):
        for i in range(n):
            QAOA.u1(-gammasbetas[j], i)
        for i in range(n):
            nbrs = 0
            nbs = []
            for nbr in cgraph[i]:
                QAOA.x(nbr)
                nbrs += 1
                nbs.append(nbr)

            nbs.append(i)
            if (nbrs != 0):
                gate = MCMT(RXGate(gammasbetas[p + j]), nbrs, 1)
                QAOA.append(gate, nbs)
            #else:
            #warum habe ich das ausgeklammert?  Testen ob es besser ist?
            #QAOA.rx(gammasbetas[p+j],i)
            for nbr in cgraph[i]:
                QAOA.x(nbr)

    return QAOA
Ejemplo n.º 13
0
 def fnx(circuit, qr):
     circuit.global_phase += np.pi / 4
     circuit._append(RXGate(np.pi / 2), [qr[0]], [])
    def _get_sx_vz_3cx_efficient_euler(self, decomposition, target_decomposed):
        """
        Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming
        three CNOT gates are needed.

        This first decomposes each unitary from the KAK decomposition into ZXZ on the source
        qubit of the CNOTs and XZX on the targets in order commute operators to beginning and
        end of decomposition. Inserting Hadamards reverses the direction of the CNOTs and transforms
        a variable Rx -> variable virtual Rz. The beginning and ending single qubit gates are then
        collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
        if performance is a concern.
        """
        best_nbasis = 3  # by assumption
        num_1q_uni = len(decomposition)
        # create structure to hold euler angles: 1st index represents unitary "group" wrt cx
        # 2nd index represents index of euler triple.
        euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
        euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
        global_phase = 0.0
        atol = 1e-10  # absolute tolerance for floats

        # decompose source unitaries to zxz
        zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
        for iqubit, decomp in enumerate(decomposition[0::2]):
            euler_angles = zxz_decomposer.angles_and_phase(decomp)
            euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
            global_phase += euler_angles[3]
        # decompose target unitaries to xzx
        xzx_decomposer = OneQubitEulerDecomposer("XZX")
        for iqubit, decomp in enumerate(decomposition[1::2]):
            euler_angles = xzx_decomposer.angles_and_phase(decomp)
            euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
            global_phase += euler_angles[3]

        qc = QuantumCircuit(2)
        qc.global_phase = target_decomposed.global_phase
        qc.global_phase -= best_nbasis * self.basis.global_phase
        qc.global_phase += global_phase

        x12 = euler_q0[1][2] + euler_q0[2][0]
        x12_isNonZero = not math.isclose(x12, 0, abs_tol=atol)
        x12_isOddMult = None
        x12_isPiMult = math.isclose(math.sin(x12), 0, abs_tol=atol)
        if x12_isPiMult:
            x12_isOddMult = math.isclose(math.cos(x12), -1, abs_tol=atol)
            x12_phase = math.pi * math.cos(x12)
        x02_add = x12 - euler_q0[1][0]
        x12_isHalfPi = math.isclose(x12, math.pi / 2, abs_tol=atol)

        # TODO: make this more effecient to avoid double decomposition
        circ = QuantumCircuit(1)
        circ.rz(euler_q0[0][0], 0)
        circ.rx(euler_q0[0][1], 0)
        if x12_isNonZero and x12_isPiMult:
            circ.rz(euler_q0[0][2] - x02_add, 0)
        else:
            circ.rz(euler_q0[0][2] + euler_q0[1][0], 0)
        circ.h(0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [0], inplace=True)

        circ = QuantumCircuit(1)
        circ.rx(euler_q1[0][0], 0)
        circ.rz(euler_q1[0][1], 0)
        circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
        circ.h(0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [1], inplace=True)

        qc.cx(1, 0)

        if x12_isPiMult:
            # even or odd multiple
            if x12_isNonZero:
                qc.global_phase += x12_phase
            if x12_isNonZero and x12_isOddMult:
                qc.rz(-euler_q0[1][1], 0)
            else:
                qc.rz(euler_q0[1][1], 0)
                qc.global_phase += math.pi
        if x12_isHalfPi:
            qc.sx(0)
            qc.global_phase -= math.pi / 4
        elif x12_isNonZero and not x12_isPiMult:
            # this is non-optimal but doesn't seem to occur currently
            if self.pulse_optimize is None:
                qc.compose(self._decomposer1q(Operator(RXGate(x12)).data), [0],
                           inplace=True)
            else:
                raise QiskitError(
                    "possible non-pulse-optimal decomposition encountered")
        if math.isclose(euler_q1[1][1], math.pi / 2, abs_tol=atol):
            qc.sx(1)
            qc.global_phase -= math.pi / 4
        else:
            # this is non-optimal but doesn't seem to occur currently
            if self.pulse_optimize is None:
                qc.compose(self._decomposer1q(
                    Operator(RXGate(euler_q1[1][1])).data), [1],
                           inplace=True)
            else:
                raise QiskitError(
                    "possible non-pulse-optimal decomposition encountered")
        qc.rz(euler_q1[1][2] + euler_q1[2][0], 1)

        qc.cx(1, 0)

        qc.rz(euler_q0[2][1], 0)
        if math.isclose(euler_q1[2][1], math.pi / 2, abs_tol=atol):
            qc.sx(1)
            qc.global_phase -= math.pi / 4
        else:
            # this is non-optimal but doesn't seem to occur currently
            if self.pulse_optimize is None:
                qc.compose(self._decomposer1q(
                    Operator(RXGate(euler_q1[2][1])).data), [1],
                           inplace=True)
            else:
                raise QiskitError(
                    "possible non-pulse-optimal decomposition encountered")

        qc.cx(1, 0)

        circ = QuantumCircuit(1)
        circ.h(0)
        circ.rz(euler_q0[2][2] + euler_q0[3][0], 0)
        circ.rx(euler_q0[3][1], 0)
        circ.rz(euler_q0[3][2], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [0], inplace=True)

        circ = QuantumCircuit(1)
        circ.h(0)
        circ.rx(euler_q1[2][2] + euler_q1[3][0], 0)
        circ.rz(euler_q1[3][1], 0)
        circ.rx(euler_q1[3][2], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [1], inplace=True)

        # TODO: fix the sign problem to avoid correction here
        if cmath.isclose(target_decomposed.unitary_matrix[0, 0],
                         -(Operator(qc).data[0, 0]),
                         abs_tol=atol):
            qc.global_phase += math.pi
        return qc
Ejemplo n.º 15
0
    def _hessian_states(
        self,
        state_op: StateFn,
        meas_op: Optional[OperatorBase] = None,
        target_params: Optional[Union[Tuple[ParameterExpression,
                                            ParameterExpression],
                                      List[Tuple[ParameterExpression,
                                                 ParameterExpression]]]] = None
    ) -> OperatorBase:
        """Generate the operator states whose evaluation returns the Hessian (items).

        Args:
            state_op: The operator representing the quantum state for which we compute the Hessian.
            meas_op: The operator representing the observable for which we compute the gradient.
            target_params: The parameters we are computing the Hessian wrt: ω

        Returns:
            Operators which give the Hessian. If a parameter appears multiple times, one circuit is
            created per parameterized gates to compute the product rule.

        Raises:
            AquaError: If one of the circuits could not be constructed.
            TypeError: If ``operator`` is of unsupported type.
        """
        state_qc = deepcopy(state_op.primitive)
        if isinstance(target_params, list) and isinstance(
                target_params[0], tuple):
            tuples_list = deepcopy(target_params)
            target_params = []
            for tuples in tuples_list:
                if all([
                        param in state_qc._parameter_table.get_keys()
                        for param in tuples
                ]):
                    for param in tuples:
                        if param not in target_params:
                            target_params.append(param)
        elif isinstance(target_params, tuple):
            tuples_list = deepcopy([target_params])
            target_params = []
            for tuples in tuples_list:
                if all([
                        param in state_qc._parameter_table.get_keys()
                        for param in tuples
                ]):
                    for param in tuples:
                        if param not in target_params:
                            target_params.append(param)
        else:
            raise TypeError(
                'Please define in the parameters for which the Hessian is evaluated either '
                'as parameter tuple or a list of parameter tuples')

        qr_add0 = QuantumRegister(1, 'work_qubit0')
        work_q0 = qr_add0[0]
        qr_add1 = QuantumRegister(1, 'work_qubit1')
        work_q1 = qr_add1[0]
        # create a copy of the original circuit with an additional working qubit register
        circuit = state_qc.copy()
        circuit.add_register(qr_add0, qr_add1)
        # Get the circuits needed to compute the Hessian
        hessian_ops = None
        for param_a, param_b in tuples_list:

            if param_a not in state_qc._parameter_table.get_keys() or param_b \
                    not in state_qc._parameter_table.get_keys():
                hessian_op = ~Zero @ One
            else:
                param_gates_a = state_qc._parameter_table[param_a]
                param_gates_b = state_qc._parameter_table[param_b]
                for i, param_occurence_a in enumerate(param_gates_a):
                    coeffs_a, gates_a = self._gate_gradient_dict(
                        param_occurence_a[0])[param_occurence_a[1]]
                    # apply Hadamard on working qubit
                    self.insert_gate(circuit,
                                     param_occurence_a[0],
                                     HGate(),
                                     qubits=[work_q0])
                    self.insert_gate(circuit,
                                     param_occurence_a[0],
                                     HGate(),
                                     qubits=[work_q1])
                    for j, gate_to_insert_a in enumerate(gates_a):

                        coeff_a = coeffs_a[j]
                        hessian_circuit_temp = QuantumCircuit(*circuit.qregs)
                        hessian_circuit_temp.data = circuit.data
                        # Fix working qubit 0 phase
                        sign = np.sign(coeff_a)
                        is_complex = np.iscomplex(coeff_a)
                        if sign == -1:
                            if is_complex:
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 SdgGate(),
                                                 qubits=[work_q0])
                            else:
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 ZGate(),
                                                 qubits=[work_q0])
                        else:
                            if is_complex:
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 SGate(),
                                                 qubits=[work_q0])

                        # Insert controlled, intercepting gate - controlled by |1>
                        if isinstance(param_occurence_a[0], UGate):
                            if param_occurence_a[1] == 0:
                                self.insert_gate(
                                    hessian_circuit_temp, param_occurence_a[0],
                                    RZGate(param_occurence_a[0].params[2]))
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 RXGate(np.pi / 2))
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 gate_to_insert_a,
                                                 additional_qubits=([work_q0],
                                                                    []))
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 RXGate(-np.pi / 2))
                                self.insert_gate(
                                    hessian_circuit_temp, param_occurence_a[0],
                                    RZGate(-param_occurence_a[0].params[2]))

                            elif param_occurence_a[1] == 1:
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 gate_to_insert_a,
                                                 after=True,
                                                 additional_qubits=([work_q0],
                                                                    []))
                            else:
                                self.insert_gate(hessian_circuit_temp,
                                                 param_occurence_a[0],
                                                 gate_to_insert_a,
                                                 additional_qubits=([work_q0],
                                                                    []))
                        else:
                            self.insert_gate(hessian_circuit_temp,
                                             param_occurence_a[0],
                                             gate_to_insert_a,
                                             additional_qubits=([work_q0], []))

                        for m, param_occurence_b in enumerate(param_gates_b):
                            coeffs_b, gates_b = self._gate_gradient_dict(
                                param_occurence_b[0])[param_occurence_b[1]]
                            for n, gate_to_insert_b in enumerate(gates_b):
                                coeff_b = coeffs_b[n]
                                # create a copy of the original circuit with the same registers
                                hessian_circuit = QuantumCircuit(
                                    *hessian_circuit_temp.qregs)
                                hessian_circuit.data = hessian_circuit_temp.data

                                # Fix working qubit 1 phase
                                sign = np.sign(coeff_b)
                                is_complex = np.iscomplex(coeff_b)
                                if sign == -1:
                                    if is_complex:
                                        self.insert_gate(hessian_circuit,
                                                         param_occurence_b[0],
                                                         SdgGate(),
                                                         qubits=[work_q1])
                                    else:
                                        self.insert_gate(hessian_circuit,
                                                         param_occurence_b[0],
                                                         ZGate(),
                                                         qubits=[work_q1])
                                else:
                                    if is_complex:
                                        self.insert_gate(hessian_circuit,
                                                         param_occurence_b[0],
                                                         SGate(),
                                                         qubits=[work_q1])

                                # Insert controlled, intercepting gate - controlled by |1>

                                if isinstance(param_occurence_b[0], UGate):
                                    if param_occurence_b[1] == 0:
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            RZGate(param_occurence_b[0].
                                                   params[2]))
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            RXGate(np.pi / 2))
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            gate_to_insert_b,
                                            additional_qubits=([work_q1], []))
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            RXGate(-np.pi / 2))
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            RZGate(-param_occurence_b[0].
                                                   params[2]))

                                    elif param_occurence_b[1] == 1:
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            gate_to_insert_b,
                                            after=True,
                                            additional_qubits=([work_q1], []))
                                    else:
                                        self.insert_gate(
                                            hessian_circuit,
                                            param_occurence_b[0],
                                            gate_to_insert_b,
                                            additional_qubits=([work_q1], []))
                                else:
                                    self.insert_gate(
                                        hessian_circuit,
                                        param_occurence_b[0],
                                        gate_to_insert_b,
                                        additional_qubits=([work_q1], []))

                                hessian_circuit.h(work_q0)
                                hessian_circuit.cz(work_q1, work_q0)
                                hessian_circuit.h(work_q1)

                                term = state_op.coeff * np.sqrt(np.abs(coeff_a) * np.abs(coeff_b)) \
                                                      * CircuitStateFn(hessian_circuit)

                                # Chain Rule Parameter Expression
                                gate_param_a = param_occurence_a[0].params[
                                    param_occurence_a[1]]
                                gate_param_b = param_occurence_b[0].params[
                                    param_occurence_b[1]]

                                if meas_op:
                                    meas = deepcopy(meas_op)
                                    if isinstance(gate_param_a,
                                                  ParameterExpression):
                                        expr_grad = DerivativeBase.parameter_expression_grad(
                                            gate_param_a, param_a)
                                        meas *= expr_grad
                                    if isinstance(gate_param_b,
                                                  ParameterExpression):
                                        expr_grad = DerivativeBase.parameter_expression_grad(
                                            gate_param_a, param_a)
                                        meas *= expr_grad
                                    term = meas @ term
                                else:
                                    term = ListOp([term],
                                                  combo_fn=partial(
                                                      self._hess_combo_fn,
                                                      state_op=state_op))
                                    if isinstance(gate_param_a,
                                                  ParameterExpression):
                                        expr_grad = DerivativeBase.parameter_expression_grad(
                                            gate_param_a, param_a)
                                        term *= expr_grad
                                    if isinstance(gate_param_b,
                                                  ParameterExpression):
                                        expr_grad = DerivativeBase.parameter_expression_grad(
                                            gate_param_a, param_a)
                                        term *= expr_grad

                                if i == 0 and j == 0 and m == 0 and n == 0:
                                    hessian_op = term
                                else:
                                    # Product Rule
                                    hessian_op += term
            # Create a list of Hessian elements w.r.t. the given parameter tuples
            if len(tuples_list) == 1:
                return hessian_op
            else:
                if not hessian_ops:
                    hessian_ops = [hessian_op]
                else:
                    hessian_ops += [hessian_op]
        return ListOp(hessian_ops)
Ejemplo n.º 16
0
    def _gradient_states(
        self,
        state_op: StateFn,
        meas_op: Optional[OperatorBase] = None,
        target_params: Optional[Union[ParameterExpression, ParameterVector,
                                      List[ParameterExpression]]] = None
    ) -> ListOp:
        """Generate the gradient states.

        Args:
            state_op: The operator representing the quantum state for which we compute the gradient.
            meas_op: The operator representing the observable for which we compute the gradient.
            target_params: The parameters we are taking the gradient wrt: ω

        Returns:
            ListOp of StateFns as quantum circuits which are the states w.r.t. which we compute the
            gradient. If a parameter appears multiple times, one circuit is created per
            parameterized gates to compute the product rule.

        Raises:
            AquaError: If one of the circuits could not be constructed.
            TypeError: If the operators is of unsupported type.
        """
        state_qc = deepcopy(state_op.primitive)

        # Define the working qubit to realize the linear combination of unitaries
        qr_work = QuantumRegister(1, 'work_qubit_lin_comb_grad')
        work_q = qr_work[0]

        if not isinstance(target_params, (list, np.ndarray)):
            target_params = [target_params]

        if len(target_params) > 1:
            states = None

        additional_qubits: Tuple[List[Qubit], List[Qubit]] = ([work_q], [])

        for param in target_params:
            if param not in state_qc._parameter_table.get_keys():
                op = ~Zero @ One
            else:
                param_gates = state_qc._parameter_table[param]
                for m, param_occurence in enumerate(param_gates):
                    coeffs, gates = self._gate_gradient_dict(
                        param_occurence[0])[param_occurence[1]]

                    # construct the states
                    for k, gate_to_insert in enumerate(gates):
                        grad_state = QuantumCircuit(*state_qc.qregs, qr_work)
                        grad_state.compose(state_qc, inplace=True)

                        # apply Hadamard on work_q
                        self.insert_gate(grad_state,
                                         param_occurence[0],
                                         HGate(),
                                         qubits=[work_q])

                        # Fix work_q phase
                        coeff_i = coeffs[k]
                        sign = np.sign(coeff_i)
                        is_complex = np.iscomplex(coeff_i)
                        if sign == -1:
                            if is_complex:
                                self.insert_gate(grad_state,
                                                 param_occurence[0],
                                                 SdgGate(),
                                                 qubits=[work_q])
                            else:
                                self.insert_gate(grad_state,
                                                 param_occurence[0],
                                                 ZGate(),
                                                 qubits=[work_q])
                        else:
                            if is_complex:
                                self.insert_gate(grad_state,
                                                 param_occurence[0],
                                                 SGate(),
                                                 qubits=[work_q])

                        # Insert controlled, intercepting gate - controlled by |0>
                        if isinstance(param_occurence[0], UGate):
                            if param_occurence[1] == 0:
                                self.insert_gate(
                                    grad_state, param_occurence[0],
                                    RZGate(param_occurence[0].params[2]))
                                self.insert_gate(grad_state,
                                                 param_occurence[0],
                                                 RXGate(np.pi / 2))
                                self.insert_gate(
                                    grad_state,
                                    param_occurence[0],
                                    gate_to_insert,
                                    additional_qubits=additional_qubits)
                                self.insert_gate(grad_state,
                                                 param_occurence[0],
                                                 RXGate(-np.pi / 2))
                                self.insert_gate(
                                    grad_state, param_occurence[0],
                                    RZGate(-param_occurence[0].params[2]))

                            elif param_occurence[1] == 1:
                                self.insert_gate(
                                    grad_state,
                                    param_occurence[0],
                                    gate_to_insert,
                                    after=True,
                                    additional_qubits=additional_qubits)
                            else:
                                self.insert_gate(
                                    grad_state,
                                    param_occurence[0],
                                    gate_to_insert,
                                    additional_qubits=additional_qubits)
                        else:
                            self.insert_gate(
                                grad_state,
                                param_occurence[0],
                                gate_to_insert,
                                additional_qubits=additional_qubits)
                        grad_state.h(work_q)

                        state = np.sqrt(
                            np.abs(coeff_i)) * state_op.coeff * CircuitStateFn(
                                grad_state)
                        # Chain Rule parameter expressions
                        gate_param = param_occurence[0].params[
                            param_occurence[1]]
                        if meas_op:
                            if gate_param == param:
                                state = meas_op @ state
                            else:
                                if isinstance(gate_param, ParameterExpression):
                                    expr_grad = DerivativeBase.parameter_expression_grad(
                                        gate_param, param)
                                    state = (expr_grad * meas_op) @ state
                                else:
                                    state = ~Zero @ One
                        else:
                            if gate_param == param:
                                state = ListOp([state],
                                               combo_fn=partial(
                                                   self._grad_combo_fn,
                                                   state_op=state_op))
                            else:
                                if isinstance(gate_param, ParameterExpression):
                                    expr_grad = DerivativeBase.parameter_expression_grad(
                                        gate_param, param)
                                    state = expr_grad * ListOp(
                                        [state],
                                        combo_fn=partial(self._grad_combo_fn,
                                                         state_op=state_op))
                                else:
                                    state = ~Zero @ One

                        if m == 0 and k == 0:
                            op = state
                        else:
                            # Product Rule
                            op += state
                if len(target_params) > 1:
                    if not states:
                        states = [op]
                    else:
                        states += [op]
                else:
                    return op
        if len(target_params) > 1:
            return ListOp(states)
        else:
            return op
Ejemplo n.º 17
0
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit, QuantumRegister

from basis_library import BasisLibrary

from qiskit.circuit.library.standard_gates import (HGate, ZGate, XGate, YGate,
                                                   RZGate, RXGate, RYGate,
                                                   CXGate, CZGate)

libr = RxzCzLibrary = BasisLibrary()

# H - Gate
q = QuantumRegister(1, 'q')
_def = QuantumCircuit(q, global_phase=pi / 2)

rules = [(RZGate(pi / 2), [q[0]], []), (RXGate(pi / 2), [q[0]], []),
         (RZGate(pi / 2), [q[0]], [])]

for inst, qargs, cargs in rules:
    _def.append(inst, qargs, cargs)

libr.add('h', _def)

# Z - Gate
q = QuantumRegister(1, 'q')
_def = QuantumCircuit(q, global_phase=pi / 2)

rules = [(RZGate(pi), [q[0]], [])]

for inst, qargs, cargs in rules:
    _def.append(inst, qargs, cargs)
Ejemplo n.º 18
0
    def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos,
                        open_ctrl=False, trim_after_grad_gate=False):
        """Util function to apply a gradient gate for the linear combination of unitaries method.

        Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as
        superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit.

        Args:
            circuit (QuantumCircuit): The circuit in which to do the replacements.
            gate (Gate): The gate instance to replace.
            param_index (int): The index of the parameter in ``gate``.
            grad_gate (Gate): A controlled gate encoding the gradient of ``gate``.
            grad_coeff (float): A coefficient to the gradient component. Might not be one if the
                gradient contains multiple summed terms.
            qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit``
                that is used as control for ``grad_gate``.
            open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed.
            trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can
                be used to reduce the circuit depth in e.g. computing an overlap of gradients.

        Returns:
            QuantumCircuit: A copy of the original circuit with the gradient gate added.

        Raises:
            RuntimeError: If ``gate`` is not in ``circuit``.
        """
        # copy the input circuit taking the gates by reference
        out = QuantumCircuit(*circuit.qregs)
        out._data = circuit._data.copy()
        out._parameter_table = ParameterTable({
            param: values.copy() for param, values in circuit._parameter_table.items()
        })

        # get the data index and qubits of the target gate  TODO use built-in
        gate_idx, gate_qubits = None, None
        for i, (op, qarg, _) in enumerate(out._data):
            if op is gate:
                gate_idx, gate_qubits = i, qarg
                break
        if gate_idx is None:
            raise RuntimeError('The specified gate could not be found in the circuit data.')

        # initialize replacement instructions
        replacement = []

        # insert the phase fix before the target gate better documentation
        sign = np.sign(grad_coeff)
        is_complex = np.iscomplex(grad_coeff)

        if sign < 0 and is_complex:
            replacement.append((SdgGate(), qr_superpos[:], []))
        elif sign < 0:
            replacement.append((ZGate(), qr_superpos[:], []))
        elif is_complex:
            replacement.append((SGate(), qr_superpos[:], []))
        # else no additional gate required

        # open control if specified
        if open_ctrl:
            replacement += [(XGate(), qr_superpos[:], [])]

        # compute the replacement
        if isinstance(gate, UGate) and param_index == 0:
            theta = gate.params[2]
            rz_plus, rz_minus = RZGate(theta), RZGate(-theta)
            replacement += [(rz_plus, [qubit], []) for qubit in gate_qubits]
            replacement += [(RXGate(np.pi / 2), [qubit], []) for qubit in gate_qubits]
            replacement.append((grad_gate, qr_superpos[:] + gate_qubits, []))
            replacement += [(RXGate(-np.pi / 2), [qubit], []) for qubit in gate_qubits]
            replacement += [(rz_minus, [qubit], []) for qubit in gate_qubits]

            # update parametertable if necessary
            if isinstance(theta, ParameterExpression):
                out._update_parameter_table(rz_plus)
                out._update_parameter_table(rz_minus)

            if open_ctrl:
                replacement += [(XGate(), qr_superpos[:], [])]

            if not trim_after_grad_gate:
                replacement.append((gate, gate_qubits, []))

        elif isinstance(gate, UGate) and param_index == 1:
            # gradient gate is applied after the original gate in this case
            replacement.append((gate, gate_qubits, []))
            replacement.append((grad_gate, qr_superpos[:] + gate_qubits, []))
            if open_ctrl:
                replacement += [(XGate(), qr_superpos[:], [])]

        else:
            replacement.append((grad_gate, qr_superpos[:] + gate_qubits, []))
            if open_ctrl:
                replacement += [(XGate(), qr_superpos[:], [])]
            if not trim_after_grad_gate:
                replacement.append((gate, gate_qubits, []))

        # replace the parameter we compute the derivative of with the replacement
        # TODO can this be done more efficiently?
        if trim_after_grad_gate:  # remove everything after the gradient gate
            out._data[gate_idx:] = replacement
            # reset parameter table
            table = ParameterTable()
            for op, _, _ in out._data:
                for idx, param_expression in enumerate(op.params):
                    if isinstance(param_expression, ParameterExpression):
                        for param in param_expression.parameters:
                            if param not in table.keys():
                                table[param] = [(op, idx)]
                            else:
                                table[param].append((op, idx))

            out._parameter_table = table

        else:
            out._data[gate_idx:gate_idx + 1] = replacement

        return out
Ejemplo n.º 19
0
    def apply_grad_gate(
        circuit,
        gate,
        param_index,
        grad_gate,
        grad_coeff,
        qr_superpos,
        open_ctrl=False,
        trim_after_grad_gate=False,
    ):
        """Util function to apply a gradient gate for the linear combination of unitaries method.
        Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as
        superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit.

        Args:
            circuit (QuantumCircuit): The circuit in which to do the replacements.
            gate (Gate): The gate instance to replace.
            param_index (int): The index of the parameter in ``gate``.
            grad_gate (Gate): A controlled gate encoding the gradient of ``gate``.
            grad_coeff (float): A coefficient to the gradient component. Might not be one if the
                gradient contains multiple summed terms.
            qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit``
                that is used as control for ``grad_gate``.
            open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed.
            trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can
                be used to reduce the circuit depth in e.g. computing an overlap of gradients.

        Returns:
            QuantumCircuit: A copy of the original circuit with the gradient gate added.

        Raises:
            RuntimeError: If ``gate`` is not in ``circuit``.
        """
        qr_superpos_qubits = tuple(qr_superpos)
        # copy the input circuit taking the gates by reference
        out = QuantumCircuit(*circuit.qregs)
        out._data = circuit._data.copy()
        out._parameter_table = ParameterTable({
            param: values.copy()
            for param, values in circuit._parameter_table.items()
        })

        # get the data index and qubits of the target gate  TODO use built-in
        gate_idx, gate_qubits = None, None
        for i, instruction in enumerate(out._data):
            if instruction.operation is gate:
                gate_idx, gate_qubits = i, instruction.qubits
                break
        if gate_idx is None:
            raise RuntimeError(
                "The specified gate could not be found in the circuit data.")

        # initialize replacement instructions
        replacement = []

        # insert the phase fix before the target gate better documentation
        sign = np.sign(grad_coeff)
        is_complex = np.iscomplex(grad_coeff)

        if sign < 0 and is_complex:
            replacement.append(
                CircuitInstruction(SdgGate(), qr_superpos_qubits, ()))
        elif sign < 0:
            replacement.append(
                CircuitInstruction(ZGate(), qr_superpos_qubits, ()))
        elif is_complex:
            replacement.append(
                CircuitInstruction(SGate(), qr_superpos_qubits, ()))
        # else no additional gate required

        # open control if specified
        if open_ctrl:
            replacement += [
                CircuitInstruction(XGate(), qr_superpos_qubits, [])
            ]

        # compute the replacement
        if isinstance(gate, UGate) and param_index == 0:
            theta = gate.params[2]
            rz_plus, rz_minus = RZGate(theta), RZGate(-theta)
            replacement += [
                CircuitInstruction(rz_plus, (qubit, ), ())
                for qubit in gate_qubits
            ]
            replacement += [
                CircuitInstruction(RXGate(np.pi / 2), (qubit, ), ())
                for qubit in gate_qubits
            ]
            replacement.append(
                CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits,
                                   []))
            replacement += [
                CircuitInstruction(RXGate(-np.pi / 2), (qubit, ), ())
                for qubit in gate_qubits
            ]
            replacement += [
                CircuitInstruction(rz_minus, (qubit, ), ())
                for qubit in gate_qubits
            ]

            # update parametertable if necessary
            if isinstance(theta, ParameterExpression):
                # This dangerously subverts ParameterTable by abusing the fact that binding will
                # mutate the exact instruction instance, and relies on all instances of `rz_plus`
                # that were added before being the same in memory, which QuantumCircuit usually
                # ensures is not the case.  I'm leaving this as close to its previous form as
                # possible, to avoid introducing further complications, but this whole method
                # accesses internal attributes of `QuantumCircuit` and needs rewriting.
                # - Jake Lishman, 2022-03-02.
                out._update_parameter_table(
                    CircuitInstruction(rz_plus, (gate_qubits[0], ), ()))
                out._update_parameter_table(
                    CircuitInstruction(rz_minus, (gate_qubits[0], ), ()))

            if open_ctrl:
                replacement.append(
                    CircuitInstruction(XGate(), qr_superpos_qubits, ()))

            if not trim_after_grad_gate:
                replacement.append(CircuitInstruction(gate, gate_qubits, ()))

        elif isinstance(gate, UGate) and param_index == 1:
            # gradient gate is applied after the original gate in this case
            replacement.append(CircuitInstruction(gate, gate_qubits, ()))
            replacement.append(
                CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits,
                                   ()))
            if open_ctrl:
                replacement.append(
                    CircuitInstruction(XGate(), qr_superpos_qubits, ()))

        else:
            replacement.append(
                CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits,
                                   ()))
            if open_ctrl:
                replacement.append(
                    CircuitInstruction(XGate(), qr_superpos_qubits, ()))
            if not trim_after_grad_gate:
                replacement.append(CircuitInstruction(gate, gate_qubits, ()))

        # replace the parameter we compute the derivative of with the replacement
        # TODO can this be done more efficiently?
        if trim_after_grad_gate:  # remove everything after the gradient gate
            out._data[gate_idx:] = replacement
            # reset parameter table
            table = ParameterTable()
            for instruction in out._data:
                for idx, param_expression in enumerate(
                        instruction.operation.params):
                    if isinstance(param_expression, ParameterExpression):
                        for param in param_expression.parameters:
                            if param not in table.keys():
                                table[param] = ParameterReferences(
                                    ((instruction.operation, idx), ))
                            else:
                                table[param].add((instruction.operation, idx))

            out._parameter_table = table

        else:
            out._data[gate_idx:gate_idx + 1] = replacement

        return out