Beispiel #1
0
def mi_mcsolve(H,
               psi0,
               tlist,
               c_ops,
               e_ops,
               ntraj=500,
               args={},
               options=Odeoptions()):
    if psi0.type != 'ket':
        raise ValueError("psi0 must be a state vector")
    if type(ntraj) == int:
        ntraj = [ntraj]
    elif type(ntraj[0]) != int:
        raise ValueError(
            "ntraj must either be an integer or a list of integers")

    num_eops = len(e_ops)
    num_cops = len(c_ops)
    # Just use mcsolve if there aren't any collapse or expect. operators
    if num_eops == num_cops == 0:
        raise ValueError(
            "Must supply at least one expectation value operator.")
        # should not ever meet this condition
        #return qutip.mcsolve(H, psi0, tlist, c_ops, e_ops, ntraj, args, options)
    elif num_cops == 0:
        ntraj = 1

    # Let's be sure we're not changing anything:
    H = copy.deepcopy(H)
    H = np.matrix(H.full())
    psi0 = copy.deepcopy(psi0)
    psi0 = psi0.full()
    tlist = copy.deepcopy(tlist)
    c_ops = copy.deepcopy(c_ops)
    for i in range(num_cops):
        c_ops[i] = np.matrix(c_ops[i].full())
    e_ops = copy.deepcopy(e_ops)
    eops_herm = [False for _ in range(num_eops)]
    for i in range(num_eops):
        e_ops[i] = np.matrix(e_ops[i].full())
        eops_herm[i] = not any(abs(e_ops[i].getH() - e_ops[i]) >
                               1e-15)  # check if each e_op is Hermetian

    # Construct the effective Hamiltonian
    Heff = H
    for cop in c_ops:
        Heff += -0.5j * np.dot(cop.getH(), cop)
    Heff = (-1j) * Heff
    # Find the eigenstates of the effective Hamiltonian
    la, v = np.linalg.eig(Heff)
    # Construct the similarity transformation matricies
    S = np.matrix(v)
    Sinv = np.linalg.inv(S)
    Heff_diag = np.dot(Sinv, np.dot(Heff, S)).round(10)

    for i in range(num_cops):
        c_ops[i] = np.dot(
            c_ops[i], S)  # Multiply each Collapse Operator to the left by S

    psi0 = psi0 / np.linalg.norm(psi0)
    psi0_nb = np.dot(Sinv, psi0)  # change basis for initial state vector

    for i in range(num_eops):
        e_ops[i] = np.dot(
            S.getH(), np.dot(e_ops[i], S)
        )  # Change basis for the operator for which expectation values are requested

    if len(ntraj) > 1:
        exp_vals = [
            list(
                np.zeros(len(tlist),
                         dtype=(float if eops_herm[i] else complex))
                for i in range(num_eops)) for _ in range(len(ntraj))
        ]
        collapse_times_out = [list() for _ in range(len(ntraj))]
        which_op_out = [list() for _ in range(len(ntraj))]
    else:
        exp_vals = list(
            np.zeros(len(tlist), dtype=(float if eops_herm[i] else complex))
            for i in range(num_eops))
        collapse_times_out, which_op_out = list(), list()
    for _n in range(len(ntraj)):  # ntraj can be passed in as a list
        print "Calculation Starting on", multiprocessing.cpu_count(), "CPUs"
        p = Pool()

        def callback(r):  # method to display progress
            callback.counter += 1
            if (round(100.0 * float(callback.counter) / callback.ntraj) >=
                    10 + round(100.0 * float(callback.last) / callback.ntraj)):
                print "Progress: %.0f%% (approx. %.2fs remaining)" % (
                    (100.0 * float(callback.counter) / callback.ntraj),
                    ((time.time() - callback.start) / callback.counter *
                     (callback.ntraj - callback.counter)))
                callback.last = callback.counter

        callback.last = 0
        callback.counter = 0
        callback.ntraj = ntraj[_n]
        callback.start = time.time()

        results = [
            r.get() for r in [
                p.apply_async(one_traj, (Heff_diag, S, Sinv, psi0_nb, tlist,
                                         e_ops, c_ops, num_eops,
                                         num_cops), {}, callback)
                for _ in range(ntraj[_n])
            ]
        ]
        p.close()
        p.join()
        # The following is a manipulation of the data resulting from the calculation
        # The goal is to output the results in an identical format as those from qutip.mcsolve()
        if len(ntraj) > 1:
            for i in range(ntraj[_n]):
                collapse_times_out[_n].append(results[i][1])
                which_op_out[_n].append(results[i][2])
                for j in range(num_eops):
                    if eops_herm[j]: exp_vals[_n][j] += results[i][0][j].real
                    else: exp_vals[_n][j] += results[i][0][j]
            for i in range(num_eops):
                exp_vals[_n][i] = exp_vals[_n][i] / ntraj[_n]
        else:
            for i in range(ntraj[_n]):
                collapse_times_out.append(results[i][1])
                which_op_out.append(results[i][2])
                for j in range(num_eops):
                    if eops_herm[j]: exp_vals[j] += results[i][0][j].real
                    else: exp_vals[j] += results[i][0][j]
            for i in range(num_eops):
                exp_vals[i] = exp_vals[i] / ntraj[_n]
    output = Odedata()
    output.solver = 'mi_mcsolve'
    output.expect = exp_vals
    output.times = tlist
    output.num_expect = num_eops
    output.num_collapse = num_cops
    output.ntraj = ntraj
    output.col_times = collapse_times_out
    output.col_which = which_op_out
    return output
