Example #1
0
    def test_dispersivecqed_combination(self):
        """
        Dispersive cQED Setup: compare unitary matrix for ISWAP, SQRTISWAP,
        RX and RY gates and the propogator matrix of the implemented physical
        model.
        """
        N = 3

        qc1 = QubitCircuit(N)
        qc1.add_gate("ISWAP", targets=[0, 1])
        qc1.add_gate("RZ",
                     arg_value=np.pi / 2,
                     arg_label=r"\pi/2",
                     targets=[1])
        qc1.add_gate("RX",
                     arg_value=np.pi / 2,
                     arg_label=r"\pi/2",
                     targets=[0])
        U_ideal = gate_sequence_product(qc1.propagators())

        p = DispersivecQED(N, correct_global_phase=True)
        U_list = p.run(qc1)
        U_physical = gate_sequence_product(U_list)

        print((U_ideal - U_physical).norm())
        assert_((U_ideal - U_physical).norm() < 1e-2)
Example #2
0
 def testCSIGNtoCNOT(self):
     """
     CSIGN to CNOT: compare unitary matrix for CSIGN and product of
     resolved matrices in terms of CNOT.
     """
     qc1 = QubitCircuit(2)
     qc1.add_gate("CSIGN", targets=[1], controls=[0])
     U1 = gate_sequence_product(qc1.propagators())
     qc2 = qc1.resolve_gates(basis="CNOT")
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
Example #3
0
 def testCNOTtoSQRTSWAP(self):
     """
     CNOT to SQRTSWAP: compare unitary matrix for CNOT and product of
     resolved matrices in terms of SQRTSWAP.
     """
     qc1 = QubitCircuit(2)
     qc1.add_gate("CNOT", targets=[0], controls=[1])
     U1 = gate_sequence_product(qc1.propagators())
     qc2 = qc1.resolve_gates(basis="SQRTSWAP")
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
Example #4
0
 def testCSIGNtoCNOT(self):
     """
     CSIGN to CNOT: compare unitary matrix for CSIGN and product of
     resolved matrices in terms of CNOT.
     """
     qc1 = QubitCircuit(2)
     qc1.add_gate("CSIGN", targets=[1], controls=[0])
     U1 = gate_sequence_product(qc1.propagators())
     qc2 = qc1.resolve_gates(basis="CNOT")
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
Example #5
0
 def testCNOTtoSQRTSWAP(self):
     """
     CNOT to SQRTSWAP: compare unitary matrix for CNOT and product of
     resolved matrices in terms of SQRTSWAP.
     """
     qc1 = QubitCircuit(2)
     qc1.add_gate("CNOT", targets=[0], controls=[1])
     U1 = gate_sequence_product(qc1.propagators())
     qc2 = qc1.resolve_gates(basis="SQRTSWAP")
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
 def testSNOTdecompose(self):
     """
     SNOT to rotation: compare unitary matrix for SNOT and product of
     resolved matrices in terms of rotation gates.
     """
     qc1 = QubitCircuit(1)
     qc1.add_gate("SNOT", targets=0)
     U1 = gate_sequence_product(qc1.propagators())
     qc2 = qc1.resolve_gates()
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
Example #7
0
 def testadjacentgates(self):
     """
     Adjacent Gates: compare unitary matrix for ISWAP and product of
     resolved matrices in terms of adjacent gates interaction.
     """
     qc1 = QubitCircuit(3)
     qc1.add_gate("ISWAP", targets=[0, 2])
     U1 = gate_sequence_product(qc1.propagators())
     qc0 = qc1.adjacent_gates()
     qc2 = qc0.resolve_gates(basis="ISWAP")
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
Example #8
0
 def testadjacentgates(self):
     """
     Adjacent Gates: compare unitary matrix for ISWAP and product of
     resolved matrices in terms of adjacent gates interaction.
     """
     qc1 = QubitCircuit(3)
     qc1.add_gate("ISWAP", targets=[0, 2])
     U1 = gate_sequence_product(qc1.propagators())
     qc0 = qc1.adjacent_gates()
     qc2 = qc0.resolve_gates(basis="ISWAP")
     U2 = gate_sequence_product(qc2.propagators())
     assert_((U1 - U2).norm() < 1e-12)
