Example #1
0
def e_ops_gen(params):

    projectors = gen_projectors(params)

    sm = tensor(sigmam(), qeye(params.c_levels))
    a = tensor(qeye(2), destroy(params.c_levels))

    base_e_ops = {'a': a, 'sm': sm, 'n': a.dag() * a}
    e_ops = base_e_ops.copy()

    for key, item in base_e_ops.items():
        for i in range(2):
            for j in range(2):
                e_ops[key + '_' + str(i) + str(j)] = projectors[i] * item * projectors[j]

    for n in range(2):
        e_ops['p_q' + str(n)] = tensor(fock_dm(2, n), qeye(params.c_levels))

    for n in range(params.c_levels):
        e_ops['p_c' + str(n)] = tensor(qeye(2), fock_dm(params.c_levels, n))

    e_ops['P_0'] = projectors[0]
    e_ops['P_1'] = projectors[1]
    e_ops['P_01'] = projectors[0] * projectors[1]

    return e_ops
Example #2
0
 def controlled_mat3(arg_value):
     """
     A qubit control an operator acting on a 3 level system
     """
     control_value = arg_value
     dim = mat3.dims[0][0]
     return (tensor(fock_dm(2, control_value), mat3) +
             tensor(fock_dm(2, 1 - control_value), identity(dim)))
Example #3
0
def adc_choi(x):
    kraus = [
        np.sqrt(1 - x) * qeye(2),
        np.sqrt(x) * destroy(2),
        np.sqrt(x) * fock_dm(2, 0),
    ]
    return kraus_to_choi(kraus)
Example #4
0
    def __init__(self, nbrOfQubits=4, FockDim=3):
        self.nbrOfQubits = nbrOfQubits
        self.FockDim = FockDim

        self.qubits = []
        for i in range(nbrOfQubits):
            self.qubits.append(qt.fock_dm(FockDim, 0))
Example #5
0
 def __init__(self, resonance_freq, anharmonicity, T1=0, T2=0):
     self.resonance_freq = resonance_freq
     self.anharmonicity = anharmonicity
     self.T1 = T1
     self.T2 = T2
     self.state = qt.fock_dm(3,
                             0)  # 3-level qubit initialized in ground state
Example #6
0
 def testFockDensityMatrix(self):
     """
     states: Fock density matrix
     """
     N = 10
     for i in range(N):
         rho = fock_dm(N, i)
         # make sure rho has trace close to 1.0
         assert_(abs(rho.tr() - 1.0) < 1e-12)
         assert_(rho.data[i, i] == 1.0)
Example #7
0
 def testFockDensityMatrix(self):
     """
     states: Fock density matrix
     """
     N = 10
     for i in range(N):
         rho = fock_dm(N, i)
         # make sure rho has trace close to 1.0
         assert_(abs(rho.tr() - 1.0) < 1e-12)
         assert_(rho.data[i, i] == 1.0)
def test_smesolve_homodyne_methods():
    "Stochastic: smesolve: homodyne methods with single jump operator"
    
    def arccoth(x):
        return 0.5*np.log((1.+x)/(x-1.))
    
    th = 0.1 # Interaction parameter
    alpha = np.cos(th)
    beta = np.sin(th)
    gamma = 1.
    
    N = 30                 # number of Fock states
    Id = qeye(N)
    a = destroy(N)
    s = 0.5*((alpha+beta)*a + (alpha-beta)*a.dag())
    x = (a + a.dag()) * 2**-0.5
    H = Id
    c_op = [gamma**0.5 * a]
    sc_op = [s]
    e_op = [x, x*x]
    rho0 = fock_dm(N,0)      # initial vacuum state
    
    T = 6.                   # final time          
    N_store = 200            # number of time steps for which we save the expectation values
    Nsub = 5
    tlist = np.linspace(0, T, N_store)
    ddt = (tlist[1]-tlist[0])

    #### Analytic solution
    y0 = 0.5
    A = (gamma**2 + alpha**2 * (beta**2 + 4*gamma) - 2*alpha*beta*gamma)**0.5
    B = arccoth((-4*alpha**2*y0 + alpha*beta - gamma)/A)
    y_an = (alpha*beta - gamma + A / np.tanh(0.5*A*tlist - B))/(4*alpha**2)
    

    list_methods_tol = [['euler-maruyama', 2e-2],
                        ['fast-euler-maruyama', 2e-2],
                        ['pc-euler', 2e-3],
                        ['milstein', 1e-3],
                        ['fast-milstein', 1e-3],
                        ['milstein-imp', 1e-3],
                        ['taylor15', 1e-4],
                        ['taylor15-imp', 1e-4]
                        ]
    for n_method in list_methods_tol:
        sol = smesolve(H, rho0, tlist, c_op, sc_op, e_op,
                       nsubsteps=Nsub, method='homodyne', solver = n_method[0])
        sol2 = smesolve(H, rho0, tlist, c_op, sc_op, e_op,
                       nsubsteps=Nsub, method='homodyne', solver = n_method[0],
                       noise = sol.noise)
        err = 1/T * np.sum(np.abs(y_an - (sol.expect[1]-sol.expect[0]*sol.expect[0].conj())))*ddt
        print(n_method[0], ': deviation =', err, ', tol =', n_method[1])
        assert_(err < n_method[1])
        assert_(np.all(sol.noise == sol2.noise))# just to check that noise is not affected by smesolve
        assert_(np.all(sol.expect[0] == sol2.expect[0]))
Example #9
0
def cphase(theta, N=2, control=0, target=1):
    """
    Returns quantum object representing the controlled phase shift gate.

    Parameters
    ----------
    theta : float
        Phase rotation angle.

    N : integer
        The number of qubits in the target space.

    control : integer
        The index of the control qubit.

    target : integer
        The index of the target qubit.

    Returns
    -------
    U : qobj
        Quantum object representation of controlled phase gate.
    """

    if N < 1 or target < 0 or control < 0:
        raise ValueError("Minimum value: N=1, control=0 and target=0")

    if control >= N or target >= N:
        raise ValueError("control and target need to be smaller than N")

    U_list1 = [identity(2)] * N
    U_list2 = [identity(2)] * N

    U_list1[control] = fock_dm(2, 1)
    U_list1[target] = phasegate(theta)

    U_list2[control] = fock_dm(2, 0)

    U = tensor(U_list1) + tensor(U_list2)
    return U
Example #10
0
def test_result_states():
    N = 5
    a = qutip.destroy(N)
    coefficient = np.random.random() + 1j * np.random.random()
    H = a.dag() * a + coefficient * a + np.conj(coefficient) * a.dag()
    H_brme = [[H, '1']]
    psi0 = qutip.fock_dm(N, 2)
    times = np.linspace(0, 10, 10)
    me = qutip.mesolve(H, psi0, times).states
    brme = qutip.brmesolve(H_brme, psi0, times).states
    assert max(
        np.abs((me_state - brme_state).full()).max()
        for me_state, brme_state in zip(me, brme)) < 1e-5
Example #11
0
def test_thermal():
    N = 10
    beta = 0.5
    assert qutip.thermal_dm(N, 0) == qutip.fock_dm(N, 0)

    thermal_operator = qutip.thermal_dm(N, beta)
    thermal_analytic = qutip.thermal_dm(N, beta, method="analytic")
    np.testing.assert_allclose(thermal_operator.full(),
                               thermal_analytic.full(), atol=2e-5)

    with pytest.raises(ValueError) as e:
        qutip.thermal_dm(N, beta, method="other")
    assert str(e.value) == ("The method option can only take "
                            "values 'operator' or 'analytic'")
Example #12
0
def single_crosstalk_simulation(num_gates):
    """ A single simulation, with num_gates representing the number of rotations.

    Args:
        num_gates (int): The number of random gates to add in the simulation.

    Returns:
        result (qutip.solver.Result): A qutip Result object obtained from any of the
                                      solver methods such as mesolve.
    """
    num_qubits = 2  # Qubit-0 is the target qubit. Qubit-1 suffers from crosstalk.
    myprocessor = ModelProcessor(model=MyModel(num_qubits))
    # Add qubit frequency detuning 1.852MHz for the second qubit.
    myprocessor.add_drift(2 * np.pi * (sigmaz() + 1) / 2 * 1.852, targets=1)
    myprocessor.native_gates = None  # Remove the native gates
    mycompiler = MyCompiler(num_qubits, {
        "pulse_amplitude": 0.02,
        "duration": 25
    })
    myprocessor.add_noise(ClassicalCrossTalk(1.0))
    # Define a randome circuit.
    gates_set = [
        Gate("ROT", 0, arg_value=0),
        Gate("ROT", 0, arg_value=np.pi / 2),
        Gate("ROT", 0, arg_value=np.pi),
        Gate("ROT", 0, arg_value=np.pi / 2 * 3),
    ]
    circuit = QubitCircuit(num_qubits)
    for ind in np.random.randint(0, 4, num_gates):
        circuit.add_gate(gates_set[ind])
    # Simulate the circuit.
    myprocessor.load_circuit(circuit, compiler=mycompiler)
    init_state = tensor(
        [Qobj([[init_fid, 0], [0, 0.025]]),
         Qobj([[init_fid, 0], [0, 0.025]])])
    options = SolverOptions(nsteps=10000)  # increase the maximal allowed steps
    e_ops = [tensor([qeye(2), fock_dm(2)])]  # observable

    # compute results of the run using a solver of choice with custom options
    result = myprocessor.run_state(init_state,
                                   solver="mesolve",
                                   options=options,
                                   e_ops=e_ops)
    result = result.expect[0][-1]  # measured expectation value at the end
    return result