Beispiel #2
0
    def evolve_serial(self, args):

        if debug:
            print(inspect.stack()[0][3] + ":" + str(os.getpid()))

        # run ntraj trajectories for one process via fortran
        # get args
        queue, ntraj, instanceno, rngseed = args
        # initialize the problem in fortran
        _init_tlist()
        _init_psi0()
        if (self.ptrace_sel != []):
            _init_ptrace_stuff(self.ptrace_sel)
        _init_hamilt()
        if (odeconfig.c_num != 0):
            _init_c_ops()
        if (odeconfig.e_num != 0):
            _init_e_ops()
        # set options
        qtf90.qutraj_run.n_c_ops = odeconfig.c_num
        qtf90.qutraj_run.n_e_ops = odeconfig.e_num
        qtf90.qutraj_run.ntraj = ntraj
        qtf90.qutraj_run.unravel_type = self.unravel_type
        qtf90.qutraj_run.average_states = odeconfig.options.average_states 
        qtf90.qutraj_run.average_expect = odeconfig.options.average_expect
        qtf90.qutraj_run.init_odedata(odeconfig.psi0_shape[0],
                                      odeconfig.options.atol,
                                      odeconfig.options.rtol, mf=self.mf,
                                      norm_steps=odeconfig.norm_steps,
                                      norm_tol=odeconfig.norm_tol)
        # set optional arguments
        qtf90.qutraj_run.order = odeconfig.options.order
        qtf90.qutraj_run.nsteps = odeconfig.options.nsteps
        qtf90.qutraj_run.first_step = odeconfig.options.first_step
        qtf90.qutraj_run.min_step = odeconfig.options.min_step
        qtf90.qutraj_run.max_step = odeconfig.options.max_step
        qtf90.qutraj_run.norm_steps = odeconfig.options.norm_steps
        qtf90.qutraj_run.norm_tol = odeconfig.options.norm_tol
        # use sparse density matrices during computation?
        qtf90.qutraj_run.rho_return_sparse = self.sparse_dms
        # calculate entropy of reduced density matrice?
        qtf90.qutraj_run.calc_entropy = self.calc_entropy
        # run
        show_progress = 1 if debug else 0
        qtf90.qutraj_run.evolve(instanceno, rngseed, show_progress)
    

        # construct Odedata instance
        sol = Odedata()
        sol.ntraj = ntraj
        # sol.col_times = qtf90.qutraj_run.col_times
        # sol.col_which = qtf90.qutraj_run.col_which-1
        sol.col_times, sol.col_which = self.get_collapses(ntraj)
        if (odeconfig.e_num == 0):
            sol.states = self.get_states(len(odeconfig.tlist), ntraj)
        else:
            sol.expect = self.get_expect(len(odeconfig.tlist), ntraj)
        if (self.calc_entropy):
            sol.entropy = self.get_entropy(len(odeconfig.tlist))

        if (not self.serial_run):            
            # put to queue
            queue.put(sol)
            queue.join()

        # deallocate stuff
        # finalize()
        return sol