Example #9
0
    def test_numerical_evo(self):
        """
        Test of run_state with qutip solver
        """
        N = 3
        qc = QubitCircuit(N)
        qc.add_gate("RX", targets=[0], arg_value=np.pi / 2)
        qc.add_gate("CNOT", targets=[0], controls=[1])
        qc.add_gate("ISWAP", targets=[2, 1])
        qc.add_gate("CNOT", targets=[0], controls=[2])
        # qc.add_gate("SQRTISWAP", targets=[0, 2])

        with warnings.catch_warnings(record=True):
            test = DispersivecQED(N, g=0.1)
        tlist, coeff = test.load_circuit(qc)

        # test numerical run_state
        qu0 = rand_ket(2**N)
        qu0.dims = [[2] * N, [1] * N]
        rho0 = tensor(basis(10, 0), qu0)
        qu1 = gate_sequence_product([qu0] + qc.propagators())
        result = test.run_state(rho0=rho0,
                                analytical=False,
                                options=Options(store_final_state=True,
                                                nsteps=50000)).final_state
        assert_allclose(fidelity(result, tensor(basis(10, 0), qu1)),
                        1.,
                        rtol=1e-2,
                        err_msg="Numerical run_state fails in DispersivecQED")
    def test_linear_SQRTISWAP(self):
        """
        Linear Spin Chain Setup: compare unitary matrix for SQRTISWAP and
        propogator matrix of the implemented physical model.
        """
        N = 3

        qc = QubitCircuit(N)
        qc.add_gate("SQRTISWAP", targets=[0, 1])
        U_ideal = gate_sequence_product(qc.propagators())

        p = LinearSpinChain(N, correct_global_phase=True)
        U_list = p.run(qc)
        U_physical = gate_sequence_product(U_list)

        assert_((U_ideal - U_physical).norm() < 1e-12)
Example #11
0
    def test_linear_SQRTISWAP(self):
        """
        Linear Spin Chain Setup: compare unitary matrix for SQRTISWAP and
        propogator matrix of the implemented physical model.
        """
        N = 3

        qc = QubitCircuit(N)
        qc.add_gate("SQRTISWAP", targets=[0, 1])
        U_ideal = gate_sequence_product(qc.propagators())

        p = LinearSpinChain(N, correct_global_phase=True)
        U_list = p.run(qc)
        U_physical = gate_sequence_product(U_list)

        assert_((U_ideal - U_physical).norm() < 1e-12)
Example #12
0
    def test_multi_gates(self):
        N = 2
        H_d = tensor([sigmaz()]*2)
        H_c = []

        test = OptPulseProcessor(N, H_d, H_c)
        test.add_ctrl(sigmax(), cyclic_permutation=True)
        test.add_ctrl(sigmay(), cyclic_permutation=True)
        test.add_ctrl(tensor([sigmay(), sigmay()]))

        # qubits circuit with 3 gates
        setting_args = {"SNOT": {"num_tslots": 10, "evo_time": 1},
                        "SWAP": {"num_tslots": 30, "evo_time": 3},
                        "CNOT": {"num_tslots": 30, "evo_time": 3}}
        qc = QubitCircuit(N)
        qc.add_gate("SNOT", 0)
        qc.add_gate("SWAP", targets=[0, 1])
        qc.add_gate('CNOT', controls=1, targets=[0])
        test.load_circuit(qc, setting_args=setting_args,
                          merge_gates=False)

        rho0 = rand_ket(4)  # use random generated ket state
        rho0.dims = [[2, 2], [1, 1]]
        U = gate_sequence_product(qc.propagators())
        rho1 = U * rho0
        result = test.run_state(rho0)
        assert_(fidelity(result.states[-1], rho1) > 1-1.0e-6)
Example #13
0
 def testQFTComparison(self):
     """
     qft: compare qft and product of qft steps
     """
     for N in range(1, 5):
         U1 = qft(N)
         U2 = gate_sequence_product(qft_steps(N))
         assert_((U1 - U2).norm() < 1e-12)
Example #14
0
 def testQFTComparison(self):
     """
     qft: compare qft and product of qft steps
     """
     for N in range(1, 5):
         U1 = qft(N)
         U2 = gate_sequence_product(qft_steps(N))
         assert_((U1 - U2).norm() < 1e-12)
Example #15
0
    def skip_dispersivecqed_SQRTISWAP(self):
        """
        Dispersive cQED Setup: compare unitary matrix for SQRTISWAP and
        propogator matrix of the implemented physical model.
        """
        N = 3

        qc1 = QubitCircuit(N)
        qc1.add_gate("SQRTISWAP", targets=[0, 1])
        U_ideal = gate_sequence_product(qc1.propagators())

        p = DispersivecQED(N, correct_global_phase=True)
        U_list = p.run(qc1)
        U_physical = gate_sequence_product(U_list)

        print((U_ideal - U_physical).norm())
        assert_((U_ideal - U_physical).norm() < 1e-4)
