def _gather(sols): # gather list of Odedata objects, sols, into one. sol = Odedata() # 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 (odeconfig.e_num == 0): if (odeconfig.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 (odeconfig.options.average_expect): # collect expectation values, averaged for i in range(odeconfig.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 (odeconfig.options.average_states or odeconfig.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 (odeconfig.options.average_states or odeconfig.options.average_expect): if (odeconfig.e_num == 0): sol.states = sol.states / len(sols) else: sol.expect = list(sol.expect / len(sols)) inds=np.where(odeconfig.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 odeconfig.options.average_expect) and odeconfig.e_num!=0: temp=[list(sol.expect[ii]) for ii in range(ntraj)] for ii in range(ntraj): for jj in np.where(odeconfig.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
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
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