Beispiel #3
0
def mcsolve_f90(H, psi0, tlist, c_ops, e_ops, ntraj=None,
                options=Odeoptions(), sparse_dms=True, serial=False,
                ptrace_sel=[], calc_entropy=False):
    """
    Monte-Carlo wave function solver with fortran 90 backend.
    Usage is identical to qutip.mcsolve, for problems without explicit
    time-dependence, and with some optional input:

    Parameters
    ----------
    H : qobj
        System Hamiltonian.
    psi0 : qobj
        Initial state vector
    tlist : array_like
        Times at which results are recorded.
    ntraj : int
        Number of trajectories to run.
    c_ops : array_like
        ``list`` or ``array`` of collapse operators.
    e_ops : array_like
        ``list`` or ``array`` of operators for calculating expectation values.
    options : Odeoptions
        Instance of ODE solver options.
    sparse_dms : boolean
        If averaged density matrices are returned, they will be stored as
        sparse (Compressed Row Format) matrices during computation if
        sparse_dms = True (default), and dense matrices otherwise. Dense
        matrices might be preferable for smaller systems.
    serial : boolean
        If True (default is False) the solver will not make use of the
        multiprocessing module, and simply run in serial.
    ptrace_sel: list
        This optional argument specifies a list of components to keep when
        returning a partially traced density matrix. This can be convenient for
        large systems where memory becomes a problem, but you are only
        interested in parts of the density matrix.
    calc_entropy : boolean
        If ptrace_sel is specified, calc_entropy=True will have the solver
        return the averaged entropy over trajectories in results.entropy. This
        can be interpreted as a measure of entanglement. See Phys. Rev. Lett.
        93, 120408 (2004), Phys. Rev. A 86, 022310 (2012).

    Returns
    -------
    results : Odedata
        Object storing all results from simulation.

    """
    if ntraj is None:
        ntraj = options.ntraj

    if psi0.type != 'ket':
        raise Exception("Initial state must be a state vector.")
    odeconfig.options = options
    # set num_cpus to the value given in qutip.settings
    # if none in Odeoptions
    if not odeconfig.options.num_cpus:
        odeconfig.options.num_cpus = qutip.settings.num_cpus
    # set initial value data
    if options.tidy:
        odeconfig.psi0 = psi0.tidyup(options.atol).full()
    else:
        odeconfig.psi0 = psi0.full()
    odeconfig.psi0_dims = psi0.dims
    odeconfig.psi0_shape = psi0.shape
    # set general items
    odeconfig.tlist = tlist
    if isinstance(ntraj, (list, np.ndarray)):
        raise Exception("ntraj as list argument is not supported.")
    else:
        odeconfig.ntraj = ntraj
        # ntraj_list = [ntraj]
    # set norm finding constants
    odeconfig.norm_tol = options.norm_tol
    odeconfig.norm_steps = options.norm_steps

    if not options.rhs_reuse:
        odeconfig.soft_reset()
        # no time dependence
        odeconfig.tflag = 0
        # check for collapse operators
        if len(c_ops) > 0:
            odeconfig.cflag = 1
        else:
            odeconfig.cflag = 0
        # Configure data
        _mc_data_config(H, psi0, [], c_ops, [], [], e_ops, options, odeconfig)

    # Load Monte Carlo class
    mc = _MC_class()
    # Set solver type
    if (options.method == 'adams'):
        mc.mf = 10
    elif (options.method == 'bdf'):
        mc.mf = 22
    else:
        if debug:
            print('Unrecognized method for ode solver, using "adams".')
        mc.mf = 10
    # store ket and density matrix dims and shape for convenience
    mc.psi0_dims = psi0.dims
    mc.psi0_shape = psi0.shape
    mc.dm_dims = (psi0 * psi0.dag()).dims
    mc.dm_shape = (psi0 * psi0.dag()).shape
    # use sparse density matrices during computation?
    mc.sparse_dms = sparse_dms
    # run in serial?
    mc.serial_run = serial or (ntraj == 1)
    # are we doing a partial trace for returned states?
    mc.ptrace_sel = ptrace_sel
    if (ptrace_sel != []):
        if debug:
            print("ptrace_sel set to " + str(ptrace_sel))
            print("We are using dense density matrices during computation " +
                  "when performing partial trace. Setting sparse_dms = False")
            print("This feature is experimental.")
        mc.sparse_dms = False
        mc.dm_dims = psi0.ptrace(ptrace_sel).dims
        mc.dm_shape = psi0.ptrace(ptrace_sel).shape
    if (calc_entropy):
        if (ptrace_sel == []):
            if debug:
                print("calc_entropy = True, but ptrace_sel = []. Please set " +
                     "a list of components to keep when calculating average " +
                     "entropy of reduced density matrix in ptrace_sel. " +
                     "Setting calc_entropy = False.")
            calc_entropy = False
        mc.calc_entropy = calc_entropy

    # construct output Odedata object
    output = Odedata()

    # Run
    mc.run()
    output.states = mc.sol.states
    output.expect = mc.sol.expect
    output.col_times = mc.sol.col_times
    output.col_which = mc.sol.col_which
    if (hasattr(mc.sol, 'entropy')):
        output.entropy = mc.sol.entropy

    output.solver = 'Fortran 90 Monte Carlo solver'
    # simulation parameters
    output.times = odeconfig.tlist
    output.num_expect = odeconfig.e_num
    output.num_collapse = odeconfig.c_num
    output.ntraj = odeconfig.ntraj

    return output