Example #16
0
    def test_linear_combination(self):
        """
        Linear Spin Chain Setup: compare unitary matrix for ISWAP, SQRTISWAP,
        RX and RY gates and the propogator matrix of the implemented physical
        model.
        """
        N = 3

        qc = QubitCircuit(N)
        qc.add_gate("ISWAP", targets=[0, 1])
        qc.add_gate("SQRTISWAP", targets=[0, 1])
        qc.add_gate("RZ", arg_value=np.pi/2, arg_label=r"\pi/2", targets=[1])
        qc.add_gate("RX", arg_value=np.pi/2, arg_label=r"\pi/2", targets=[0])
        U_ideal = gate_sequence_product(qc.propagators())

        p = LinearSpinChain(N, correct_global_phase=True)
        U_list = p.run(qc)
        U_physical = gate_sequence_product(U_list)

        assert_((U_ideal - U_physical).norm() < 1e-12)
Example #17
0
    def test_dispersivecqed_combination(self):
        """
        Dispersive cQED Setup: compare unitary matrix for ISWAP, SQRTISWAP,
        RX and RY gates and the propogator matrix of the implemented physical
        model.
        """
        N = 3

        qc1 = QubitCircuit(N)
        qc1.add_gate("ISWAP", targets=[0, 1])
        qc1.add_gate("RZ", arg_value=np.pi/2, arg_label=r"\pi/2", targets=[1])
        qc1.add_gate("RX", arg_value=np.pi/2, arg_label=r"\pi/2", targets=[0])
        U_ideal = gate_sequence_product(qc1.propagators())

        p = DispersivecQED(N, correct_global_phase=True)
        U_list = p.run(qc1)
        U_physical = gate_sequence_product(U_list)

        print((U_ideal - U_physical).norm())
        assert_((U_ideal - U_physical).norm() < 1e-2)
    def test_linear_combination(self):
        """
        Linear Spin Chain Setup: compare unitary matrix for ISWAP, SQRTISWAP,
        RX and RY gates and the propogator matrix of the implemented physical
        model.
        """
        N = 3

        qc = QubitCircuit(N)
        qc.add_gate("ISWAP", targets=[0, 1])
        qc.add_gate("SQRTISWAP", targets=[0, 1])
        qc.add_gate("RZ", arg_value=np.pi / 2, arg_label=r"\pi/2", targets=[1])
        qc.add_gate("RX", arg_value=np.pi / 2, arg_label=r"\pi/2", targets=[0])
        U_ideal = gate_sequence_product(qc.propagators())

        p = LinearSpinChain(N, correct_global_phase=True)
        U_list = p.run(qc)
        U_physical = gate_sequence_product(U_list)

        assert_((U_ideal - U_physical).norm() < 1e-12)
Example #19
0
    def test_numerical_evo(self):
        """
        Test run_state with qutip solver
        """
        N = 3
        qc = QubitCircuit(N)
        qc.add_gate("RX", targets=[0], arg_value=np.pi/2)
        qc.add_gate("CNOT", targets=[0], controls=[1])
        qc.add_gate("ISWAP", targets=[2, 1])
        qc.add_gate("CNOT", targets=[0], controls=[2])
        qc.add_gate("SQRTISWAP", targets=[0, 2])
        qc.add_gate("RZ", arg_value=np.pi/2, targets=[1])

        # CircularSpinChain
        test = CircularSpinChain(N)
        tlist, coeffs = test.load_circuit(qc)

        rho0 = rand_ket(2**N)
        rho0.dims = [[2]*N, [1]*N]
        rho1 = gate_sequence_product([rho0] + qc.propagators())
        result = test.run_state(
            rho0=rho0, analytical=False,
            options=Options(store_final_state=True)).final_state
        assert_allclose(
            fidelity(result, rho1), 1., rtol=1e-6,
            err_msg="Numerical run_state fails in CircularSpinChain")

        # LinearSpinChain
        test = LinearSpinChain(N)
        tlist, coeffs = test.load_circuit(qc)

        rho0 = rand_ket(2**N)
        rho0.dims = [[2]*N, [1]*N]
        rho1 = gate_sequence_product([rho0] + qc.propagators())
        result = test.run_state(
            rho0=rho0, analytical=False,
            options=Options(store_final_state=True)).final_state
        assert_allclose(
            fidelity(result, rho1), 1., rtol=1e-6,
            err_msg="Numerical run_state fails in LinearSpinChain")
