Exemplo n.º 1
0
    def get_result(self, ntraj=[]):
        # Store results in the Result object
        if not ntraj:
            ntraj = [self.num_traj]
        elif not isinstance(ntraj, list):
            ntraj = [ntraj]

        output = Result()
        output.solver = 'mcsolve'
        output.seeds = self.seeds

        options = self.options
        output.options = options

        if options.steady_state_average:
            output.states = self.steady_state
        elif options.average_states and options.store_states:
            output.states = self.states
        elif options.store_states:
            output.states = self.runs_states

        if options.store_final_state:
            if options.average_states:
                output.final_state = self.final_state
            else:
                output.final_state = self.runs_final_states

        if options.average_expect:
            output.expect = [self.expect_traj_avg(n) for n in ntraj]
            if len(output.expect) == 1:
                output.expect = output.expect[0]
        else:
            output.expect = self.runs_expect

        # simulation parameters
        output.times = self.tlist
        output.num_expect = self.e_ops.e_num
        output.num_collapse = len(self.ss.td_c_ops)
        output.ntraj = self.num_traj
        output.col_times = self.collapse_times
        output.col_which = self.collapse_which

        return output
Exemplo n.º 2
0
def mcsolve_f90(H, psi0, tlist, c_ops, e_ops, ntraj=None,
                options=Options(), 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 : Options
        Instance of 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 : Result
        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.")
    config.options = options
    # set num_cpus to the value given in qutip.settings
    # if none in Options
    if not config.options.num_cpus:
        config.options.num_cpus = qutip.settings.num_cpus
    # set initial value data
    if options.tidy:
        config.psi0 = psi0.tidyup(options.atol).full()
    else:
        config.psi0 = psi0.full()
    config.psi0_dims = psi0.dims
    config.psi0_shape = psi0.shape
    # set general items
    config.tlist = tlist
    if isinstance(ntraj, (list, np.ndarray)):
        raise Exception("ntraj as list argument is not supported.")
    else:
        config.ntraj = ntraj
        # ntraj_list = [ntraj]
    # set norm finding constants
    config.norm_tol = options.norm_tol
    config.norm_steps = options.norm_steps

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

    # 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 Result object
    output = Result()

    # 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 = config.tlist
    output.num_expect = config.e_num
    output.num_collapse = config.c_num
    output.ntraj = config.ntraj

    return output
Exemplo n.º 3
0
def _gather(sols):
    # gather list of Result objects, sols, into one.
    sol = Result()
    # sol = sols[0]
    ntraj = sum([a.ntraj for a in sols])
    sol.col_times = np.zeros((ntraj), dtype=np.ndarray)
    sol.col_which = np.zeros((ntraj), dtype=np.ndarray)
    sol.col_times[0:sols[0].ntraj] = sols[0].col_times
    sol.col_which[0:sols[0].ntraj] = sols[0].col_which
    sol.states = np.array(sols[0].states)
    sol.expect = np.array(sols[0].expect)
    if (hasattr(sols[0], 'entropy')):
        sol.entropy = np.array(sols[0].entropy)
    sofar = 0
    for j in range(1, len(sols)):
        sofar = sofar + sols[j - 1].ntraj
        sol.col_times[sofar:sofar + sols[j].ntraj] = (
            sols[j].col_times)
        sol.col_which[sofar:sofar + sols[j].ntraj] = (
            sols[j].col_which)
        if (config.e_num == 0):
            if (config.options.average_states):
                # collect states, averaged over trajectories
                sol.states += np.array(sols[j].states)
            else:
                # collect states, all trajectories
                sol.states = np.vstack((sol.states,
                                        np.array(sols[j].states)))
        else:
            if (config.options.average_expect):
                # collect expectation values, averaged
                for i in range(config.e_num):
                    sol.expect[i] += np.array(sols[j].expect[i])
            else:
                # collect expectation values, all trajectories
                sol.expect = np.vstack((sol.expect,
                                        np.array(sols[j].expect)))
        if (hasattr(sols[j], 'entropy')):
            if (config.options.average_states or
                    config.options.average_expect):
                # collect entropy values, averaged
                sol.entropy += np.array(sols[j].entropy)
            else:
                # collect entropy values, all trajectories
                sol.entropy = np.vstack((sol.entropy,
                                         np.array(sols[j].entropy)))
    if (config.options.average_states or config.options.average_expect):
        if (config.e_num == 0):
            sol.states = sol.states / len(sols)
        else:
            sol.expect = list(sol.expect / len(sols))
            inds = np.where(config.e_ops_isherm)[0]
            for jj in inds:
                sol.expect[jj] = np.real(sol.expect[jj])
        if (hasattr(sols[0], 'entropy')):
            sol.entropy = sol.entropy / len(sols)

    # convert sol.expect array to list and fix dtypes of arrays
    if (not config.options.average_expect) and config.e_num != 0:
        temp = [list(sol.expect[ii]) for ii in range(ntraj)]
        for ii in range(ntraj):
            for jj in np.where(config.e_ops_isherm)[0]:
                temp[ii][jj] = np.real(temp[ii][jj])
        sol.expect = temp
    # convert to list/array to be consistent with qutip mcsolve
    sol.states = list(sol.states)
    return sol
Exemplo n.º 4
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 (config.c_num != 0):
            _init_c_ops()
        if (config.e_num != 0):
            _init_e_ops()
        # set options
        qtf90.qutraj_run.n_c_ops = config.c_num
        qtf90.qutraj_run.n_e_ops = config.e_num
        qtf90.qutraj_run.ntraj = ntraj
        qtf90.qutraj_run.unravel_type = self.unravel_type
        qtf90.qutraj_run.average_states = config.options.average_states
        qtf90.qutraj_run.average_expect = config.options.average_expect
        qtf90.qutraj_run.init_result(config.psi0_shape[0],
                                     config.options.atol,
                                     config.options.rtol, mf=self.mf,
                                     norm_steps=config.norm_steps,
                                     norm_tol=config.norm_tol)
        # set optional arguments
        qtf90.qutraj_run.order = config.options.order
        qtf90.qutraj_run.nsteps = config.options.nsteps
        qtf90.qutraj_run.first_step = config.options.first_step
        qtf90.qutraj_run.min_step = config.options.min_step
        qtf90.qutraj_run.max_step = config.options.max_step
        qtf90.qutraj_run.norm_steps = config.options.norm_steps
        qtf90.qutraj_run.norm_tol = config.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 Result instance
        sol = Result()
        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 (config.e_num == 0):
            sol.states = self.get_states(len(config.tlist), ntraj)
        else:
            sol.expect = self.get_expect(len(config.tlist), ntraj)
        if (self.calc_entropy):
            sol.entropy = self.get_entropy(len(config.tlist))

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

        # deallocate stuff
        # finalize()
        return sol
Exemplo n.º 5
0
def mcsolve(H, psi0, tlist, c_ops, e_ops, ntraj=None,
            args={}, options=None, progress_bar=True,
            map_func=None, map_kwargs=None):
    """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 Options 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_ops = [C0, [C1, C1_coeff]]

        args={'a': A, 'w': W}

    or in String (Cython) format we could write::

        H = [H0, [H1, 'sin(w*t)']]

        c_ops = [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 : :class:`qutip.Qobj`
        System Hamiltonian.

    psi0 : :class:`qutip.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 : Options
        Instance of ODE solver options.

    progress_bar: BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation. Set to None to disable the
        progress bar.

    map_func: function
        A map function for managing the calls to the single-trajactory solver.

    map_kwargs: dictionary
        Optional keyword arguments to the map_func function.

    Returns
    -------
    results : :class:`qutip.solver.Result`
        Object storing all results from the simulation.

    .. note::

        It is possible to reuse the random number seeds from a previous run
        of the mcsolver by passing the output Result object seeds via the
        Options class, i.e. Options(seeds=prev_result.seeds).
    """

    if debug:
        print(inspect.stack()[0][3])

    if options is None:
        options = Options()

    if ntraj is None:
        ntraj = options.ntraj

    config.map_func = map_func if map_func is not None else parallel_map
    config.map_kwargs = map_kwargs if map_kwargs is not None else {}

    if not psi0.isket:
        raise Exception("Initial state must be a state vector.")

    if isinstance(c_ops, Qobj):
        c_ops = [c_ops]

    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    config.options = options

    if progress_bar:
        if progress_bar is True:
            config.progress_bar = TextProgressBar()
        else:
            config.progress_bar = progress_bar
    else:
        config.progress_bar = BaseProgressBar()

    # set num_cpus to the value given in qutip.settings if none in Options
    if not config.options.num_cpus:
        config.options.num_cpus = qutip.settings.num_cpus
        if config.options.num_cpus == 1:
            # fallback on serial_map if num_cpu == 1, since there is no
            # benefit of starting multiprocessing in this case
            config.map_func = serial_map

    # set initial value data
    if options.tidy:
        config.psi0 = psi0.tidyup(options.atol).full().ravel()
    else:
        config.psi0 = psi0.full().ravel()

    config.psi0_dims = psi0.dims
    config.psi0_shape = psi0.shape

    # set options on ouput states
    if config.options.steady_state_average:
        config.options.average_states = True

    # set general items
    config.tlist = tlist
    if isinstance(ntraj, (list, np.ndarray)):
        config.ntraj = np.sort(ntraj)[-1]
    else:
        config.ntraj = ntraj

    # set norm finding constants
    config.norm_tol = options.norm_tol
    config.norm_steps = options.norm_steps

    # convert array based time-dependence to string format
    H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist)

    # SETUP ODE DATA IF NONE EXISTS OR NOT REUSING
    # --------------------------------------------
    if not options.rhs_reuse or not config.tdfunc:
        # reset config collapse and time-dependence flags to default values
        config.soft_reset()

        # check for type of time-dependence (if any)
        time_type, h_stuff, c_stuff = _td_format_check(H, c_ops, 'mc')
        c_terms = len(c_stuff[0]) + len(c_stuff[1]) + len(c_stuff[2])
        # set time_type for use in multiprocessing
        config.tflag = time_type

        # check for collapse operators
        if c_terms > 0:
            config.cflag = 1
        else:
            config.cflag = 0

        # Configure data
        _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops,
                        options, config)

        # compile and load cython functions if necessary
        _mc_func_load(config)

    else:
        # setup args for new parameters when rhs_reuse=True and tdfunc is given
        # string based
        if config.tflag in [1, 10, 11]:
            if any(args):
                config.c_args = []
                arg_items = list(args.items())
                for k in range(len(arg_items)):
                    config.c_args.append(arg_items[k][1])
        # function based
        elif config.tflag in [2, 3, 20, 22]:
            config.h_func_args = args

    # load monte carlo class
    mc = _MC(config)

    # Run the simulation
    mc.run()

    # Remove RHS cython file if necessary
    if not options.rhs_reuse and config.tdname:
        _cython_build_cleanup(config.tdname)

    # AFTER MCSOLVER IS DONE
    # ----------------------

    # Store results in the Result object
    output = Result()
    output.solver = 'mcsolve'
    output.seeds = config.options.seeds
    # state vectors
    if (mc.psi_out is not None and config.options.average_states
            and config.cflag and ntraj != 1):
        output.states = parfor(_mc_dm_avg, mc.psi_out.T)
    elif mc.psi_out is not None:
        output.states = mc.psi_out

    # expectation values
    if (mc.expect_out is not None and config.cflag
            and config.options.average_expect):
        # averaging if multiple trajectories
        if isinstance(ntraj, int):
            output.expect = [np.mean(np.array([mc.expect_out[nt][op]
                                               for nt in range(ntraj)],
                                              dtype=object),
                                     axis=0)
                             for op in range(config.e_num)]
        elif isinstance(ntraj, (list, np.ndarray)):
            output.expect = []
            for num in ntraj:
                expt_data = np.mean(mc.expect_out[:num], axis=0)
                data_list = []
                if any([not op.isherm for op in e_ops]):
                    for k in range(len(e_ops)):
                        if e_ops[k].isherm:
                            data_list.append(np.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 average_expect flag
        # (Options) is off
        if mc.expect_out is not None:
            output.expect = mc.expect_out

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

    if e_ops_dict:
        output.expect = {e: output.expect[n]
                         for n, e in enumerate(e_ops_dict.keys())}

    return output
Exemplo n.º 6
0
def mcsolve(H, psi0, tlist, c_ops, e_ops, ntraj=None, args={}, options=Options()):
    """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 Options 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 : Options
        Instance of ODE solver options.

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

    """

    if debug:
        print(inspect.stack()[0][3])

    if ntraj is None:
        ntraj = options.ntraj

    if not psi0.isket:
        raise Exception("Initial state must be a state vector.")

    if isinstance(c_ops, Qobj):
        c_ops = [c_ops]

    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    config.options = options
    if isinstance(ntraj, list):
        config.progress_bar = TextProgressBar(max(ntraj))
    else:
        config.progress_bar = TextProgressBar(ntraj)

    # set num_cpus to the value given in qutip.settings if none in Options
    if not config.options.num_cpus:
        config.options.num_cpus = qutip.settings.num_cpus

    # set initial value data
    if options.tidy:
        config.psi0 = psi0.tidyup(options.atol).full().ravel()
    else:
        config.psi0 = psi0.full().ravel()

    config.psi0_dims = psi0.dims
    config.psi0_shape = psi0.shape

    # set options on ouput states
    if config.options.steady_state_average:
        config.options.average_states = True

    # set general items
    config.tlist = tlist
    if isinstance(ntraj, (list, ndarray)):
        config.ntraj = sort(ntraj)[-1]
    else:
        config.ntraj = ntraj

    # set norm finding constants
    config.norm_tol = options.norm_tol
    config.norm_steps = options.norm_steps

    # convert array based time-dependence to string format
    H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist)

    # ----------------------------------------------
    # SETUP ODE DATA IF NONE EXISTS OR NOT REUSING
    # ----------------------------------------------
    if (not options.rhs_reuse) or (not config.tdfunc):
        # reset config collapse and time-dependence flags to default values
        config.soft_reset()

        # check for type of time-dependence (if any)
        time_type, h_stuff, c_stuff = _td_format_check(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
        config.tflag = time_type

        # check for collapse operators
        if c_terms > 0:
            config.cflag = 1
        else:
            config.cflag = 0

        # Configure data
        _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops, options, config)

        # compile and load cython functions if necessary
        _mc_func_load(config)

    else:
        # setup args for new parameters when rhs_reuse=True and tdfunc is given
        # string based
        if config.tflag in array([1, 10, 11]):
            if any(args):
                config.c_args = []
                arg_items = args.items()
                for k in range(len(args)):
                    config.c_args.append(arg_items[k][1])
        # function based
        elif config.tflag in array([2, 3, 20, 22]):
            config.h_func_args = args

    # load monte-carlo class
    mc = _MC_class(config)

    # RUN THE SIMULATION
    mc.run()

    # remove RHS cython file if necessary
    if not options.rhs_reuse and config.tdname:
        try:
            os.remove(config.tdname + ".pyx")
        except:
            pass

    # AFTER MCSOLVER IS DONE --------------------------------------
    # ------- COLLECT AND RETURN OUTPUT DATA IN ODEDATA OBJECT --------------
    output = Result()
    output.solver = "mcsolve"
    # state vectors
    if mc.psi_out is not None and config.options.average_states and config.cflag and ntraj != 1:
        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 config.cflag and config.options.average_expect:
        # averaging if multiple trajectories
        if isinstance(ntraj, int):
            output.expect = [mean([mc.expect_out[nt][op] for nt in range(ntraj)], axis=0) for op in range(config.e_num)]
        elif isinstance(ntraj, (list, ndarray)):
            output.expect = []
            for num in ntraj:
                expt_data = mean(mc.expect_out[:num], axis=0)
                data_list = []
                if any([not op.isherm for op in e_ops]):
                    for k in range(len(e_ops)):
                        if e_ops[k].isherm:
                            data_list.append(np.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 average_states flag
        # (Options) is off
        if mc.expect_out is not None:
            output.expect = mc.expect_out

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

    if e_ops_dict:
        output.expect = {e: output.expect[n] for n, e in enumerate(e_ops_dict.keys())}

    return output
Exemplo n.º 7
0
def mcsolve_f90(H,
                psi0,
                tlist,
                c_ops,
                e_ops,
                ntraj=None,
                options=Options(),
                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 : Options
        Instance of 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 : Result
        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.")
    config.options = options
    # set num_cpus to the value given in qutip.settings
    # if none in Options
    if not config.options.num_cpus:
        config.options.num_cpus = qutip.settings.num_cpus
    # set initial value data
    if options.tidy:
        config.psi0 = psi0.tidyup(options.atol).full()
    else:
        config.psi0 = psi0.full()
    config.psi0_dims = psi0.dims
    config.psi0_shape = psi0.shape
    # set general items
    config.tlist = tlist
    if isinstance(ntraj, (list, np.ndarray)):
        raise Exception("ntraj as list argument is not supported.")
    else:
        config.ntraj = ntraj
        # ntraj_list = [ntraj]
    # set norm finding constants
    config.norm_tol = options.norm_tol
    config.norm_steps = options.norm_steps

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

    # 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 Result object
    output = Result()

    # 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 = config.tlist
    output.num_expect = config.e_num
    output.num_collapse = config.c_num
    output.ntraj = config.ntraj

    return output
Exemplo n.º 8
0
def _gather(sols):
    # gather list of Result objects, sols, into one.
    sol = Result()
    # sol = sols[0]
    ntraj = sum([a.ntraj for a in sols])
    sol.col_times = np.zeros((ntraj), dtype=np.ndarray)
    sol.col_which = np.zeros((ntraj), dtype=np.ndarray)
    sol.col_times[0:sols[0].ntraj] = sols[0].col_times
    sol.col_which[0:sols[0].ntraj] = sols[0].col_which
    sol.states = np.array(sols[0].states, dtype=object)
    sol.expect = np.array(sols[0].expect)
    if (hasattr(sols[0], 'entropy')):
        sol.entropy = np.array(sols[0].entropy)
    sofar = 0
    for j in range(1, len(sols)):
        sofar = sofar + sols[j - 1].ntraj
        sol.col_times[sofar:sofar + sols[j].ntraj] = (sols[j].col_times)
        sol.col_which[sofar:sofar + sols[j].ntraj] = (sols[j].col_which)
        if (config.e_num == 0):
            if (config.options.average_states):
                # collect states, averaged over trajectories
                sol.states += np.array(sols[j].states)
            else:
                # collect states, all trajectories
                sol.states = np.vstack((sol.states, np.array(sols[j].states)))
        else:
            if (config.options.average_expect):
                # collect expectation values, averaged
                for i in range(config.e_num):
                    sol.expect[i] += np.array(sols[j].expect[i])
            else:
                # collect expectation values, all trajectories
                sol.expect = np.vstack((sol.expect, np.array(sols[j].expect)))
        if (hasattr(sols[j], 'entropy')):
            if (config.options.average_states
                    or config.options.average_expect):
                # collect entropy values, averaged
                sol.entropy += np.array(sols[j].entropy)
            else:
                # collect entropy values, all trajectories
                sol.entropy = np.vstack(
                    (sol.entropy, np.array(sols[j].entropy)))
    if (config.options.average_states or config.options.average_expect):
        if (config.e_num == 0):
            sol.states = sol.states / len(sols)
        else:
            sol.expect = list(sol.expect / len(sols))
            inds = np.where(config.e_ops_isherm)[0]
            for jj in inds:
                sol.expect[jj] = np.real(sol.expect[jj])
        if (hasattr(sols[0], 'entropy')):
            sol.entropy = sol.entropy / len(sols)

    # convert sol.expect array to list and fix dtypes of arrays
    if (not config.options.average_expect) and config.e_num != 0:
        temp = [list(sol.expect[ii]) for ii in range(ntraj)]
        for ii in range(ntraj):
            for jj in np.where(config.e_ops_isherm)[0]:
                temp[ii][jj] = np.real(temp[ii][jj])
        sol.expect = temp
    # convert to list/array to be consistent with qutip mcsolve
    sol.states = list(sol.states)
    return sol
Exemplo n.º 9
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 (config.c_num != 0):
            _init_c_ops()
        if (config.e_num != 0):
            _init_e_ops()
        # set options
        qtf90.qutraj_run.n_c_ops = config.c_num
        qtf90.qutraj_run.n_e_ops = config.e_num
        qtf90.qutraj_run.ntraj = ntraj
        qtf90.qutraj_run.unravel_type = self.unravel_type
        qtf90.qutraj_run.average_states = config.options.average_states
        qtf90.qutraj_run.average_expect = config.options.average_expect
        qtf90.qutraj_run.init_result(config.psi0_shape[0],
                                     config.options.atol,
                                     config.options.rtol,
                                     mf=self.mf,
                                     norm_steps=config.norm_steps,
                                     norm_tol=config.norm_tol)
        # set optional arguments
        qtf90.qutraj_run.order = config.options.order
        qtf90.qutraj_run.nsteps = config.options.nsteps
        qtf90.qutraj_run.first_step = config.options.first_step
        qtf90.qutraj_run.min_step = config.options.min_step
        qtf90.qutraj_run.max_step = config.options.max_step
        qtf90.qutraj_run.norm_steps = config.options.norm_steps
        qtf90.qutraj_run.norm_tol = config.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 Result instance
        sol = Result()
        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 (config.e_num == 0):
            sol.states = self.get_states(len(config.tlist), ntraj)
        else:
            sol.expect = self.get_expect(len(config.tlist), ntraj)
        if (self.calc_entropy):
            sol.entropy = self.get_entropy(len(config.tlist))

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

        # deallocate stuff
        # finalize()
        return sol
Exemplo n.º 10
0
def mcsolve(H, psi0, tlist, c_ops, e_ops, ntraj=None,
            args={}, options=None, progress_bar=True,
            map_func=None, map_kwargs=None):
    """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 Options 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_ops = [C0, [C1, C1_coeff]]

        args={'a': A, 'w': W}

    or in String (Cython) format we could write::

        H = [H0, [H1, 'sin(w*t)']]

        c_ops = [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 : :class:`qutip.Qobj`
        System Hamiltonian.

    psi0 : :class:`qutip.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 : Options
        Instance of ODE solver options.

    progress_bar: BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation. Set to None to disable the
        progress bar.

    map_func: function
        A map function for managing the calls to the single-trajactory solver.

    map_kwargs: dictionary
        Optional keyword arguments to the map_func function.

    Returns
    -------
    results : :class:`qutip.solver.Result`
        Object storing all results from the simulation.

    .. note::

        It is possible to reuse the random number seeds from a previous run
        of the mcsolver by passing the output Result object seeds via the
        Options class, i.e. Options(seeds=prev_result.seeds).
    """

    if debug:
        print(inspect.stack()[0][3])

    if options is None:
        options = Options()

    if ntraj is None:
        ntraj = options.ntraj

    config.map_func = map_func if map_func is not None else parallel_map
    config.map_kwargs = map_kwargs if map_kwargs is not None else {}

    if not psi0.isket:
        raise Exception("Initial state must be a state vector.")

    if isinstance(c_ops, Qobj):
        c_ops = [c_ops]

    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    config.options = options

    if progress_bar:
        if progress_bar is True:
            config.progress_bar = TextProgressBar()
        else:
            config.progress_bar = progress_bar
    else:
        config.progress_bar = BaseProgressBar()

    # set num_cpus to the value given in qutip.settings if none in Options
    if not config.options.num_cpus:
        config.options.num_cpus = qutip.settings.num_cpus
        if config.options.num_cpus == 1:
            # fallback on serial_map if num_cpu == 1, since there is no
            # benefit of starting multiprocessing in this case
            config.map_func = serial_map

    # set initial value data
    if options.tidy:
        config.psi0 = psi0.tidyup(options.atol).full().ravel()
    else:
        config.psi0 = psi0.full().ravel()

    config.psi0_dims = psi0.dims
    config.psi0_shape = psi0.shape

    # set options on ouput states
    if config.options.steady_state_average:
        config.options.average_states = True

    # set general items
    config.tlist = tlist
    if isinstance(ntraj, (list, np.ndarray)):
        config.ntraj = np.sort(ntraj)[-1]
    else:
        config.ntraj = ntraj

    # set norm finding constants
    config.norm_tol = options.norm_tol
    config.norm_steps = options.norm_steps

    # convert array based time-dependence to string format
    H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist)

    # SETUP ODE DATA IF NONE EXISTS OR NOT REUSING
    # --------------------------------------------
    if not options.rhs_reuse or not config.tdfunc:
        # reset config collapse and time-dependence flags to default values
        config.soft_reset()

        # check for type of time-dependence (if any)
        time_type, h_stuff, c_stuff = _td_format_check(H, c_ops, 'mc')
        c_terms = len(c_stuff[0]) + len(c_stuff[1]) + len(c_stuff[2])
        # set time_type for use in multiprocessing
        config.tflag = time_type

        # check for collapse operators
        if c_terms > 0:
            config.cflag = 1
        else:
            config.cflag = 0

        # Configure data
        _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops,
                        options, config)

        # compile and load cython functions if necessary
        _mc_func_load(config)

    else:
        # setup args for new parameters when rhs_reuse=True and tdfunc is given
        # string based
        if config.tflag in [1, 10, 11]:
            if any(args):
                config.c_args = []
                arg_items = list(args.items())
                for k in range(len(arg_items)):
                    config.c_args.append(arg_items[k][1])
        # function based
        elif config.tflag in [2, 3, 20, 22]:
            config.h_func_args = args

    # load monte carlo class
    mc = _MC(config)

    # Run the simulation
    mc.run()

    # Remove RHS cython file if necessary
    if not options.rhs_reuse and config.tdname:
        _cython_build_cleanup(config.tdname)

    # AFTER MCSOLVER IS DONE
    # ----------------------

    # Store results in the Result object
    output = Result()
    output.solver = 'mcsolve'
    output.seeds = config.options.seeds
    # state vectors
    if (mc.psi_out is not None and config.options.average_states
            and config.cflag and ntraj != 1):
        output.states = parfor(_mc_dm_avg, mc.psi_out.T)
    elif mc.psi_out is not None:
        output.states = mc.psi_out

    # expectation values
    if (mc.expect_out is not None and config.cflag
            and config.options.average_expect):
        # averaging if multiple trajectories
        if isinstance(ntraj, int):
            output.expect = [np.mean(np.array([mc.expect_out[nt][op]
                                               for nt in range(ntraj)],
                                              dtype=object),
                                     axis=0)
                             for op in range(config.e_num)]
        elif isinstance(ntraj, (list, np.ndarray)):
            output.expect = []
            for num in ntraj:
                expt_data = np.mean(mc.expect_out[:num], axis=0)
                data_list = []
                if any([not op.isherm for op in e_ops]):
                    for k in range(len(e_ops)):
                        if e_ops[k].isherm:
                            data_list.append(np.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 average_expect flag
        # (Options) is off
        if mc.expect_out is not None:
            output.expect = mc.expect_out

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

    if e_ops_dict:
        output.expect = {e: output.expect[n]
                         for n, e in enumerate(e_ops_dict.keys())}

    return output