Beispiel #4
0
def mcsolve(H,psi0,tlist,c_ops,e_ops,ntraj=500,args={},options=Odeoptions()):
    """Monte-Carlo evolution of a state vector :math:`|\psi \\rangle` for a given
    Hamiltonian and sets of collapse operators, and possibly, operators
    for calculating expectation values. Options for the underlying ODE solver are 
    given by the Odeoptions class.
    
    mcsolve supports time-dependent Hamiltonians and collapse operators using either
    Python functions of strings to represent time-dependent coefficients.  Note that, 
    the system Hamiltonian MUST have at least one constant term.
    
    As an example of a time-dependent problem, consider a Hamiltonian with two terms ``H0``
    and ``H1``, where ``H1`` is time-dependent with coefficient ``sin(w*t)``, and collapse operators
    ``C0`` and ``C1``, where ``C1`` is time-dependent with coeffcient ``exp(-a*t)``.  Here, w and a are
    constant arguments with values ``W`` and ``A``.  
    
    Using the Python function time-dependent format requires two Python functions,
    one for each collapse coefficient. Therefore, this problem could be expressed as::
    
        def H1_coeff(t,args):
            return sin(args['w']*t)
    
        def C1_coeff(t,args):
            return exp(-args['a']*t)
    
        H=[H0,[H1,H1_coeff]]
    
        c_op_list=[C0,[C1,C1_coeff]]
    
        args={'a':A,'w':W}
    
    or in String (Cython) format we could write::
    
        H=[H0,[H1,'sin(w*t)']]
    
        c_op_list=[C0,[C1,'exp(-a*t)']]
    
        args={'a':A,'w':W}
    
    Constant terms are preferably placed first in the Hamiltonian and collapse 
    operator lists.
    
    Parameters
    ----------
    H : qobj
        System Hamiltonian.
    psi0 : qobj 
        Initial state vector
    tlist : array_like 
        Times at which results are recorded.
    ntraj : int 
        Number of trajectories to run.
    c_ops : array_like 
        single collapse operator or ``list`` or ``array`` of collapse operators.
    e_ops : array_like 
        single operator or ``list`` or ``array`` of operators for calculating expectation values.
    args : dict
        Arguments for time-dependent Hamiltonian and collapse operator terms.
    options : Odeoptions
        Instance of ODE solver options.
    
    Returns
    -------
    results : Odedata    
        Object storing all results from simulation.
        
    """


    # if single operator is passed for c_ops or e_ops, convert it to
    # list containing only that operator
    if isinstance(c_ops, Qobj):
        c_ops = [c_ops]
    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]


    if psi0.type!='ket':
        raise Exception("Initial state must be a state vector.")
    odeconfig.options=options
    #set num_cpus to the value given in qutip.settings if none in Odeoptions
    if not odeconfig.options.num_cpus:
        odeconfig.options.num_cpus=qutip.settings.num_cpus
    #set initial value data
    if options.tidy:
        odeconfig.psi0=psi0.tidyup(options.atol).full()
    else:
        odeconfig.psi0=psi0.full()
    odeconfig.psi0_dims=psi0.dims
    odeconfig.psi0_shape=psi0.shape
    #set general items
    odeconfig.tlist=tlist
    if isinstance(ntraj,(list,ndarray)):
        odeconfig.ntraj=sort(ntraj)[-1]
    else:
        odeconfig.ntraj=ntraj
    #set norm finding constants
    odeconfig.norm_tol=options.norm_tol
    odeconfig.norm_steps=options.norm_steps
    #----
    
    #----------------------------------------------
    # SETUP ODE DATA IF NONE EXISTS OR NOT REUSING
    #----------------------------------------------
    if (not options.rhs_reuse) or (not odeconfig.tdfunc):
        #reset odeconfig collapse and time-dependence flags to default values
        _reset_odeconfig()
        
        #check for type of time-dependence (if any)
        time_type,h_stuff,c_stuff=_ode_checks(H,c_ops,'mc')
        h_terms=len(h_stuff[0])+len(h_stuff[1])+len(h_stuff[2])
        c_terms=len(c_stuff[0])+len(c_stuff[1])+len(c_stuff[2])
        #set time_type for use in multiprocessing
        odeconfig.tflag=time_type
        
        #-Check for PyObjC on Mac platforms
        if sys.platform=='darwin' and odeconfig.options.gui:
            try:
                import Foundation
            except:
                odeconfig.options.gui=False

        #check if running in iPython and using Cython compiling (then no GUI to work around error)
        if odeconfig.options.gui and odeconfig.tflag in array([1,10,11]):
            try:
                __IPYTHON__
            except:
                pass
            else:
                odeconfig.options.gui=False    
        if qutip.settings.qutip_gui=="NONE":
            odeconfig.options.gui=False

        #check for collapse operators
        if c_terms>0:
            odeconfig.cflag=1
        else:
            odeconfig.cflag=0
    
        #Configure data
        _mc_data_config(H,psi0,h_stuff,c_ops,c_stuff,args,e_ops,options)
        if odeconfig.tflag in array([1,10,11]): #compile time-depdendent RHS code
            os.environ['CFLAGS'] = '-O3 -w'
            import pyximport
            pyximport.install(setup_args={'include_dirs':[numpy.get_include()]})
            if odeconfig.tflag in array([1,11]):
                code = compile('from '+odeconfig.tdname+' import cyq_td_ode_rhs,col_spmv,col_expect', '<string>', 'exec')
                exec(code, globals())
                odeconfig.tdfunc=cyq_td_ode_rhs
                odeconfig.colspmv=col_spmv
                odeconfig.colexpect=col_expect
            else:
                code = compile('from '+odeconfig.tdname+' import cyq_td_ode_rhs', '<string>', 'exec')
                exec(code, globals())
                odeconfig.tdfunc=cyq_td_ode_rhs
            try:
                os.remove(odeconfig.tdname+".pyx")
            except:
                print("Error removing pyx file.  File not found.")
        elif odeconfig.tflag==0:
            odeconfig.tdfunc=cyq_ode_rhs
    else:#setup args for new parameters when rhs_reuse=True and tdfunc is given
        #string based
        if odeconfig.tflag in array([1,10,11]):
            if any(args):
                odeconfig.c_args=[]
                arg_items=args.items()
                for k in range(len(args)):
                    odeconfig.c_args.append(arg_items[k][1])
        #function based
        elif odeconfig.tflag in array([2,3,20,22]):
            odeconfig.h_func_args=args
    
    
    #load monte-carlo class
    mc=_MC_class()
    #RUN THE SIMULATION
    mc.run()
    
    
    #AFTER MCSOLVER IS DONE --------------------------------------
    
    
    
    #-------COLLECT AND RETURN OUTPUT DATA IN ODEDATA OBJECT --------------#
    output=Odedata()
    output.solver='mcsolve'
    #state vectors
    if mc.psi_out is not None and odeconfig.options.mc_avg and odeconfig.cflag:
        output.states=parfor(_mc_dm_avg,mc.psi_out.T)
    elif mc.psi_out is not None:
        output.states=mc.psi_out
    #expectation values
    elif mc.expect_out is not None and odeconfig.cflag and odeconfig.options.mc_avg:#averaging if multiple trajectories
        if isinstance(ntraj,int):
            output.expect=mean(mc.expect_out,axis=0)
        elif isinstance(ntraj,(list,ndarray)):
            output.expect=[]
            for num in ntraj:
                expt_data=mean(mc.expect_out[:num],axis=0)
                data_list=[]
                if any([op.isherm==False for op in e_ops]):
                    for k in range(len(e_ops)):
                        if e_ops[k].isherm:
                            data_list.append(real(expt_data[k]))
                        else:
                            data_list.append(expt_data[k])
                else:
                    data_list=[data for data in expt_data]
                output.expect.append(data_list)
    else:#no averaging for single trajectory or if mc_avg flag (Odeoptions) is off
        if mc.expect_out is not None:        
            output.expect=mc.expect_out

    #simulation parameters
    output.times=odeconfig.tlist
    output.num_expect=odeconfig.e_num
    output.num_collapse=odeconfig.c_num
    output.ntraj=odeconfig.ntraj
    output.col_times=mc.collapse_times_out
    output.col_which=mc.which_op_out
    return output