Example #20
0
    def test_analytical_evo(self):
        """
        Test of run_state with exp(-iHt)
        """
        N = 3

        qc = QubitCircuit(N)
        qc.add_gate("ISWAP", targets=[0, 1])
        qc.add_gate("RZ", arg_value=np.pi / 2, arg_label=r"\pi/2", targets=[1])
        qc.add_gate("RX", arg_value=np.pi / 2, arg_label=r"\pi/2", targets=[0])
        U_ideal = gate_sequence_product(qc.propagators())

        rho0 = rand_ket(2**N)
        rho0.dims = [[2] * N, [1] * N]
        rho1 = gate_sequence_product([rho0] + qc.propagators())

        p = DispersivecQED(N, correct_global_phase=True)
        U_list = p.run_state(rho0=rho0, qc=qc, analytical=True)
        result = gate_sequence_product(U_list)
        assert_allclose(fidelity(result, rho1),
                        1.,
                        rtol=1e-2,
                        err_msg="Analytical run_state fails in DispersivecQED")
Example #21
0
def grape_unitary_adaptive(
    U,
    H0,
    H_ops,
    R,
    times,
    eps=None,
    u_start=None,
    u_limits=None,
    interp_kind="linear",
    use_interp=False,
    alpha=None,
    beta=None,
    phase_sensitive=False,
    overlap_terminate=1.0,
    progress_bar=BaseProgressBar(),
):
    """
    Calculate control pulses for the Hamiltonian operators in H_ops so that
    the unitary U is realized.

    Experimental: Work in progress.

    Parameters
    ----------
    U : Qobj
        Target unitary evolution operator.

    H0 : Qobj
        Static Hamiltonian (that cannot be tuned by the control fields).

    H_ops: list of Qobj
        A list of operators that can be tuned in the Hamiltonian via the
        control fields.

    R : int
        Number of GRAPE iterations.

    time : array / list
        Array of time coordinates for control pulse evalutation.

    u_start : array
        Optional array with initial control pulse values.

    Returns
    -------
        Instance of GRAPEResult, which contains the control pulses calculated
        with GRAPE, a time-dependent Hamiltonian that is defined by the
        control pulses, as well as the resulting propagator.
    """

    if eps is None:
        eps = 0.1 * (2 * np.pi) / (times[-1])

    eps_vec = np.array([eps / 2, eps, 2 * eps])
    eps_log = np.zeros(R)
    overlap_log = np.zeros(R)

    best_k = 0
    _k_overlap = np.array([0.0, 0.0, 0.0])

    M = len(times)
    J = len(H_ops)
    K = len(eps_vec)
    Uf = [None for _ in range(K)]

    u = np.zeros((R, J, M, K))

    if u_limits and len(u_limits) != 2:
        raise ValueError("u_limits must be a list with two values")

    if u_limits:
        warnings.warn("Causion: Using experimental feature u_limits")

    if u_limits and u_start:
        # make sure that no values in u0 violates the u_limits conditions
        u_start = np.array(u_start)
        u_start[u_start < u_limits[0]] = u_limits[0]
        u_start[u_start > u_limits[1]] = u_limits[1]

    if u_start is not None:
        for idx, u0 in enumerate(u_start):
            for k in range(K):
                u[0, idx, :, k] = u0

    if beta:
        warnings.warn("Causion: Using experimental feature time-penalty")

    if phase_sensitive:
        _fidelity_function = lambda x: x
    else:
        _fidelity_function = lambda x: abs(x) ** 2

    best_k = 1
    _r = 0
    _prev_overlap = 0

    progress_bar.start(R)
    for r in range(R - 1):
        progress_bar.update(r)

        _r = r
        eps_log[r] = eps_vec[best_k]

        logger.debug("eps_vec: {}".format(eps_vec))

        _t0 = time.time()

        dt = times[1] - times[0]

        if use_interp:
            ip_funcs = [
                interp1d(
                    times, u[r, j, :, best_k], kind=interp_kind, bounds_error=False, fill_value=u[r, j, -1, best_k]
                )
                for j in range(J)
            ]

            def _H_t(t, args=None):
                return H0 + sum([float(ip_funcs[j](t)) * H_ops[j] for j in range(J)])

            U_list = [(-1j * _H_t(times[idx]) * dt).expm() for idx in range(M - 1)]

        else:

            def _H_idx(idx):
                return H0 + sum([u[r, j, idx, best_k] * H_ops[j] for j in range(J)])

            U_list = [(-1j * _H_idx(idx) * dt).expm() for idx in range(M - 1)]

        logger.debug("Time 1: %fs" % (time.time() - _t0))
        _t0 = time.time()

        U_f_list = []
        U_b_list = []

        U_f = 1
        U_b = 1
        for m in range(M - 1):

            U_f = U_list[m] * U_f
            U_f_list.append(U_f)

            U_b_list.insert(0, U_b)
            U_b = U_list[M - 2 - m].dag() * U_b

        logger.debug("Time 2: %fs" % (time.time() - _t0))
        _t0 = time.time()

        for j in range(J):
            for m in range(M - 1):
                P = U_b_list[m] * U
                Q = 1j * dt * H_ops[j] * U_f_list[m]

                if phase_sensitive:
                    du = -cy_overlap(P.data, Q.data)
                else:
                    du = -2 * cy_overlap(P.data, Q.data) * cy_overlap(U_f_list[m].data, P.data)

                if alpha:
                    # penalty term for high power control signals u
                    du += -2 * alpha * u[r, j, m, best_k] * dt

                if beta:
                    # penalty term for late control signals u
                    du += -2 * beta * k ** 2 * u[r, j, k] * dt

                for k, eps_val in enumerate(eps_vec):
                    u[r + 1, j, m, k] = u[r, j, m, k] + eps_val * du.real

                    if u_limits:
                        if u[r + 1, j, m, k] < u_limits[0]:
                            u[r + 1, j, m, k] = u_limits[0]
                        elif u[r + 1, j, m, k] > u_limits[1]:
                            u[r + 1, j, m, k] = u_limits[1]

            u[r + 1, j, -1, :] = u[r + 1, j, -2, :]

        logger.debug("Time 3: %fs" % (time.time() - _t0))
        _t0 = time.time()

        for k, eps_val in enumerate(eps_vec):

            def _H_idx(idx):
                return H0 + sum([u[r + 1, j, idx, k] * H_ops[j] for j in range(J)])

            U_list = [(-1j * _H_idx(idx) * dt).expm() for idx in range(M - 1)]

            Uf[k] = gate_sequence_product(U_list)
            _k_overlap[k] = _fidelity_function(cy_overlap(Uf[k].data, U.data)).real

        best_k = np.argmax(_k_overlap)
        logger.debug("k_overlap: ", _k_overlap, best_k)

        if _prev_overlap > _k_overlap[best_k]:
            logger.debug("Regression, stepping back with smaller eps.")

            u[r + 1, :, :, :] = u[r, :, :, :]
            eps_vec /= 2
        else:

            if best_k == 0:
                eps_vec /= 2

            elif best_k == 2:
                eps_vec *= 2

            _prev_overlap = _k_overlap[best_k]

        overlap_log[r] = _k_overlap[best_k]

        if overlap_terminate < 1.0:
            if _k_overlap[best_k] > overlap_terminate:
                logger.info("Reached target fidelity, terminating.")
                break

        logger.debug("Time 4: %fs" % (time.time() - _t0))
        _t0 = time.time()

    if use_interp:
        ip_funcs = [
            interp1d(times, u[_r, j, :, best_k], kind=interp_kind, bounds_error=False, fill_value=u[R - 1, j, -1])
            for j in range(J)
        ]

        H_td_func = [H0] + [[H_ops[j], lambda t, args, j=j: ip_funcs[j](t)] for j in range(J)]
    else:
        H_td_func = [H0] + [[H_ops[j], u[_r, j, :, best_k]] for j in range(J)]

    progress_bar.finished()

    result = GRAPEResult(u=u[:_r, :, :, best_k], U_f=Uf[best_k], H_t=H_td_func)

    result.eps = eps_log
    result.overlap = overlap_log

    return result
    def load_circuit(self, qc, min_fid_err=np.inf, merge_gates=True,
                     setting_args=None, verbose=False, **kwargs):
        """
        Find the pulses realizing a given :class:`qutip.qip.Circuit` using
        `qutip.control.optimize_pulse_unitary`. Further parameter for
        for `qutip.control.optimize_pulse_unitary` needs to be given as
        keyword arguments. By default, it first merge all the gates
        into one unitary and then find the control pulses for it.
        It can be turned off and one can set different parameters
        for different gates. See examples for details.

        Examples
        --------
        # Same parameter for all the gates
        qc = QubitCircuit(N=1)
        qc.add_gate("SNOT", 0)

        num_tslots = 10
        evo_time = 10
        processor = OptPulseProcessor(N=1, drift=sigmaz(), ctrls=[sigmax()])
        # num_tslots and evo_time are two keyword arguments
        tlist, coeffs = processor.load_circuit(
            qc, num_tslots=num_tslots, evo_time=evo_time)

        # Different parameters for different gates
        qc = QubitCircuit(N=2)
        qc.add_gate("SNOT", 0)
        qc.add_gate("SWAP", targets=[0, 1])
        qc.add_gate('CNOT', controls=1, targets=[0])

        processor = OptPulseProcessor(N=2, drift=tensor([sigmaz()]*2))
        processor.add_ctrl(sigmax(), cyclic_permutation=True)
        processor.add_ctrl(sigmay(), cyclic_permutation=True)
        processor.add_ctrl(tensor([sigmay(), sigmay()]))
        setting_args = {"SNOT": {"num_tslots": 10, "evo_time": 1},
                        "SWAP": {"num_tslots": 30, "evo_time": 3},
                        "CNOT": {"num_tslots": 30, "evo_time": 3}}
        tlist, coeffs = processor.load_circuit(qc, setting_args=setting_args,
                                               merge_gates=False)

        Parameters
        ----------
        qc: :class:`qutip.QubitCircuit` or list of Qobj
            The quantum circuit to be translated.

        min_fid_err: float, optional
            The minimal fidelity tolerance, if the fidelity error of any
            gate decomposition is higher, a warning will be given.
            Default is infinite.

        merge_gates: boolean, optimal
            If True, merge all gate/Qobj into one Qobj and then
            find the optimal pulses for this unitary matrix. If False,
            find the optimal pulses for each gate/Qobj.

        setting_args: dict, optional
            Only considered if merge_gates is False.
            It is a dictionary containing keyword arguments
            for different gates.

            E.g:
            setting_args = {"SNOT": {"num_tslots": 10, "evo_time": 1},
                            "SWAP": {"num_tslots": 30, "evo_time": 3},
                            "CNOT": {"num_tslots": 30, "evo_time": 3}}

        verbose: boolean, optional
            If true, the information for each decomposed gate
            will be shown. Default is False.

        **kwargs
            keyword arguments for `qutip.control.optimize_pulse_unitary`

        Returns
        -------
        tlist: array_like
            A NumPy array specifies the time of each coefficient

        coeffs: array_like
            A 2d NumPy array of the shape (len(ctrls), len(tlist)-1). Each
            row corresponds to the control pulse sequence for
            one Hamiltonian.

        Notes
        -----
        len(tlist)-1=coeffs.shape[1] since tlist gives the beginning and the
        end of the pulses
        """
        if setting_args is None:
            setting_args = {}
        if isinstance(qc, QubitCircuit):
            props = qc.propagators()
            gates = [g.name for g in qc.gates]
        elif isinstance(qc, Iterable):
            props = qc
            gates = None  # using list of Qobj, no gates name
        else:
            raise ValueError(
                "qc should be a "
                "QubitCircuit or a list of Qobj")
        if merge_gates:  # merge all gates/Qobj into one Qobj
            props = [gate_sequence_product(props)]
            gates = None

        time_record = []  # a list for all the gates
        coeff_record = []
        last_time = 0.  # used in concatenation of tlist
        for prop_ind, U_targ in enumerate(props):
            U_0 = identity(U_targ.dims[0])

            # If qc is a QubitCircuit and setting_args is not empty,
            # we update the kwargs for each gate.
            # keyword arguments in setting_arg have priority
            if gates is not None and setting_args:
                kwargs.update(setting_args[gates[prop_ind]])

            result = cpo.optimize_pulse_unitary(
                self.drift, self.ctrls, U_0, U_targ, **kwargs)

            if result.fid_err > min_fid_err:
                warnings.warn(
                    "The fidelity error of gate {} is higher "
                    "than required limit. Use verbose=True to see"
                    "the more detailed information.".format(prop_ind))

            time_record.append(result.time[1:] + last_time)
            last_time += result.time[-1]
            coeff_record.append(result.final_amps.T)

            if verbose:
                print("********** Gate {} **********".format(prop_ind))
                print("Final fidelity error {}".format(result.fid_err))
                print("Final gradient normal {}".format(
                                                result.grad_norm_final))
                print("Terminated due to {}".format(result.termination_reason))
                print("Number of iterations {}".format(result.num_iter))
        self.tlist = np.hstack([[0.]] + time_record)
        self.coeffs = np.vstack([np.hstack(coeff_record)])
        return self.tlist, self.coeffs