Example #13
0
    def osc_relax():
        w = 1.0               # oscillator frequency
        kappa = 0.1           # relaxation rate
        a = destroy(10)       # oscillator annihilation operator
        rho0 = fock_dm(10, 5) # initial state, fock state with 5 photons
        H = w * a.dag() * a   # Hamiltonian

        # A list of collapse operators
        c_ops = [sqrt(kappa) * a]
        tlist = linspace(0, 50, 100)

        # request that the solver return the expectation value of the photon number state operator a.dag() * a
        result = mesolve(H, rho0, tlist, c_ops, [a.dag() * a])
        print result
        fig, axes = plt.subplots(1,1)
        axes.plot(tlist, result.expect[0])
        axes.set_xlabel(r'$t$', fontsize=20)
        axes.set_ylabel(r"Photon number", fontsize=16);
Example #14
0
    def __init__(self, resonance_freq, anharmonicity, fock_dim=3):
        self.fock_dim = fock_dim
        self.resonance_freq = resonance_freq
        self.anharmonicity = anharmonicity

        self.state = qt.fock_dm(fock_dim, 0)
Example #15
0
def run_sim_full_and_approx(p):
    """
    TODO:
    -Should get separate the approximate (rotating wave) evolution into its own function. Then the plotting routines should take a list 
        of results, using different  
    -Double check that coupling of higher qudit levels scales as standard lowering/rasing ops to a good approximation.
    -Add co-rotating terms evolution to approximate RF results.

    IMPORTANT:
    the passed in options 'p' should not be changed inside this function if one wants to run things in parallel via multiprocessing.Pool.map()
    Otherwise, in particular if p gets entries with new objects, can lead to things break down in non-trivial way.
    
    """

    # if p.show_approx_evolution:
    # print("The codebase for the approx evolution needs a couple of minor updates with the recent changes...\n\n\n")
    # return None

    if p.N_q > 2 and p.show_approx_evolution:
        #Need to generalize the approximate treatment to more levels
        print(
            "ERROR: The approximate evolution (in terms of J_m functions) for now only handles a 2 level qudit...\n\
        ... set p.show_approx_evolution=False in order to look at the full evolution instead.\n"
        )
        return None

    #make sure the config is sane
    assert (len(p.mode_freqs) == len(p.mode_q_coupling_freqs) == len(
        p.mode_decay_rates))
    assert (p.N_q == len(p.q_level_freqs) == len(p.q_level_freqs_time_dep) ==
            len(p.q_level_dephasing_rate) == (len(p.q_level_decay_rate) + 1))
    assert (p.non_rwa_terms == 0 or p.non_rwa_terms == 1)

    results = DictExtended()

    #keep a reference to the specific options/parameters that were used in the simulation along with the results
    results.p = DictExtended(p)

    #qubit lowering operator
    c = q.tensor(q.destroy(p.N_q),
                 *[q.qeye(p.N_m) for i in xrange(p.mode_count)])

    #mode lowering operators; a[j], gives the lowering operator for the jth mode (first is the 0th indexed)
    a = []
    for j in xrange(p.mode_count):
        #prettier way to do this?
        temp_op_list = [q.qeye(p.N_q)]
        for i in xrange(p.mode_count):
            if i == j:
                temp_op_list.append(q.destroy(p.N_m))
            else:
                temp_op_list.append(q.qeye(p.N_m))

        a.append(q.tensor(*temp_op_list))

    #keep a reference to the key operators of our system
    results.op = DictExtended(a=a, c=c)

    #Build the total Hamiltonian of our system
    H_rest_list = []
    for i in xrange(p.mode_count):
        H_rest_list.append(p.mode_freqs[i] * a[i].dag() * a[i])
        H_rest_list.append(p.mode_q_coupling_freqs[i] *
                           (a[i].dag() * c + a[i] * c.dag()) +
                           p.non_rwa_terms * p.mode_q_coupling_freqs[i] *
                           (a[i].dag() * c.dag() + a[i] * c))
    H_rest_static = np.sum(H_rest_list)

    H_q_static = q.tensor(q.Qobj(np.diag(p.q_level_freqs)),
                          *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
    H_q_t_list = []
    #then time dependence...
    for lvl in xrange(p.N_q):
        h_matrix = q.tensor(q.fock_dm(p.N_q, lvl),
                            *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
        H_q_t_list.append([
            h_matrix, p.q_level_freqs_time_dep[lvl](p.tlist, p.epsilon,
                                                    p.omega_drive)
        ])

    H_static = H_q_static + H_rest_static
    results.H = [H_static] + H_q_t_list
    # print(H)

    #Get the eigenvalues/vectors of static portion of the Hamiltonian
    eigen_system = H_static.eigenstates()
    results.eigen_vals = eigen_system[0]
    results.eigen_vecs = eigen_system[1]

    if p.print_extra_info:
        print("Eigen frequencies:")
        print(
            map(lambda item: "%0.9g" % item,
                (results.eigen_vals - results.eigen_vals[0]) / (2.0 * np.pi)))
        print("Consecutive differences in eigen frequencies:")
        print(
            map(lambda item: "%0.9g" % item,
                (results.eigen_vals[1:] - results.eigen_vals[:-1]) /
                (2.0 * np.pi)))
        print("Differences between mode and 1st excited qubit freqs")
        print((np.array(p.mode_freqs) - p.q_level_freqs[1]) / (2.0 * np.pi))

    if p.only_show_ev:
        #in case we only want to see the eigenvalues/extra_info
        return None

    #Here we generate a whole bunch of states that might be of interest... but later we'll only consider some subset of those that we want to plot
    #...this could be streamlined... in reality no need to create all these states if they are not to be used... but this portion of the code is fast anyway
    #WE ASSUME that the initial state is one of those states defined here
    #Generate the single excitation states in bare basis
    results.states_collection = generate_single_excitation_states(p)
    # print(states_collection)
    #add the ground state
    results.states_collection['state_0'] = fock_state(p, 0, [])
    #add the systems's eigenstates
    eigenvectors_count = p.N_q * p.N_m * p.mode_count
    for m in xrange(eigenvectors_count):
        results.states_collection[
            "state_v%d" %
            m] = results.eigen_vecs[m] * results.eigen_vecs[m].dag()
        # results.states_collection["state_pv%d" % m]=   results.eigen_vecs[m]*results.eigen_vecs[m].dag()
    #add some other fun states
    results.states_collection["g"] = q.tensor(
        q.fock_dm(p.N_q, 0), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
    results.states_collection["e"] = q.tensor(
        q.fock_dm(p.N_q, 1), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
    results.states_collection["sup_v1-v2"] = 0.5 * (
        results.states_collection["state_v1"] +
        results.states_collection["state_v2"])

    #get the right states whose expectation values we want to plot
    results.e_ops = [
        results.states_collection[state_name]
        for state_name in p.states_to_evolve
    ]

    #initial state
    results.rho0 = results.states_collection[p.rho0_name]

    results.c_op_list = []
    #qubit dephasing
    if np.count_nonzero(p.q_level_dephasing_rate) > 0:
        dis_op = q.tensor(
            q.Qobj(np.diag(np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate))),
            *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
        if p.dissipators_in_dressed_basis:
            dis_op = dis_op.transform(results.eigen_vecs, inverse=False)
            # dis_op=dis_op.transform(results.eigen_vecs, inverse=True)
        results.c_op_list.append(dis_op)
        #TESTING
        #reverse which state "feels" the phase shift
        # rev_dephasing=np.array(p.q_level_dephasing_rate)[::-1]
    # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)]))
    # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]))
    # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)]))
    #qubit decay
    if np.count_nonzero(p.q_level_decay_rate) > 0:
        dis_op = q.tensor(q.Qobj(np.diag(np.sqrt(p.q_level_decay_rate), 1)),
                          *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
        if p.dissipators_in_dressed_basis:
            dis_op = dis_op.transform(results.eigen_vecs, inverse=False)
            # dis_op=dis_op.transform(results.eigen_vecs, inverse=True)
        results.c_op_list.append(dis_op)
        #TESTING
        # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(p.q_level_decay_rate), 1 ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)]))
    #mode decay
    for i, entry in enumerate(p.mode_decay_rates):
        if entry != 0:
            dis_op = np.sqrt(entry) * a[i]
            if p.dissipators_in_dressed_basis:
                dis_op = dis_op.transform(results.eigen_vecs, inverse=False)
                # dis_op=dis_op.transform(results.eigen_vecs, inverse=True)
            results.c_op_list.append(dis_op)
    # print(results.c_op_list)

    results.output = q.mesolve(results.H,
                               results.rho0,
                               p.tlist,
                               results.c_op_list,
                               results.e_ops,
                               options=p.odeoptions,
                               progress_bar={
                                   False: None,
                                   True: True
                               }[p.show_progress_bar])

    if p.show_approx_evolution:

        if p.N_q > 2:
            #Need to generalize the approximate (i.e. rotating frame) treatment to more levels
            print(
                "ERROR: The approximate (in terms of J_m functions) for now only handles a 2 level qudit...\n\n\n"
            )
            return None

        if p.omega_drive == 0:
            #the rotated frame we've chosen assumes p.omega_drive is not zero, because we end up with a 1/p.omega_drive in the arugment
            #of the Bessel functions.
            print(
                "ERROR: The approximate expression for the Hamiltonian (in terms of Bessel functions) assumes that omega_drive is not zero. \
                If the drive is not needed, simply set its amplitude to zero. Full numerics if of course possible with omega_drive==0."
            )
            return results

        #TODO: double check minus signs, etc and generalize to many qudit levels
        def Utrans(t):
            return ((+1.0j) * (
                (p.q_level_freqs[1] - p.q_level_freqs[0]) * t - p.epsilon /
                (1.0 * p.omega_drive) * np.cos(p.omega_drive * t)) * c.dag() *
                    c +
                    np.sum([(+1.0j) * t * p.mode_freqs[i] * a[i].dag() * a[i]
                            for i in xrange(p.mode_count)])).expm()

        results.Utrans = Utrans

        zero_ops = [q.Qobj(np.zeros((p.N_q, p.N_q)))] + [
            q.Qobj(np.zeros((p.N_m, p.N_m))) for i in xrange(p.mode_count)
        ]
        zero_H = q.tensor(*zero_ops)

        results.H_rf = []
        results.H_rf.append(
            zero_H
        )  #dirrtyyy... seems like qutip wants at least one time independent Hamiltonian portion... just give it all zeros.
        for i in xrange(p.mode_count):
            ham_coeff_time_array = np.zeros(np.array(p.tlist).shape)
            for m in p.bessel_indices:
                ham_coeff_time_array = ham_coeff_time_array + (
                    (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv(
                        m, p.epsilon / (1.0 * p.omega_drive)) * np.exp(
                            (1.0j) *
                            (p.mode_freqs[i] -
                             (p.q_level_freqs[1] - p.q_level_freqs[0]) -
                             m * p.omega_drive) * p.tlist))

            matrix_components = [c * a[i].dag(), ham_coeff_time_array]
            results.H_rf.append(matrix_components)
            results.H_rf.append([
                matrix_components[0].dag(),
                np.conjugate(matrix_components[1])
            ])

            if p.non_rwa_terms != 0:  #do the same but for the non-rwa terms
                ham_coeff_time_array = np.zeros(np.array(p.tlist).shape)
                for m in p.bessel_indices:
                    ham_coeff_time_array = ham_coeff_time_array + (
                        (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv(
                            m, p.epsilon / (1.0 * p.omega_drive)) * np.exp(
                                (1.0j) *
                                (p.mode_freqs[i] +
                                 (p.q_level_freqs[1] - p.q_level_freqs[0]) -
                                 m * p.omega_drive) * p.tlist))

                matrix_components = [
                    c.dag() * a[i].dag(), ham_coeff_time_array
                ]
                results.H_rf.append(matrix_components)
                results.H_rf.append([
                    matrix_components[0].dag(),
                    np.conjugate(matrix_components[1])
                ])

        results.rho0_rf = Utrans(0) * results.rho0 * Utrans(0).dag()

        #In general we have to be careful about how the dissipators transform in the rotated frame...
        #take them as the same as in lab frame, which should be true in our case
        # results.c_op_list = []

        #If the "measurement operators" (i.e. ones we calculate the expectation values of) do not commute with the unitary transformation
        #that defines the rotating frame, we need to transform the resulting density matrix before calculating expectation values if we
        #want to compare the results to the full lab frame evolution

        if p.transform_before_measurement:

            def e_ops_func(t, rho, transformation=Utrans, e_ops=results.e_ops):
                """Transform the density matrix into the lab frame, and calculate the expectation values. 
                TODO: this could probably be streamlined... need to look into qutip's code
                """
                rho_lab_frame = Utrans(t).dag() * q.Qobj(rho) * Utrans(t)
                for i, e_operator in enumerate(e_ops):
                    expectation_values[i][e_ops_func.idx] = q.expect(
                        e_operator, rho_lab_frame).real
                e_ops_func.idx += 1

            e_ops_func.idx = 0
            expectation_values = [
                np.zeros(len(p.tlist)) for i in xrange(len(results.e_ops))
            ]

            results.output_rf = q.mesolve(results.H_rf,
                                          results.rho0_rf,
                                          p.tlist,
                                          results.c_op_list,
                                          e_ops_func,
                                          options=p.odeoptions,
                                          progress_bar={
                                              False: None,
                                              True: True
                                          }[p.show_progress_bar])
            results.output_rf.expect = expectation_values

        else:
            results.output_rf = q.mesolve(results.H_rf,
                                          results.rho0_rf,
                                          p.tlist,
                                          results.c_op_list,
                                          results.e_ops,
                                          options=p.odeoptions,
                                          progress_bar={
                                              False: None,
                                              True: True
                                          }[p.show_progress_bar])

        print("p.epsilon / ( p.omega_drive )=%f, " %
              (p.epsilon / (1.0 * p.omega_drive), ) + ",".join([
                  "J_%d()=%f" %
                  (m, sc.special.jv(m, (p.epsilon / (1.0 * p.omega_drive))))
                  for m in p.bessel_indices
              ]))

    if p.show_plots:
        plot_results(p, results)

    return results
 def vacuum_ket(self):
     kets = []
     for m in self.modes():
         kets.append(qt.fock_dm(self.nfock(m)))
     return qt.tensor(kets)
    def __init__(self,
                 nHO=15,
                 nbar_mode=0.0,
                 delta_g=2 * pi * 40e3,
                 Omega_R=2 * pi * 80e3,
                 T=25e-6,
                 two_loops=True,
                 species='8888',
                 ion_spacing=-1,
                 mode=1,
                 factor=1,
                 tau_mot=0,
                 tau_spin=0,
                 gamma_el=0,
                 gamma_ram=0,
                 Delta_ca=-2 * pi * 10e12,
                 Delta_sr=2 * pi * 30e12,
                 Omega_R_2=2 * pi * 80e3,
                 phase_insensitive=False,
                 sq_factor=1.0,
                 qudpl_and_raman=False,
                 on_radial_modes=False,
                 nbars=[0.1, 0.1, 5, 5, 5, 5],
                 mode_freqs=None):
        ''' nHO: dimension of HO space which is simulated
        nbar_mode: mean thermal population of motional mode
        delta_g: gate detuning
        Omega_R: Rabi frequency
        T: elapsed time
        two loops: do two-loop (True) or single loop (false) gate
        species: ion species of crystal
        ion-spacing: integer (+1) or half-integer (-1) standing wavelengths
        mode: ip (1) or oop (-1), this code assumes gate is performed an axial mode
        factor: square-root of ratio of Rabi frequencies for efficiency tests
        tau_mot: motional coherence time, =0 for intinity
        tau_spin: spin coherence time, =0 for intinity
        gamma_el: Rayleigh elastic dephasing rate
        gamma_ram: Raman scattering rate
        Delta_ca: calcium Raman detuning, from 397
        Delta_sr: strontium Raman detuning, from 422
        sq_factor: relative area offset of single qubit pulses (i.e. to simulate if 
                   Ramsey pi/2 and pi pulses aren't calibrated properly)
        Only for MS gate:
        Omega_R_2: Rabi frequency for second ion, for mixed species gate driven with different lasers
        phase_insensitive: wraps gate in single qubit operations driven by gate laser, to make it insensitive
                           relative to phase of other single qubit operations
        qudpl_and_raman: use two different lasers for MS gate, with different lamb-dicke factors
        on_radial_modes: does gate on radial modes, but still sets it up for axial modes, 
                         this is used for simulating off-resonant excitation of radial modes for a tilted ion crystal
        nbars: temperature of all modes, this is only used for calculating errors from off-resonantly exciting other modes
        ! Note that most gate parameters are class objects, and therefore parameters
        are changed by previously run scans !
        '''

        self.nq = 2  # number of qubit states

        self.nHO = nHO
        self.nbar_mode = nbar_mode
        self.delta_g = delta_g
        self.Omega_R = Omega_R
        self.Omega_R_2 = Omega_R_2
        self.T = T
        self.two_loops = two_loops
        self.species = species
        self.ion_spacing = ion_spacing
        self.mode = mode
        self.factor = factor
        self.tau_mot = tau_mot
        self.tau_spin = tau_spin
        self.gamma_el = gamma_el
        self.gamma_ram = gamma_ram
        self.Delta_ca = Delta_ca  # detuning from 397 transition
        self.Delta_sr = Delta_sr  # detuning from 422 transition!! (not 408, would require different matrix elements)
        self.delta_LS = 0  # frequency offset due to light shift
        self.ampl_asym_1 = 0  # amplitude asymmetry in sidebands
        self.ampl_asym_2 = 0  # amplitude asymmetry in sidebands of second species laser
        self.species_Rabi_asym_MS = 0  # Rabi frequency asymmetry btw the two species, for MS gate
        self.phase_insensitive = phase_insensitive  # surround gate pulse with pi/2 pulses so that can use analysis pulse that is not phase-stabilised to gate
        self.sq_factor = sq_factor
        self.qudpl_and_raman = qudpl_and_raman
        self.on_radial_modes = on_radial_modes
        self.nbars = nbars

        # single qubit operators
        self.up_up = fock_dm(2, 0)  # |up><up|
        self.dn_dn = fock_dm(2, 1)  # |dn><dn|
        self.sz = self.up_up - self.dn_dn
        self.up = basis(2, 0)  # |up>
        self.dn = basis(2, 1)  # |dn>
        self.pl = (self.up + self.dn) / 2  # |+>
        self.mn = (self.up - self.dn) / 2  # |->
        self.pp = self.pl * self.pl.dag()  # |+><+|
        self.mm = self.mn * self.mn.dag()  # |+><+|
        self.pm = self.pl * self.mn.dag()  # |+><+|
        self.mp = self.mn * self.pl.dag()  # |+><+|

        # two qubit operators
        self.uu_uu = tensor(self.up_up, self.up_up)  # |up,up><up,up|
        self.dd_dd = tensor(self.dn_dn, self.dn_dn)  # |dn,dn><dn,dn|
        self.ud_ud = tensor(sigmap(), sigmap())  # |up,dn><up,dn|
        self.du_du = tensor(sigmam(), sigmam())  # |dn,up><dn,up|

        self.pp_pp = tensor(self.pp, self.pp)  # |+,+><+,+|
        self.mm_mm = tensor(self.mm, self.mm)  # |-,-><-,-|
        self.pm_pm = tensor(self.pm, self.pm)  # |+,-><+,-|
        self.mp_mp = tensor(self.mp, self.mp)  # |-,+><-,+|

        # two qubit operators with HO, o* means tensor product
        self.uu_id = tensor(self.up_up, qeye(self.nq),
                            qeye(self.nHO))  # |up><up| o* id
        self.id_uu = tensor(qeye(self.nq), self.up_up,
                            qeye(self.nHO))  # id o* |up><up|
        self.dd_id = tensor(self.dn_dn, qeye(self.nq),
                            qeye(self.nHO))  # |dn><dn| o* id
        self.id_dd = tensor(qeye(self.nq), self.dn_dn,
                            qeye(self.nHO))  # id o* |dn><dn|

        self.sp_id = tensor(sigmap(), qeye(self.nq),
                            qeye(self.nHO))  # |up><up| o* id
        self.id_sp = tensor(qeye(self.nq), sigmap(),
                            qeye(self.nHO))  # id o* |up><up|
        self.sm_id = tensor(sigmam(), qeye(self.nq),
                            qeye(self.nHO))  # |dn><dn| o* id
        self.id_sm = tensor(qeye(self.nq), sigmam(),
                            qeye(self.nHO))  # id o* |dn><dn|

        # readout projection operators
        self.spin_dndn = tensor(self.dn_dn, self.dn_dn, qeye(self.nHO))
        self.spin_upup = tensor(self.up_up, self.up_up, qeye(self.nHO))
        self.spin_updn_dnup = tensor(self.up_up, self.dn_dn, qeye(
            self.nHO)) + tensor(self.dn_dn, self.up_up, qeye(self.nHO))
        self.spin_updn = tensor(self.up_up, self.dn_dn, qeye(self.nHO))
        self.spin_dnup = tensor(self.dn_dn, self.up_up, qeye(self.nHO))

        self.spin_pp = tensor(self.pp, self.pp, qeye(self.nHO))
        self.spin_mm = tensor(self.mm, self.mm, qeye(self.nHO))
        self.spin_pm = tensor(self.pp, self.mm, qeye(self.nHO))
        self.spin_mp = tensor(self.mm, self.pp, qeye(self.nHO))

        # annihilation operator
        self.a = tensor(qeye(self.nq), qeye(self.nq), destroy(self.nHO))
        self.ad = self.a.dag()

        self.mw_offset_phase_1 = random.random(
        ) * 2 * pi  # use this phase offset for mw pulses because they don't have a fixed phase relationship to gate lasers
        self.phi_sum_1 = random.random(
        ) * 2 * pi  # sum frequency of phases of two sidebands, ie the phase that determines phi in sigma_phi
        self.phi_diff_1 = random.random(
        ) * 2 * pi  # difference frequency of phases of two sidebands, constant and ~0 for our frequency geometry

        self.setup_mode_structure(mode_freqs)
        self.set_ion_parameters()
Example #18
0
tau = 0.6
steps = 2e4
sigma = 0.01

gamma_1 = (1000. * tau)**-1.  #ridic low, just trying for stability
gamma_phi = (1000. * tau)**-1.  #ridic low, just trying for stability

delta = 2. * np.pi * 1600.
g = 2. * np.pi * 50.
kappa = 2. * np.pi * 5.
omega_cav = 2. * np.pi * 7400.

n_c = 60  #number of cavity states
n_q = 1

zero_state = qt.fock_dm(2, 0)
one_state = qt.fock_dm(2, 1)
vacuum = qt.fock_dm(n_c, 0)

zero_ket = qt.fock(2, 0)
one_ket = qt.fock(2, 1)
vacuum_ket = qt.fock(n_c, 0)

sz = jc.s_z(n_c, n_q, 0)
rest_ham = delta * (-sz / 2.)
a = jc.ann(n_c, n_q)
a_dag = a.dag()
sp = jc.s_p(n_c, n_q, 0)
sm = sp.dag()
jc_ham = g * (a * sp + a_dag * sm)
rabi_ham = [
Example #19
0
def test_fockdm_type():
    "State CSR Type: fock_dm"
    st = fock_dm(5,3)
    assert_equal(isspmatrix_csr(st.data), True)
Example #20
0
# Similarly,
print("Basis with {N} states, where state {n} is occupied:\n".format(N=4, n=2),
      fock(4, 2))
print()

# Generating a Coherent State
print(
    "Coherent State with {N} fock states in hilbert space and eigenvalue = {alpha} \n"
    .format(N=10, alpha=1.0), coherent(N=10, alpha=1.0))
print()

# Fock state (as a density matrix)
print(
    "Fock State as a Density Matrix with hilbert space size = {dim} and occupied state = {n} \n"
    .format(dim=5, n=2), fock_dm(5, 2))
print()

print(
    "Coherent State as a Density Matrix with number of fock states = {N} and eigenvalue = {alpha}"
    .format(N=8, alpha=1.0), coherent_dm(N=8, alpha=1.0))
print()

n = 1  # average number of thermal photons
print(
    "Thermal State as Density Matrix with with number of basis states in hilbert space = {N} and expectation value for number of particles in thermal state = {n}\n"
    .format(N=8, n=n), thermal_dm(8, n))

wait()
""" --------------- </Operators> --------------- """
Example #21
0
wa = 1.0 * 2 * pi  # atom frequency
g = 0.05 * 2 * pi  # coupling strength
kappa = 0.005  # cavity dissipation rate
gamma = 0.05  # atom dissipation rate
N = 15  # number of cavity fock states
n_th_a = 0.0  # avg number of thermal bath excitation
use_rwa = True

tlist = linspace(0, 100, 10000)

qt.Options(nsteps=1e10)

# intial state
psi0 = qt.tensor(qt.basis(N, 0), qt.basis(2, 0))  # start with an excited atom

e_cav = qt.tensor(qt.fock_dm(N, 1), qt.fock_dm(2, 0))
e_qb = qt.tensor(qt.fock_dm(N, 0), qt.fock_dm(2, 1))

# operators
a = qt.tensor(qt.destroy(N), qt.qeye(2))
sm = qt.tensor(qt.qeye(N), qt.destroy(2))

# Hamiltonian
if use_rwa:
    H = wc * a.dag() * a + wa * sm.dag() * sm + g * (a.dag() * sm +
                                                     a * sm.dag())
else:
    H = wc * a.dag() * a + wa * sm.dag() * sm + g * (a.dag() + a) * (sm +
                                                                     sm.dag())

c_ops = []
Example #22
0
    def evolve(self, rho, omegas, detuning, detuning_percent, detuning_fixed,
               dephasing, dephasing_sigma, timestep):
        """
		Constant state evolution of the system, for a single timestep, using the STIRAP 
		hamiltonian as in arXiv:1901.06603v1.
		:params:
		--------
		rho: (np.array) Input state of the systems (from either the previous timestep or the initial
						state)
		omegas: (np.array) An array of size 2 containing the constant pulse values for the pump and stokes
						   pulse respectively.
		omega_max: (float) Maximum value for the pulses
		detuning: (bool) True for inclusion of random detuning in the interval [-detuning_percent,detuning_percent]*omega_max
		detuning_percent: (float) Value between 0 and 1 defining the interval from which detuning is randomly sampled.
		:returns:
		---------
		rho_out: (np.array) State of the system after the evolution.
		reduced_rho_out: (np.array) Array containing only the diagonal elements of rho_out.
		"""
        tlist = [0.0, timestep]

        # Turn the listform of rho into a proper qobj for the solver
        rho_proper = qt.Qobj(rho.reshape(self.num_levels, self.num_levels))

        # Initialize an empty array to fill with the pulse values to make the hamiltonian.
        ham_array = np.zeros((self.num_levels, self.num_levels))

        for i in range(self.num_levels):
            for j in range(self.num_levels):
                if i == j + 1:
                    ham_array[i, j] = 0.5 * omegas[j]
                elif j == i + 1:
                    ham_array[i, j] = 0.5 * omegas[i]
        hamiltonian = qt.Qobj(ham_array)

        # If the detuning kwarg = True we introduce uniformly random detuning into
        # each timestep.
        if detuning_fixed == False:
            delta1 = np.random.uniform(low=-detuning_percent * self.omega_max,
                                       high=detuning_percent * self.omega_max)
            delta2 = 0
        elif detuning_fixed == True:
            delta2 = detuning_percent * self.omega_max
            delta1 = detuning_percent * self.omega_max
        elif detuning_fixed == "pierpaolo":
            delta2 = detuning_percent * self.omega_max
            delta1 = -23.5 * delta2

        detuning_hamiltonian = qt.Qobj([[0, 0, 0], [0, delta1, 0],
                                        [0, 0, delta2]])

        # Currently only compatablibe with num_levels = 3
        if detuning == True:
            H = hamiltonian + detuning_hamiltonian
            #print(H)
        else:
            H = hamiltonian
        #print(H)
        # Dephasing section:
        Sigma = dephasing_sigma * self.omega_max
        c0 = np.sqrt(Sigma) * qt.fock_dm(3, 0)
        c1 = np.sqrt(Sigma) * qt.fock_dm(3, 1)
        c2 = np.sqrt(Sigma) * qt.fock_dm(3, 2)
        collapse_operators = [c0, c1, c2]
        # Pass the (stepwise constant) hamiltonian into the mesolve function to evolve the
        # state and access the resultant state using the .states attribute.
        if dephasing == True:
            rho_out = qt.mesolve(H,
                                 rho_proper,
                                 tlist,
                                 c_ops=collapse_operators)
        elif dephasing == False:
            rho_out = qt.mesolve(H, rho_proper, tlist)

        x = rho_out.states[-1]

        # flatten the resultant state back into an array again. Also return a reduced_rho
        # containing only the diagonal elements of the density matrix.
        rho_final = x.full().reshape((self.num_levels**2, ))

        reduced_rho_final = x.diag()

        return rho_final, reduced_rho_final
Example #23
0
    axes.plot(kappa_arr, np.absolute(max_pts), '--b*')
    axes.set_xlabel("$\kappa$")
    axes.set_ylabel("Radius of Wigner Function")
    axes.set_title("$nbar(\omega)=%f$" % nbar_omega)

if __name__ == "__main__":
    gamma1 = 1  # TODO
    gamma2 = 0.5  # 0.1         # TODO
    nbar_omega = 0.1  # Temperature dependence

    tlist = np.linspace(0.0, 20.0, tN)
    nbar_omega = (0.1, 0.4, 0.7)
    kappa = np.linspace(0, 1, 3)
    kappa = np.append(kappa, np.linspace(1, 1.25, 10))
    kappa = np.append(kappa, np.linspace(1.5, 3, 4))
    rho0 = qutip.fock_dm(N, 0)

    print(kappa)

    results = [[0 for j in kappa] for i in nbar_omega]
    fig1, axes = plt.subplots(1, len(nbar_omega), figsize=(16, 3))
    for i, nb in enumerate(nbar_omega):
        for j, kap in enumerate(kappa):
            results[i][j] = solve_lindblad(gamma1, gamma2, nb, nb, kap, rho0, tlist, 0)
            print("solved for nbar_omega=%f and kappa=%f" % (nb, kap))
        plot_max_W_func(kappa, nb, results[i], axes[i])

    # plt.hold(True)
    # plt.plot(kappa)
    plt.show()
print("Eseries 1 + Eseries 2\n", es1 + es2, end="\n\n")
print("Eseries 1 - Eseries 2\n", es1 - es2, end="\n\n")
print("Eseries 1 * Eseries 2\n", es1 * es2, end="\n\n")
print("Eseries: (es1 + es2) * (es1 - es2) \n", (es1 + es2) * (es1 - es2),
      end="\n\n")

wait()
""" --------------- </Expecation Values of eseries> --------------- """

es3 = eseries([0.5 * sigmaz(), 0.5 * sigmaz()], [1j, -1j]) + eseries(
    [-0.5j * sigmax(), 0.5 * sigmax()], [1j, -1j])
print("*Eseries:\n", es3)
print("Eseries' Value at t = 0.0:\n", es3.value(0.0))
print("Eseries' Value at t = pi/2:\n", es3.value(pi / 2))

rho = fock_dm(2, 1)
es3_expect = expect(rho, es3)
print(
    "For fock density matrix(2,1) Expectation of fock density matrix(2,1) on the state given above (*Eseries)\n",
    es3_expect)
print("Expectation values at times 0 and pi/2 are:\n",
      es3_expect.value([0.0, pi / 2]))

wait()
""" --------------- </Software Versions> --------------- """
"""

Software	Version
QuTiP	    4.6.2
Numpy	    1.21.4
SciPy	    1.7.2
def test_smesolve_homodyne_methods():
    "Stochastic: smesolve: homodyne methods with single jump operator"

    def arccoth(x):
        return 0.5 * np.log((1. + x) / (x - 1.))

    th = 0.1  # Interaction parameter
    alpha = np.cos(th)
    beta = np.sin(th)
    gamma = 1.

    N = 30  # number of Fock states
    Id = qeye(N)
    a = destroy(N)
    s = 0.5 * ((alpha + beta) * a + (alpha - beta) * a.dag())
    x = (a + a.dag()) * 2**-0.5
    H = Id
    c_op = [gamma**0.5 * a]
    sc_op = [s]
    e_op = [x, x * x]
    rho0 = fock_dm(N, 0)  # initial vacuum state

    T = 6.  # final time
    N_store = 200  # number of time steps for which we save the expectation values
    Nsub = 5
    tlist = np.linspace(0, T, N_store)
    ddt = (tlist[1] - tlist[0])

    #### Analytic solution
    y0 = 0.5
    A = (gamma**2 + alpha**2 * (beta**2 + 4 * gamma) -
         2 * alpha * beta * gamma)**0.5
    B = arccoth((-4 * alpha**2 * y0 + alpha * beta - gamma) / A)
    y_an = (alpha * beta - gamma +
            A / np.tanh(0.5 * A * tlist - B)) / (4 * alpha**2)

    list_methods_tol = [['euler-maruyama', 2e-2],
                        ['fast-euler-maruyama', 2e-2], ['pc-euler', 2e-3],
                        ['milstein', 1e-3], ['fast-milstein', 1e-3],
                        ['milstein-imp', 1e-3], ['taylor15', 1e-4],
                        ['taylor15-imp', 1e-4]]
    for n_method in list_methods_tol:
        sol = smesolve(H,
                       rho0,
                       tlist,
                       c_op,
                       sc_op,
                       e_op,
                       nsubsteps=Nsub,
                       method='homodyne',
                       solver=n_method[0])
        sol2 = smesolve(H,
                        rho0,
                        tlist,
                        c_op,
                        sc_op,
                        e_op,
                        nsubsteps=Nsub,
                        method='homodyne',
                        solver=n_method[0],
                        noise=sol.noise)
        err = 1 / T * np.sum(
            np.abs(y_an - (sol.expect[1] -
                           sol.expect[0] * sol.expect[0].conj()))) * ddt
        print(n_method[0], ': deviation =', err, ', tol =', n_method[1])
        assert_(err < n_method[1])
        assert_(np.all(sol.noise == sol2.noise)
                )  # just to check that noise is not affected by smesolve
        assert_(np.all(sol.expect[0] == sol2.expect[0]))
Example #26
0
def H_approx_Par(phi):
    H = -np.exp(-phi**2 / 2) * sum(
        eval_laguerre(n, phi**2) * qt.fock_dm(NFock, n) for n in range(NFock))

    return H
Example #27
0
def test_smesolve_homodyne_methods():
    "Stochastic: smesolve: homodyne methods with single jump operator"

    def arccoth(x):
        return 0.5 * np.log((1. + x) / (x - 1.))

    th = 0.1  # Interaction parameter
    alpha = np.cos(th)
    beta = np.sin(th)
    gamma = 1.

    N = 30  # number of Fock states
    Id = qeye(N)
    a = destroy(N)
    s = 0.5 * ((alpha + beta) * a + (alpha - beta) * a.dag())
    x = (a + a.dag()) * 2**-0.5
    H = Id
    c_op = [gamma**0.5 * a]
    sc_op = [s]
    e_op = [x, x * x]
    rho0 = fock_dm(N, 0)  # initial vacuum state

    T = 3.  # final time
    # number of time steps for which we save the expectation values
    N_store = 121
    Nsub = 10
    tlist = np.linspace(0, T, N_store)
    ddt = (tlist[1] - tlist[0])

    #### Analytic solution
    y0 = 0.5
    A = (gamma**2 + alpha**2 * (beta**2 + 4 * gamma) -
         2 * alpha * beta * gamma)**0.5
    B = arccoth((-4 * alpha**2 * y0 + alpha * beta - gamma) / A)
    y_an = (alpha * beta - gamma +
            A / np.tanh(0.5 * A * tlist - B)) / (4 * alpha**2)

    list_methods_tol = [['euler-maruyama', 2e-2], ['pc-euler', 2e-3],
                        ['pc-euler-2', 2e-3], ['platen', 1e-3],
                        ['milstein', 1e-3], ['milstein-imp', 1e-3],
                        ['rouchon', 1e-3], ['taylor1.5', 1e-4],
                        ['taylor1.5-imp', 1e-4], ['explicit1.5', 1e-4],
                        ['taylor2.0', 1e-4]]
    for n_method in list_methods_tol:
        sol = smesolve(H,
                       rho0,
                       tlist,
                       c_op,
                       sc_op,
                       e_op,
                       nsubsteps=Nsub,
                       method='homodyne',
                       solver=n_method[0])
        sol2 = smesolve(H,
                        rho0,
                        tlist,
                        c_op,
                        sc_op,
                        e_op,
                        store_measurement=0,
                        nsubsteps=Nsub,
                        method='homodyne',
                        solver=n_method[0],
                        noise=sol.noise)
        sol3 = smesolve(H,
                        rho0,
                        tlist,
                        c_op,
                        sc_op,
                        e_op,
                        nsubsteps=Nsub * 5,
                        method='homodyne',
                        solver=n_method[0],
                        tol=1e-8)
        err = 1/T * np.sum(np.abs(y_an - \
                    (sol.expect[1]-sol.expect[0]*sol.expect[0].conj())))*ddt
        err3 = 1/T * np.sum(np.abs(y_an - \
                    (sol3.expect[1]-sol3.expect[0]*sol3.expect[0].conj())))*ddt
        print(n_method[0], ': deviation =', err, ', tol =', n_method[1])
        assert_(err < n_method[1])
        # 5* more substep should decrease the error
        assert_(err3 < err)
        # just to check that noise is not affected by smesolve
        assert_(np.all(sol.noise == sol2.noise))
        assert_(np.all(sol.expect[0] == sol2.expect[0]))

    sol = smesolve(H,
                   rho0,
                   tlist[:2],
                   c_op,
                   sc_op,
                   e_op,
                   noise=10,
                   ntraj=2,
                   nsubsteps=Nsub,
                   method='homodyne',
                   solver='euler',
                   store_measurement=1)
    sol2 = smesolve(H,
                    rho0,
                    tlist[:2],
                    c_op,
                    sc_op,
                    e_op,
                    noise=10,
                    ntraj=2,
                    nsubsteps=Nsub,
                    method='homodyne',
                    solver='euler',
                    store_measurement=0)
    sol3 = smesolve(H,
                    rho0,
                    tlist[:2],
                    c_op,
                    sc_op,
                    e_op,
                    noise=11,
                    ntraj=2,
                    nsubsteps=Nsub,
                    method='homodyne',
                    solver='euler')
    # sol and sol2 have the same seed, sol3 differ.
    assert_(np.all(sol.noise == sol2.noise))
    assert_(np.all(sol.noise != sol3.noise))
    assert_(not np.all(sol.measurement[0] == 0. + 0j))
    assert_(np.all(sol2.measurement[0] == 0. + 0j))
    sol = smesolve(H,
                   rho0,
                   tlist[:2],
                   c_op,
                   sc_op,
                   e_op,
                   noise=np.array([1, 2]),
                   ntraj=2,
                   nsubsteps=Nsub,
                   method='homodyne',
                   solver='euler')
    sol2 = smesolve(H,
                    rho0,
                    tlist[:2],
                    c_op,
                    sc_op,
                    e_op,
                    noise=np.array([2, 1]),
                    ntraj=2,
                    nsubsteps=Nsub,
                    method='homodyne',
                    solver='euler')
    # sol and sol2 have the seed of traj 1 and 2 reversed.
    assert_(np.all(sol.noise[0, :, :, :] == sol2.noise[1, :, :, :]))
    assert_(np.all(sol.noise[1, :, :, :] == sol2.noise[0, :, :, :]))
Example #28
0
def test_smesolve_homodyne_methods():
    "Stochastic: smesolve: homodyne methods with single jump operator"

    def arccoth(x):
        return 0.5*np.log((1.+x)/(x-1.))

    th = 0.1 # Interaction parameter
    alpha = np.cos(th)
    beta = np.sin(th)
    gamma = 1.

    N = 30                 # number of Fock states
    Id = qeye(N)
    a = destroy(N)
    s = 0.5*((alpha+beta)*a + (alpha-beta)*a.dag())
    x = (a + a.dag()) * 2**-0.5
    H = Id
    c_op = [gamma**0.5 * a]
    sc_op = [s]
    e_op = [x, x*x]
    rho0 = fock_dm(N,0)      # initial vacuum state

    T = 3.                   # final time
    # number of time steps for which we save the expectation values
    N_store = 121
    Nsub = 10
    tlist = np.linspace(0, T, N_store)
    ddt = (tlist[1]-tlist[0])

    #### Analytic solution
    y0 = 0.5
    A = (gamma**2 + alpha**2 * (beta**2 + 4*gamma) - 2*alpha*beta*gamma)**0.5
    B = arccoth((-4*alpha**2*y0 + alpha*beta - gamma)/A)
    y_an = (alpha*beta - gamma + A / np.tanh(0.5*A*tlist - B))/(4*alpha**2)


    list_methods_tol = [['euler-maruyama', 2e-2],
                        ['pc-euler', 2e-3],
                        ['pc-euler-2', 2e-3],
                        ['platen', 1e-3],
                        ['milstein', 1e-3],
                        ['milstein-imp', 1e-3],
                        ['rouchon', 1e-3],
                        ['taylor1.5', 1e-4],
                        ['taylor1.5-imp', 1e-4],
                        ['explicit1.5', 1e-4],
                        ['taylor2.0', 1e-4]]
    for n_method in list_methods_tol:
        sol = smesolve(H, rho0, tlist, c_op, sc_op, e_op,
                       nsubsteps=Nsub, method='homodyne', solver = n_method[0])
        sol2 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, store_measurement=0,
                       nsubsteps=Nsub, method='homodyne', solver = n_method[0],
                       noise = sol.noise)
        sol3 = smesolve(H, rho0, tlist, c_op, sc_op, e_op,
                        nsubsteps=Nsub*5, method='homodyne',
                        solver = n_method[0], tol=1e-8)
        err = 1/T * np.sum(np.abs(y_an - \
                    (sol.expect[1]-sol.expect[0]*sol.expect[0].conj())))*ddt
        err3 = 1/T * np.sum(np.abs(y_an - \
                    (sol3.expect[1]-sol3.expect[0]*sol3.expect[0].conj())))*ddt
        print(n_method[0], ': deviation =', err, ', tol =', n_method[1])
        assert_(err < n_method[1])
        # 5* more substep should decrease the error
        assert_(err3 < err)
        # just to check that noise is not affected by smesolve
        assert_(np.all(sol.noise == sol2.noise))
        assert_(np.all(sol.expect[0] == sol2.expect[0]))

    sol = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=10, ntraj=2,
                    nsubsteps=Nsub, method='homodyne', solver='euler',
                    store_measurement=1)
    sol2 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=10, ntraj=2,
                    nsubsteps=Nsub, method='homodyne', solver='euler',
                    store_measurement=0)
    sol3 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=11, ntraj=2,
                    nsubsteps=Nsub, method='homodyne', solver='euler')
    # sol and sol2 have the same seed, sol3 differ.
    assert_(np.all(sol.noise == sol2.noise))
    assert_(np.all(sol.noise != sol3.noise))
    assert_(not np.all(sol.measurement[0] == 0.+0j))
    assert_(np.all(sol2.measurement[0] == 0.+0j))
    sol = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=np.array([1,2]),
                   ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler')
    sol2 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=np.array([2,1]),
                   ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler')
    # sol and sol2 have the seed of traj 1 and 2 reversed.
    assert_(np.all(sol.noise[0,:,:,:] == sol2.noise[1,:,:,:]))
    assert_(np.all(sol.noise[1,:,:,:] == sol2.noise[0,:,:,:]))
H = QubitHam4_MatterSitesQubits_nocounter_rot(6, 6, 0.1, 0.1, 17.0, 16.0,
                                              74e-15, 67e-15, 30.0, 30.0e-15,
                                              -0.212381034483)
H.tidyup(atol=1e-1)
psi_0 = qt.tensor(
    [qt.basis(2, 1),
     qt.basis(n, 0),
     qt.basis(n, 1),
     qt.basis(2, 0)])
tlist = np.linspace(0, 25, 500)

e_ops = []

# Create excitation states for qubits and oscillators
g_qb = qt.fock_dm(2, 0)
e_qb = qt.fock_dm(2, 1)
g_osc = qt.fock_dm(n, 0)
e_osc = qt.fock_dm(n, 1)
f_osc = qt.fock_dm(n, 2)

PG = qt.tensor([g_qb, g_osc, g_osc, g_qb])

P1 = qt.tensor([e_qb, g_osc, e_osc, g_qb])
P2 = qt.tensor([g_qb, e_osc, e_osc, g_qb])
P3 = qt.tensor([g_qb, e_osc, g_osc, e_qb])
P4 = qt.tensor([g_qb, g_osc, e_osc, e_qb])
P5 = qt.tensor([g_qb, f_osc, g_osc, g_qb])
P6 = qt.tensor([g_qb, g_osc, f_osc, g_qb])

#P_e1 = qt.tensor([e_qb,qt.qeye(n),qt.qeye(n),qt.qeye(2)])
Example #30
0
def run_sim_full_and_approx(p):
    """
    TODO:
    -Should get separate the approximate (rotating wave) evolution into its own function. Then the plotting routines should take a list 
        of results, using different  
    -Double check that coupling of higher qudit levels scales as standard lowering/rasing ops to a good approximation.
    -Add co-rotating terms evolution to approximate RF results.

    IMPORTANT:
    the passed in options 'p' should not be changed inside this function if one wants to run things in parallel via multiprocessing.Pool.map()
    Otherwise, in particular if p gets entries with new objects, can lead to things break down in non-trivial way.
    
    """

    # if p.show_approx_evolution:
        # print("The codebase for the approx evolution needs a couple of minor updates with the recent changes...\n\n\n")
        # return None

    if p.N_q>2 and p.show_approx_evolution:
        #Need to generalize the approximate treatment to more levels
        print("ERROR: The approximate evolution (in terms of J_m functions) for now only handles a 2 level qudit...\n\
        ... set p.show_approx_evolution=False in order to look at the full evolution instead.\n") 
        return None

    #make sure the config is sane
    assert(len(p.mode_freqs)==len(p.mode_q_coupling_freqs)==len(p.mode_decay_rates))
    assert(p.N_q==len(p.q_level_freqs)==len(p.q_level_freqs_time_dep)==len(p.q_level_dephasing_rate)==(len(p.q_level_decay_rate)+1))
    assert(p.non_rwa_terms==0 or p.non_rwa_terms==1)

    results=DictExtended()
    
    #keep a reference to the specific options/parameters that were used in the simulation along with the results 
    results.p=DictExtended(p)

    #qubit lowering operator
    c=q.tensor(q.destroy(p.N_q), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])


    #mode lowering operators; a[j], gives the lowering operator for the jth mode (first is the 0th indexed)
    a=[]
    for j in xrange(p.mode_count):
        #prettier way to do this?
        temp_op_list=[q.qeye(p.N_q)]
        for i in xrange(p.mode_count):
            if i==j:
                temp_op_list.append(q.destroy(p.N_m))
            else:
                temp_op_list.append(q.qeye(p.N_m))

        a.append(q.tensor(*temp_op_list))

    #keep a reference to the key operators of our system
    results.op=DictExtended(a=a, c=c)

    #Build the total Hamiltonian of our system
    H_rest_list=[]
    for i in xrange(p.mode_count):
        H_rest_list.append(p.mode_freqs[i] * a[i].dag() * a[i])
        H_rest_list.append(p.mode_q_coupling_freqs[i] * (a[i].dag()*c + a[i]*c.dag()) + 
                        p.non_rwa_terms * p.mode_q_coupling_freqs[i] * (a[i].dag()*c.dag() + a[i]*c) )
    H_rest_static=np.sum(H_rest_list)

    H_q_static=q.tensor(q.Qobj(np.diag(p.q_level_freqs)), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
    H_q_t_list=[]
    #then time dependence... 
    for lvl in xrange(p.N_q):
        h_matrix=q.tensor(q.fock_dm(p.N_q,lvl), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
        H_q_t_list.append([h_matrix, p.q_level_freqs_time_dep[lvl](p.tlist, p.epsilon, p.omega_drive)])

    H_static=H_q_static + H_rest_static
    results.H=[H_static] + H_q_t_list
    # print(H)

    #Get the eigenvalues/vectors of static portion of the Hamiltonian
    eigen_system=H_static.eigenstates()
    results.eigen_vals=eigen_system[0]
    results.eigen_vecs=eigen_system[1]

    if p.print_extra_info:
        print("Eigen frequencies:")
        print(map(lambda item: "%0.9g" % item, (results.eigen_vals-results.eigen_vals[0])/(2.0*np.pi)))
        print("Consecutive differences in eigen frequencies:")
        print(map(lambda item: "%0.9g" % item, (results.eigen_vals[1:] - results.eigen_vals[:-1])/(2.0*np.pi)))
        print("Differences between mode and 1st excited qubit freqs")
        print((np.array(p.mode_freqs) - p.q_level_freqs[1])/(2.0*np.pi))

    if p.only_show_ev:
        #in case we only want to see the eigenvalues/extra_info
        return None

    #Here we generate a whole bunch of states that might be of interest... but later we'll only consider some subset of those that we want to plot
    #...this could be streamlined... in reality no need to create all these states if they are not to be used... but this portion of the code is fast anyway
    #WE ASSUME that the initial state is one of those states defined here
    #Generate the single excitation states in bare basis
    results.states_collection=generate_single_excitation_states(p)
    # print(states_collection)
    #add the ground state
    results.states_collection['state_0']=fock_state(p, 0,[])
    #add the systems's eigenstates
    eigenvectors_count=p.N_q*p.N_m*p.mode_count
    for m in xrange(eigenvectors_count):
        results.states_collection["state_v%d" % m]=results.eigen_vecs[m]*results.eigen_vecs[m].dag()
        # results.states_collection["state_pv%d" % m]=   results.eigen_vecs[m]*results.eigen_vecs[m].dag()
    #add some other fun states
    results.states_collection["g"]=q.tensor(q.fock_dm(p.N_q, 0), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
    results.states_collection["e"]=q.tensor(q.fock_dm(p.N_q, 1), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
    results.states_collection["sup_v1-v2"]=0.5 * (results.states_collection["state_v1"] + results.states_collection["state_v2"])

    #get the right states whose expectation values we want to plot
    results.e_ops=[results.states_collection[state_name] for state_name in p.states_to_evolve]

    #initial state
    results.rho0 = results.states_collection[p.rho0_name]

    results.c_op_list = []
    #qubit dephasing
    if np.count_nonzero(p.q_level_dephasing_rate)>0:
        dis_op=q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate) ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
        if p.dissipators_in_dressed_basis:
            dis_op=dis_op.transform(results.eigen_vecs, inverse=False)
            # dis_op=dis_op.transform(results.eigen_vecs, inverse=True)
        results.c_op_list.append(dis_op)
        #TESTING
        #reverse which state "feels" the phase shift
        # rev_dephasing=np.array(p.q_level_dephasing_rate)[::-1]
       # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)]))
        # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]))
        # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)]))
    #qubit decay
    if np.count_nonzero(p.q_level_decay_rate)>0:
        dis_op=q.tensor(q.Qobj(np.diag( np.sqrt(p.q_level_decay_rate), 1 ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])
        if p.dissipators_in_dressed_basis:
            dis_op=dis_op.transform(results.eigen_vecs, inverse=False)
            # dis_op=dis_op.transform(results.eigen_vecs, inverse=True)
        results.c_op_list.append(dis_op)
        #TESTING
        # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(p.q_level_decay_rate), 1 ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)]))
    #mode decay
    for i, entry in enumerate(p.mode_decay_rates):
        if entry!=0:
            dis_op=np.sqrt(entry) * a[i]
            if p.dissipators_in_dressed_basis:
                dis_op=dis_op.transform(results.eigen_vecs, inverse=False)
                # dis_op=dis_op.transform(results.eigen_vecs, inverse=True)
            results.c_op_list.append(dis_op)
    # print(results.c_op_list)

    results.output = q.mesolve(results.H, results.rho0, p.tlist, results.c_op_list, results.e_ops, options=p.odeoptions, progress_bar={False:None, True:True}[p.show_progress_bar])

    if p.show_approx_evolution:

        if p.N_q>2:
            #Need to generalize the approximate (i.e. rotating frame) treatment to more levels
            print("ERROR: The approximate (in terms of J_m functions) for now only handles a 2 level qudit...\n\n\n") 
            return None

        if p.omega_drive==0:
            #the rotated frame we've chosen assumes p.omega_drive is not zero, because we end up with a 1/p.omega_drive in the arugment
            #of the Bessel functions.
            print("ERROR: The approximate expression for the Hamiltonian (in terms of Bessel functions) assumes that omega_drive is not zero. \
                If the drive is not needed, simply set its amplitude to zero. Full numerics if of course possible with omega_drive==0.")
            return results

        #TODO: double check minus signs, etc and generalize to many qudit levels
        def Utrans(t): 
            return ( (+1.0j) *  (  (p.q_level_freqs[1] - p.q_level_freqs[0]) * t  - p.epsilon/(1.0 * p.omega_drive ) * np.cos(p.omega_drive * t) ) * c.dag()*c   +
                    np.sum([  (+1.0j) * t * p.mode_freqs[i] * a[i].dag()*a[i]  for i in xrange(p.mode_count)]) ).expm()

        results.Utrans=Utrans

        zero_ops=[q.Qobj(np.zeros((p.N_q, p.N_q)))] + [q.Qobj(np.zeros((p.N_m, p.N_m))) for i in xrange(p.mode_count)]
        zero_H=q.tensor(*zero_ops) 

        results.H_rf=[]
        results.H_rf.append(zero_H)  #dirrtyyy... seems like qutip wants at least one time independent Hamiltonian portion... just give it all zeros.
        for i in xrange(p.mode_count):
            ham_coeff_time_array=np.zeros(np.array(p.tlist).shape)
            for m in p.bessel_indices:
                ham_coeff_time_array=ham_coeff_time_array + ( (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv(m, p.epsilon / ( 1.0 * p.omega_drive )) * np.exp( (1.0j) *  (p.mode_freqs[i] - (p.q_level_freqs[1] - p.q_level_freqs[0]) - m * p.omega_drive )  * p.tlist))

            matrix_components=[c*a[i].dag(), ham_coeff_time_array]
            results.H_rf.append(matrix_components)
            results.H_rf.append([matrix_components[0].dag(), np.conjugate(matrix_components[1])])
            
            if p.non_rwa_terms!=0:  #do the same but for the non-rwa terms
                ham_coeff_time_array=np.zeros(np.array(p.tlist).shape)
                for m in p.bessel_indices:
                    ham_coeff_time_array=ham_coeff_time_array + ( (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv(m, p.epsilon / ( 1.0 * p.omega_drive )) * np.exp( (1.0j) *  (p.mode_freqs[i] + (p.q_level_freqs[1] - p.q_level_freqs[0]) - m * p.omega_drive )  * p.tlist))

                matrix_components=[c.dag()*a[i].dag(), ham_coeff_time_array]
                results.H_rf.append(matrix_components)
                results.H_rf.append([matrix_components[0].dag(), np.conjugate(matrix_components[1])])

        results.rho0_rf=Utrans(0)*results.rho0*Utrans(0).dag()

        #In general we have to be careful about how the dissipators transform in the rotated frame... 
        #take them as the same as in lab frame, which should be true in our case
        # results.c_op_list = []

        #If the "measurement operators" (i.e. ones we calculate the expectation values of) do not commute with the unitary transformation 
        #that defines the rotating frame, we need to transform the resulting density matrix before calculating expectation values if we 
        #want to compare the results to the full lab frame evolution

        if p.transform_before_measurement:

            def e_ops_func(t, rho, transformation=Utrans, e_ops=results.e_ops):
                """Transform the density matrix into the lab frame, and calculate the expectation values. 
                TODO: this could probably be streamlined... need to look into qutip's code
                """
                rho_lab_frame=Utrans(t).dag()*q.Qobj(rho)*Utrans(t)
                for i, e_operator in enumerate(e_ops):
                    expectation_values[i][e_ops_func.idx]=q.expect(e_operator, rho_lab_frame).real
                e_ops_func.idx+=1

            e_ops_func.idx=0
            expectation_values=[np.zeros(len(p.tlist)) for i in xrange(len(results.e_ops))]

            results.output_rf=q.mesolve(results.H_rf, results.rho0_rf, p.tlist, results.c_op_list, e_ops_func, options=p.odeoptions, progress_bar={False:None, True:True}[p.show_progress_bar])
            results.output_rf.expect=expectation_values

        else:
            results.output_rf=q.mesolve(results.H_rf, results.rho0_rf, p.tlist, results.c_op_list, results.e_ops, options=p.odeoptions, progress_bar={False:None, True:True}[p.show_progress_bar])

        print("p.epsilon / ( p.omega_drive )=%f, " %  (p.epsilon / ( 1.0 * p.omega_drive ), ) + 
                ",".join([ "J_%d()=%f" % (m, sc.special.jv(m,(p.epsilon / ( 1.0 * p.omega_drive )))) for m in p.bessel_indices]) ) 

    if p.show_plots: 
        plot_results(p, results)

    return results
Example #31
0
import matplotlib.pyplot as plt
import qutip

N = 25
taus = np.linspace(0, 25.0, 200)
a = qutip.destroy(N)
H = 2 * np.pi * a.dag() * a

kappa = 0.25
n_th = 2.0  # bath temperature in terms of excitation number
c_ops = [np.sqrt(kappa * (1 + n_th)) * a, np.sqrt(kappa * n_th) * a.dag()]

states = [
    {'state': qutip.coherent_dm(N, np.sqrt(2)), 'label': "coherent state"},
    {'state': qutip.thermal_dm(N, 2), 'label': "thermal state"},
    {'state': qutip.fock_dm(N, 2), 'label': "Fock state"},
]

fig, ax = plt.subplots(1, 1)

for state in states:
    rho0 = state['state']

    # first calculate the occupation number as a function of time
    n = qutip.mesolve(H, rho0, taus, c_ops, [a.dag() * a]).expect[0]

    # calculate the correlation function G2 and normalize with n(0)n(t) to
    # obtain g2
    G2 = qutip.correlation_3op_1t(H, rho0, taus, c_ops, a.dag(), a.dag()*a, a)
    g2 = G2 / (n[0] * n)