def __init__(self, comm, system, beta, dt, options={}, H1=None, verbose=False): OneBody.__init__(self, comm, system, beta, dt, options, H1=H1, verbose=verbose) self.alpha = options.get('alpha', 0.75) self.max_scf_it = options.get('max_scf_it', self.max_it) self.max_macro_it = options.get('max_macro_it', self.max_it) self.find_mu = options.get('find_mu', True) if comm.rank == 0: P, HMF, mu = self.thermal_hartree_fock(system, beta) muN = mu * numpy.eye(system.nbasis, dtype=self.G.dtype) dmat = numpy.array([ scipy.linalg.expm(-dt * (HMF[0] - muN)), scipy.linalg.expm(-dt * (HMF[1] - muN)) ]) dmat_inv = numpy.array([ scipy.linalg.inv(self.dmat[0], check_finite=False), scipy.linalg.inv(self.dmat[1], check_finite=False) ]) G = numpy.array( [greens_function(self.dmat[0]), greens_function(self.dmat[1])]) data = { 'P': P, 'mu': mu, 'dmat': dmat, 'dmat_inv': dmat_inv, 'G': G } else: data = None data = comm.bcast(data, root=0) self.P = data['P'] self.nav = particle_number(self.P).real self.dmat = data['dmat'] self.dmat_inv = data['dmat_inv'] self.G = data['G'] self.mu = data['mu']
def scf(self, system, beta, mu, P): # 1. Compute HMF HMF = fock_matrix(system, P) dt = self.dtau muN = mu * numpy.eye(system.nbasis, dtype=self.G.dtype) rho = numpy.array([ scipy.linalg.expm(-dt * (HMF[0] - muN)), scipy.linalg.expm(-dt * (HMF[1] - muN)) ]) Pold = one_rdm_stable(rho, self.num_bins) if self.verbose: print(" # Running Thermal SCF.") for it in range(self.max_scf_it): HMF = fock_matrix(system, Pold) rho = numpy.array([ scipy.linalg.expm(-dt * (HMF[0] - muN)), scipy.linalg.expm(-dt * (HMF[1] - muN)) ]) Pnew = (1 - self.alpha) * one_rdm_stable( rho, self.num_bins) + self.alpha * Pold change = numpy.linalg.norm(Pnew - Pold) if change < self.deps: break if self.verbose: N = particle_number(P).real E = local_energy(system, P, opt=False)[0].real S = entropy(beta, mu, HMF) omega = E - mu * N - 1.0 / beta * S print( " # Iteration: {:4d} dP: {:13.8e} Omega: {:13.8e}".format( it, change, omega.real)) Pold = Pnew.copy() if self.verbose: N = particle_number(P).real print(" # Average particle number: {:13.8e}".format(N)) return HMF
def __init__(self, comm, system, beta, dt, options={}, nav=None, H1=None, verbose=False): self.name = 'thermal' self.verbose = verbose if H1 is None: try: self.H1 = system.H1 except AttributeError: self.H1 = system.h1e else: self.H1 = H1 if verbose: print("# beta in OneBody: {}".format(beta)) print("# dt in OneBody: {}".format(dt)) dmat_up = scipy.linalg.expm(-dt * (self.H1[0])) dmat_down = scipy.linalg.expm(-dt * (self.H1[1])) self.dmat = numpy.array([dmat_up, dmat_down]) cond = numpy.linalg.cond(self.dmat[0]) if verbose: print("# condition number of BT: {: 10e}".format(cond)) if nav is not None: self.nav = nav else: self.nav = options.get("nav", None) if self.nav is None: self.nav = system.nup + system.ndown if verbose: print("# Target average electron number: {}".format(self.nav)) self.max_it = options.get('max_it', 1000) self.deps = options.get('threshold', 1e-6) self.mu = options.get('mu', None) self.num_slices = int(beta / dt) self.stack_size = options.get("stack_size", None) if (self.stack_size == None): if verbose: print("# Estimating stack size from BT.") eigs, ev = scipy.linalg.eigh(self.dmat[0]) emax = numpy.max(eigs) emin = numpy.min(eigs) self.cond = numpy.linalg.cond(self.dmat[0]) # We will end up multiplying many BTs together. Can roughly determine # safe stack size from condition number of BT as the condition number of # the product will scale roughly as cond(BT)^(number of products). # We can determine a conservative stack size by requiring that the # condition number of the product does not exceed 1e3. self.stack_size = min(self.num_slices, int(3.0 / numpy.log10(self.cond))) if verbose: print("# Initial stack size, # of slices: {}, {}".format( self.stack_size, self.num_slices)) # adjust stack size self.stack_size = update_stack(self.stack_size, self.num_slices, verbose=verbose) self.num_bins = int(beta / (self.stack_size * dt)) if verbose: print("# Number of stacks: {}".format(self.num_bins)) sign = 1 if system._alt_convention: if verbose: print( "# Using alternate sign convention for chemical potential." ) sign = -1 dtau = self.stack_size * dt self.dtau = dtau if self.mu is None: self.rho = numpy.array([ scipy.linalg.expm(-dtau * (self.H1[0])), scipy.linalg.expm(-dtau * (self.H1[1])) ]) if comm.rank == 0: mu = find_chemical_potential(system, self.rho, dtau, self.num_bins, self.nav, deps=self.deps, max_it=self.max_it, verbose=verbose) else: mu = None self.mu = comm.bcast(mu, root=0) else: self.rho = numpy.array([ scipy.linalg.expm(-dtau * (self.H1[0])), scipy.linalg.expm(-dtau * (self.H1[1])) ]) if verbose: print("# Chemical potential in trial density matrix: {: .10e}". format(self.mu)) if system.mu is None: system.mu = self.mu self.P = one_rdm_stable( compute_rho(self.rho, self.mu, dtau, sign=sign), self.num_bins) self.nav = particle_number(self.P).real if verbose: print("# Average particle number in trial density matrix: " "{}".format(self.nav)) self.dmat = compute_rho(self.dmat, self.mu, dt, sign=sign) self.dmat_inv = numpy.array([ scipy.linalg.inv(self.dmat[0], check_finite=False), scipy.linalg.inv(self.dmat[1], check_finite=False) ]) self.G = numpy.array( [greens_function(self.dmat[0]), greens_function(self.dmat[1])]) self.error = False
def update(self, system, qmc, trial, psi, step, free_projection=False): """Update mixed estimates for walkers. Parameters ---------- system : system object. Container for model input options. qmc : :class:`pauxy.state.QMCOpts` object. Container for qmc input options. trial : :class:`pauxy.trial_wavefunction.X' object Trial wavefunction class. psi : :class:`pauxy.walkers.Walkers` object CPMC wavefunction. step : int Current simulation step free_projection : bool True if doing free projection. """ if free_projection: for i, w in enumerate(psi.walkers): # For T > 0 w.ot = 1 always. wfac = w.weight * w.ot * w.phase if step % self.energy_eval_freq == 0: w.greens_function(trial) if self.eval_energy: E, T, V = w.local_energy(system) else: E, T, V = 0, 0, 0 self.estimates[self.names.enumer] += wfac * E self.estimates[self.names.e1b:self.names.e2b + 1] += (wfac * numpy.array([T, V])) self.estimates[self.names.edenom] += wfac if self.thermal: nav = particle_number(one_rdm_from_G(w.G)) self.estimates[self.names.nav] += wfac * nav self.estimates[self.names.uweight] += w.unscaled_weight self.estimates[self.names.uweight] += w.weight self.estimates[self.names.ehyb] += wfac * w.hybrid_energy self.estimates[self.names.ovlp] += wfac * abs(w.ot) else: # When using importance sampling we only need to know the current # walkers weight as well as the local energy, the walker's overlap # with the trial wavefunction is not needed. for i, w in enumerate(psi.walkers): if self.thermal: if self.average_gf: E_sum = 0 T_sum = 0 V_sum = 0 nav = 0 for ts in range(w.stack_length): w.greens_function(trial, slice_ix=ts * w.stack_size) E, T, V = w.local_energy(system, two_rdm=self.two_rdm) E_sum += E T_sum += T V_sum += V nav += particle_number(one_rdm_from_G(w.G)) self.estimates[ self.names.nav] += w.weight * nav / w.stack_length self.estimates[ self.names. enumer] += w.weight * E_sum.real / w.stack_length self.estimates[self.names.e1b:self.names.e2b + 1] += ( w.weight * numpy.array([T_sum, V_sum]).real / w.stack_length) else: w.greens_function(trial) E, T, V = w.local_energy(system, two_rdm=self.two_rdm) nav = particle_number(one_rdm_from_G(w.G)) self.estimates[self.names.nav] += w.weight * nav self.estimates[self.names.enumer] += w.weight * E.real self.estimates[self.names.e1b:self.names.e2b + 1] += (w.weight * numpy.array([T, V]).real) self.estimates[self.names.edenom] += w.weight else: if step % self.energy_eval_freq == 0: w.greens_function(trial) if self.eval_energy: E, T, V = w.local_energy(system) else: E, T, V = 0, 0, 0 self.estimates[self.names.enumer] += w.weight * E.real self.estimates[self.names.e1b:self.names.e2b + 1] += (w.weight * numpy.array([T, V]).real) self.estimates[self.names.edenom] += w.weight self.estimates[self.names.uweight] += w.unscaled_weight self.estimates[self.names.weight] += w.weight self.estimates[self.names.ovlp] += w.weight * abs(w.ot) self.estimates[self.names.ehyb] += w.weight * w.hybrid_energy if self.calc_one_rdm: start = self.names.time + 1 end = self.names.time + 1 + w.G.size self.estimates[start:end] += w.weight * w.G.flatten().real if self.calc_two_rdm is not None: start = end end = end + self.two_rdm.size self.estimates[ start:end] += w.weight * self.two_rdm.flatten().real
def __init__(self, walker_opts, system, trial, verbose=False): self.weight = walker_opts.get('weight', 1.0) self.unscaled_weight = self.weight self.phase = 1.0 + 0.0j self.alive = True self.num_slices = trial.num_slices dtype = numpy.complex128 self.G = numpy.zeros(trial.dmat.shape, dtype=dtype) self.nbasis = trial.dmat[0].shape[0] self.total_weight = 0 self.stack_size = walker_opts.get('stack_size', None) max_diff_diag = numpy.linalg.norm( (numpy.diag(trial.dmat[0].diagonal()) - trial.dmat[0])) if max_diff_diag < 1e-10: self.diagonal_trial = True if verbose: print("# Trial density matrix is diagonal.") else: self.diagonal_trial = False if verbose: print("# Trial density matrix is not diagonal.") if self.stack_size == None: self.stack_size = trial.stack_size if (self.num_slices // self.stack_size) * self.stack_size != self.num_slices: if verbose: print("# Input stack size does not divide number of slices.") self.stack_size = update_stack(self.stack_size, self.num_slices, verbose) if self.stack_size > trial.stack_size: if verbose: print("# Walker stack size differs from that estimated from " "trial density matrix.") print("# Be careful. cond(BT)**stack_size: %10.3e." % (trial.cond**self.stack_size)) self.stack_length = self.num_slices // self.stack_size if verbose: print("# Walker stack size: {}".format(self.stack_size)) self.lowrank = walker_opts.get('low_rank', False) self.lowrank_thresh = walker_opts.get('low_rank_thresh', 1e-6) if verbose: print("# Using low rank trick: {}".format(self.lowrank)) self.stack = PropagatorStack(self.stack_size, trial.num_slices, trial.dmat.shape[-1], dtype, trial.dmat, trial.dmat_inv, diagonal=self.diagonal_trial, lowrank=self.lowrank, thresh=self.lowrank_thresh) # Initialise all propagators to the trial density matrix. self.stack.set_all(trial.dmat) self.greens_function_qr_strat(trial) self.stack.G = self.G self.M0 = numpy.array([ scipy.linalg.det(self.G[0], check_finite=False), scipy.linalg.det(self.G[1], check_finite=False) ]) self.stack.ovlp = numpy.array([1.0 / self.M0[0], 1.0 / self.M0[1]]) self.ot = 1.0 # # temporary storage for stacks... I = numpy.identity(system.nbasis, dtype=dtype) One = numpy.ones(system.nbasis, dtype=dtype) self.Tl = numpy.array([I, I]) self.Ql = numpy.array([I, I]) self.Dl = numpy.array([One, One]) self.Tr = numpy.array([I, I]) self.Qr = numpy.array([I, I]) self.Dr = numpy.array([One, One]) self.hybrid_energy = 0.0 if verbose: eloc = self.local_energy(system) P = one_rdm_from_G(self.G) nav = particle_number(P) print("# Initial walker energy: {} {} {}".format(*eloc)) print("# Initial walker electron number: {}".format(nav)) # self.buff_names = ['weight', 'G', 'unscaled_weight', 'phase', 'Tl', # 'Ql', 'Dl', 'Tr', 'Qr', 'Dr', 'M0'] self.buff_names, self.buff_size = get_numeric_names(self.__dict__)
"sparse": False, "integrals": "hamil.h5" } system = Generic(inputs=sys_opts) # trial = OneBody(comm, system, 1.0, 0.05, verbose=True) mus = numpy.arange(-1, 1) data = [] dt = 0.05 fci = pd.read_csv('be_fixed_n.out', sep=r'\s+') for b, n in zip(fci.beta, fci.N): trial = OneBody(comm, system, b, dt, options={"nav": n}, verbose=True) data.append([ local_energy(system, trial.P, opt=False)[0].real, particle_number(trial.P).real ]) pl.plot(fci.beta, zip(*data)[0], label='Match N') match = zip(*data)[0] data = [] for b, n in zip(fci.beta, fci.N): trial = MeanField(comm, system, b, dt, options={"nav": n}, verbose=True) data.append([ local_energy(system, trial.P, opt=False)[0].real, particle_number(trial.P).real ]) pl.plot(fci.beta, fci.E, label='FCI') pl.plot(fci.beta, zip(*data)[0], label='THF', linestyle=':') data = pd.DataFrame({ 'beta': fci.beta, 'FCI': fci.E,
def delta_nav(dm, nav): return particle_number(dm) - nav
def find_chemical_potential(system, rho, beta, num_bins, target, deps=1e-6, max_it=1000, verbose=False): # Todo: some sort of generic starting point independent of # system/temperature dmu1 = dmu2 = 1 mu1 = -1 mu2 = 1 sign = -1 if system._alt_convention else 1 if verbose: print("# Finding chemical potential to match <N> = {:13.8e}".format( target)) while numpy.sign(dmu1) * numpy.sign(dmu2) > 0: rho1 = compute_rho(rho, mu1, beta, sign=sign) dmat = one_rdm_stable(rho1, num_bins) dmu1 = delta_nav(dmat, target) rho2 = compute_rho(rho, mu2, beta, sign=sign) dmat = one_rdm_stable(rho2, num_bins) dmu2 = delta_nav(dmat, target) if numpy.sign(dmu1) * numpy.sign(dmu2) < 0: if verbose: print("# Chemical potential lies within range of [%f,%f]" % (mu1, mu2)) print("# delta_mu1 = %f, delta_mu2 = %f" % (dmu1.real, dmu2.real)) break else: mu1 -= 2 mu2 += 2 if verbose: print("# Increasing chemical potential search to [%f,%f]" % (mu1, mu2)) found_mu = False if verbose: print("# " + format_fixed_width_strings(['iteration', 'mu', 'Dmu', '<N>'])) for i in range(0, max_it): mu = 0.5 * (mu1 + mu2) rho_mu = compute_rho(rho, mu, beta, sign=sign) dmat = one_rdm_stable(rho_mu, num_bins) dmu = delta_nav(dmat, target).real if verbose: out = [i, mu, dmu, particle_number(dmat).real] print("# " + format_fixed_width_floats(out)) if abs(dmu) < deps: found_mu = True break else: if dmu * dmu1 > 0: mu1 = mu elif dmu * dmu2 > 0: mu2 = mu if found_mu: return mu else: print("# Error chemical potential not found") return None