Example #23
0
def grape_unitary_adaptive(U,
                           H0,
                           H_ops,
                           R,
                           times,
                           eps=None,
                           u_start=None,
                           u_limits=None,
                           interp_kind='linear',
                           use_interp=False,
                           alpha=None,
                           beta=None,
                           phase_sensitive=False,
                           overlap_terminate=1.0,
                           progress_bar=BaseProgressBar()):
    """
    Calculate control pulses for the Hamiltonian operators in H_ops so that
    the unitary U is realized.

    Experimental: Work in progress.

    Parameters
    ----------
    U : Qobj
        Target unitary evolution operator.

    H0 : Qobj
        Static Hamiltonian (that cannot be tuned by the control fields).

    H_ops: list of Qobj
        A list of operators that can be tuned in the Hamiltonian via the
        control fields.

    R : int
        Number of GRAPE iterations.

    time : array / list
        Array of time coordinates for control pulse evalutation.

    u_start : array
        Optional array with initial control pulse values.

    Returns
    -------
        Instance of GRAPEResult, which contains the control pulses calculated
        with GRAPE, a time-dependent Hamiltonian that is defined by the
        control pulses, as well as the resulting propagator.
    """

    if eps is None:
        eps = 0.1 * (2 * np.pi) / (times[-1])

    eps_vec = np.array([eps / 2, eps, 2 * eps])
    eps_log = np.zeros(R)
    overlap_log = np.zeros(R)

    best_k = 0
    _k_overlap = np.array([0.0, 0.0, 0.0])

    M = len(times)
    J = len(H_ops)
    K = len(eps_vec)
    Uf = [None for _ in range(K)]

    u = np.zeros((R, J, M, K))

    if u_limits and len(u_limits) != 2:
        raise ValueError("u_limits must be a list with two values")

    if u_limits:
        warnings.warn("Causion: Using experimental feature u_limits")

    if u_limits and u_start:
        # make sure that no values in u0 violates the u_limits conditions
        u_start = np.array(u_start)
        u_start[u_start < u_limits[0]] = u_limits[0]
        u_start[u_start > u_limits[1]] = u_limits[1]

    if u_start is not None:
        for idx, u0 in enumerate(u_start):
            for k in range(K):
                u[0, idx, :, k] = u0

    if beta:
        warnings.warn("Causion: Using experimental feature time-penalty")

    if phase_sensitive:
        _fidelity_function = lambda x: x
    else:
        _fidelity_function = lambda x: abs(x)**2

    best_k = 1
    _r = 0
    _prev_overlap = 0

    progress_bar.start(R)
    for r in range(R - 1):
        progress_bar.update(r)

        _r = r
        eps_log[r] = eps_vec[best_k]

        logger.debug("eps_vec: {}".format(eps_vec))

        _t0 = time.time()

        dt = times[1] - times[0]

        if use_interp:
            ip_funcs = [
                interp1d(times,
                         u[r, j, :, best_k],
                         kind=interp_kind,
                         bounds_error=False,
                         fill_value=u[r, j, -1, best_k]) for j in range(J)
            ]

            def _H_t(t, args=None):
                return H0 + sum(
                    [float(ip_funcs[j](t)) * H_ops[j] for j in range(J)])

            U_list = [(-1j * _H_t(times[idx]) * dt).expm()
                      for idx in range(M - 1)]

        else:

            def _H_idx(idx):
                return H0 + sum(
                    [u[r, j, idx, best_k] * H_ops[j] for j in range(J)])

            U_list = [(-1j * _H_idx(idx) * dt).expm() for idx in range(M - 1)]

        logger.debug("Time 1: %fs" % (time.time() - _t0))
        _t0 = time.time()

        U_f_list = []
        U_b_list = []

        U_f = 1
        U_b = 1
        for m in range(M - 1):

            U_f = U_list[m] * U_f
            U_f_list.append(U_f)

            U_b_list.insert(0, U_b)
            U_b = U_list[M - 2 - m].dag() * U_b

        logger.debug("Time 2: %fs" % (time.time() - _t0))
        _t0 = time.time()

        for j in range(J):
            for m in range(M - 1):
                P = U_b_list[m] * U
                Q = 1j * dt * H_ops[j] * U_f_list[m]

                if phase_sensitive:
                    du = -cy_overlap(P.data, Q.data)
                else:
                    du = (-2 * cy_overlap(P.data, Q.data) *
                          cy_overlap(U_f_list[m].data, P.data))

                if alpha:
                    # penalty term for high power control signals u
                    du += -2 * alpha * u[r, j, m, best_k] * dt

                if beta:
                    # penalty term for late control signals u
                    du += -2 * beta * k**2 * u[r, j, k] * dt

                for k, eps_val in enumerate(eps_vec):
                    u[r + 1, j, m, k] = u[r, j, m, k] + eps_val * du.real

                    if u_limits:
                        if u[r + 1, j, m, k] < u_limits[0]:
                            u[r + 1, j, m, k] = u_limits[0]
                        elif u[r + 1, j, m, k] > u_limits[1]:
                            u[r + 1, j, m, k] = u_limits[1]

            u[r + 1, j, -1, :] = u[r + 1, j, -2, :]

        logger.debug("Time 3: %fs" % (time.time() - _t0))
        _t0 = time.time()

        for k, eps_val in enumerate(eps_vec):

            def _H_idx(idx):
                return H0 + sum(
                    [u[r + 1, j, idx, k] * H_ops[j] for j in range(J)])

            U_list = [(-1j * _H_idx(idx) * dt).expm() for idx in range(M - 1)]

            Uf[k] = gate_sequence_product(U_list)
            _k_overlap[k] = _fidelity_function(cy_overlap(Uf[k].data,
                                                          U.data)).real

        best_k = np.argmax(_k_overlap)
        logger.debug("k_overlap: ", _k_overlap, best_k)

        if _prev_overlap > _k_overlap[best_k]:
            logger.debug("Regression, stepping back with smaller eps.")

            u[r + 1, :, :, :] = u[r, :, :, :]
            eps_vec /= 2
        else:

            if best_k == 0:
                eps_vec /= 2

            elif best_k == 2:
                eps_vec *= 2

            _prev_overlap = _k_overlap[best_k]

        overlap_log[r] = _k_overlap[best_k]

        if overlap_terminate < 1.0:
            if _k_overlap[best_k] > overlap_terminate:
                logger.info("Reached target fidelity, terminating.")
                break

        logger.debug("Time 4: %fs" % (time.time() - _t0))
        _t0 = time.time()

    if use_interp:
        ip_funcs = [
            interp1d(times,
                     u[_r, j, :, best_k],
                     kind=interp_kind,
                     bounds_error=False,
                     fill_value=u[R - 1, j, -1]) for j in range(J)
        ]

        H_td_func = [H0] + [[H_ops[j], lambda t, args, j=j: ip_funcs[j](t)]
                            for j in range(J)]
    else:
        H_td_func = [H0] + [[H_ops[j], u[_r, j, :, best_k]] for j in range(J)]

    progress_bar.finished()

    result = GRAPEResult(u=u[:_r, :, :, best_k], U_f=Uf[best_k], H_t=H_td_func)

    result.eps = eps_log
    result.overlap = overlap_log

    return result
qc3.add_gate("RX", 0, None, pi / 2, r"\pi/2")
qc3.add_gate("RY", 1, None, pi / 2, r"\pi/2")
qc3.add_gate("RZ", 2, None, pi / 2, r"\pi/2")
qc3.add_gate("ISWAP", [1, 2])
# qc3.png

print("Gates on qc3")
print(qc3.gates)
print("\n")

unitaries_gates_list = qc3.propagators()
# print("Detailed gates on qc3")
# print(unitaries_gates_list)
# print("\n")

circuit_unitary_gate = gate_sequence_product(U_list=unitaries_gates_list)

print("Gate that represents the circuit qc3")
print(circuit_unitary_gate)
print("\n")

# Resolve (decompose) the circuit gate to another circuit gate
qc4 = qc3.resolve_gates(["CNOT", "RX", "RY", "RZ"])
# qc4.png

# print("propagators")
# print("\n")
# print(qc4.propagators())
# print("\n")

circuit_unitary_gate_qc4 = gate_sequence_product(U_list=qc4.propagators())