From 67df1d7b67cb209c133a295b951331b68426bcba Mon Sep 17 00:00:00 2001 From: Roland Wirth Date: Wed, 7 Nov 2018 13:46:56 -0500 Subject: [PATCH] initial --- .gitignore | 1 + imsrg/__init__.py | 270 +++++++++++++++++++++++++++++++++++ imsrg/ci.py | 97 +++++++++++++ imsrg/operator.py | 349 ++++++++++++++++++++++++++++++++++++++++++++++ pair.py | 112 +++++++++++++++ 5 files changed, 829 insertions(+) create mode 100644 .gitignore create mode 100644 imsrg/__init__.py create mode 100644 imsrg/ci.py create mode 100644 imsrg/operator.py create mode 100644 pair.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/imsrg/__init__.py b/imsrg/__init__.py new file mode 100644 index 0000000..dfe188f --- /dev/null +++ b/imsrg/__init__.py @@ -0,0 +1,270 @@ +import numpy as np + +from collections import namedtuple +from .operator import Operator +from .ci import SDBasis, SDMatrix + +# Copied and modified from https://rosettacode.org/wiki/Bernoulli_numbers +def magnus_coefficients(): + from fractions import Fraction as Fr + + A, m = [], 0 + fct = 1 + while True: + A.append(Fr(1, m+1)) + for j in range(m, 0, -1): + A[j-1] = j*(A[j-1] - A[j]) + yield (-1 if m == 1 else +1) * A[0]/fct # (which is Bm) + m += 1 + fct *= m + + +def wegner_generator(href, hole_levels=None): + from numpy import ix_ + + holes, particles, hh_classifier, ph_classifier, pp_classifier = href.ref.classifiers(hole_levels) + + hdiag0 = href.o0 + + hdiag1 = href.o1.copy() + hdiag1[ix_(holes, particles)] = 0. + hdiag1[ix_(particles, holes)] = 0. + + hh_states = tuple( i for i, (p,q) in enumerate(href.basis.tpb) if hh_classifier(p,q) ) + pp_states = tuple( i for i, (p,q) in enumerate(href.basis.tpb) if pp_classifier(p,q) ) + + hdiag2 = href.o2.copy() + hdiag2[ix_(hh_states, pp_states)] = 0. + hdiag2[ix_(pp_states, hh_states)] = 0. + + hdiag = Operator(hdiag0, hdiag1, hdiag2, href.ref) + + return hdiag.comm(href) + +def white_generator(href, hole_levels=None): + from numpy import ix_ + + holes, particles, hh_classifier, ph_classifier, pp_classifier = href.ref.classifiers(hole_levels) + + denom1 = _get_denom1(href, holes, particles) + + gen0 = 0. + + gen1 = np.zeros_like(href.o1) + gen1[ix_(particles, holes)] = href.o1[ix_(particles, holes)] / denom1 + gen1 -= gen1.T + + denom2, pp_states, hh_states = _get_denom2(href, hh_classifier, ph_classifier, pp_classifier) + + gen2 = np.zeros_like(href.o2) + gen2[ix_(pp_states, hh_states)] = href.o2[ix_(pp_states, hh_states)] / denom2 + gen2 -= gen2.T + + gen = Operator(gen0, gen1, gen2, href.ref) + + return gen + + +def mbpt2(href): + from itertools import product + + hole_levels = set( lvl for i, (lvl, ud) in enumerate(href.basis.spb) if href.ref.gam1[i] > 0. ) + + holes, particles, hh_classifier, ph_classifier, pp_classifier = href.ref.classifiers(hole_levels) + + DEpt2 = 0. + for (I, (p,r)), (J, (q,s)) in product(enumerate(href.basis.tpb), repeat=2): + if not (hh_classifier(p, r) and pp_classifier(q, s)): + continue + DEpt2 -= href.o2[I,J]**2/(href.o1[q,q] + href.o1[s,s] - href.o1[p,p] - href.o1[r,r]) + + return href.o0 + DEpt2 + + +def _flow_rhs(generator, reference, hole_levels=None): + def rhs(s, y): + href = Operator.unpack(y, reference, symmetry='hermitian') + eta = generator(href, hole_levels) + dhds = eta.comm(href) + return dhds.pack(symmetry='hermitian') + return rhs + + +def _magnus_rhs(generator, href0, reference, hole_levels=None): + def rhs(s, y, href): + omega = Operator.unpack(y, reference, symmetry='antihermitian') + href = href0.bch(omega) + eta = generator(href, hole_levels) + + domegads = eta.copy() + domegads_term = eta.copy() + + coeffs = magnus_coefficients() + next(coeffs) + + termnorm0 = domegads.norm() + termnorm = 1. + m = 1 + while termnorm > 1e-6: + domegads_term = omega.comm(domegads_term) + + coeff = next(coeffs) + if coeff != 0: + domegads += float(coeff) * domegads_term + termnorm = abs(float(coeff)) * domegads_term.norm() / termnorm0 + print('Magnus: order = {}, coeff = {}, ||domega|| = {}, ||rem||_rel = {}'.format(m, coeff, domegads.norm(), termnorm)) + m += 1 + + domegads.o0 = 0. + domegads.o1 = 1/2 * (domegads.o1 - domegads.o1.T) + domegads.o2 = 1/2 * (domegads.o2 - domegads.o2.T) + + return domegads.pack(symmetry='antihermitian') + return rhs + +def _get_denom1(href, holes, particles): + denom1 = np.diag(href.o1)[:,np.newaxis] - np.diag(href.o1)[np.newaxis, :] + + for I, (p,q) in enumerate(href.basis.tpb): + if p in holes and q in particles: + denom1[q,p] -= href.o2[I,I] + elif p in particles and q in holes: + denom1[p,q] -= href.o2[I,I] + + return denom1[np.ix_(particles, holes)] + +def _get_denom2(href, hh_classifier, ph_classifier, pp_classifier): + hh_states = list( i for i, (p,q) in enumerate(href.basis.tpb) if hh_classifier(p,q) ) + pp_states = list( i for i, (p,q) in enumerate(href.basis.tpb) if pp_classifier(p,q) ) + + denom = np.array([ href.o1[p,p] + href.o1[q,q] for p,q in href.basis.tpb ]) + + denom2 = denom[:,np.newaxis] - denom[np.newaxis,:] + np.diag(href.o2)[:,np.newaxis] + np.diag(href.o2)[np.newaxis,:] + + for I in hh_states: + h1, h2 = href.basis.tpb[I] + for J in pp_states: + p1, p2 = href.basis.tpb[J] + + K1, _ = href.basis.get_tpi(p1, h1) + K2, _ = href.basis.get_tpi(p2, h2) + K3, _ = href.basis.get_tpi(p1, h2) + K4, _ = href.basis.get_tpi(p2, h1) + + denom2[J,I] -= href.o2[K1,K1] + href.o2[K2,K2] + href.o2[K3,K3] + href.o2[K4,K4] + + return denom2[np.ix_(pp_states, hh_states)], pp_states, hh_states + + +IntegrateStats = namedtuple('IntegrateStats', 's fd E0 E2') + +def integrate_direct(href0, s_max, generator, reference, step_monitor=None, convergence_check=None): + from scipy.integrate import ode + + solver = ode(_flow_rhs(generator, reference)) + solver.set_integrator('vode') + solver.set_initial_value(href0.pack(symmetry='hermitian')) + + stats = IntegrateStats([], [], [], []) + + sdbasis = SDBasis(reference.basis, reference.nparticles) + + while solver.t < s_max: + y = solver.integrate(s_max, step=True) + href = Operator.unpack(y, reference, symmetry='hermitian') + Ept2 = mbpt2(href) + + sdmatrix = SDMatrix(sdbasis, href.normalorder(reference.basis.vacuum)) + + stats.s.append(solver.t) + stats.fd.append(sdmatrix.eigenvalues()) + stats.E0.append(href.o0) + stats.E2.append(Ept2) + + if step_monitor: + step_monitor(href, stats) + + if not solver.successful(): + break + + if convergence_check is not None and href.o0 - Ept2 < convergence_check: + break + + return solver.successful(), href, stats + + +def integrate_magnus(href0, s_max, generator, reference, step_monitor=None, convergence_check=None): + from scipy.integrate import ode + + solver = ode(_magnus_rhs(generator, href0, reference)) + solver.set_integrator('vode', atol=1e-6) + solver.set_initial_value(Operator.zero(reference).pack(symmetry='antihermitian')) + solver.set_f_params(href0) + + stats = IntegrateStats([], [], [], []) + + sdbasis = SDBasis(reference.basis, reference.nparticles) + + while solver.t < s_max: + y = solver.integrate(s_max, step=True) + omega = Operator.unpack(y, reference, symmetry='antihermitian') + href = href0.bch(omega) + solver.set_f_params(href) + Ept2 = mbpt2(href) + + sdmatrix = SDMatrix(sdbasis, href.normalorder(reference.basis.vacuum)) + + stats.s.append(solver.t) + stats.fd.append(sdmatrix.eigenvalues()) + stats.E0.append(href.o0) + stats.E2.append(Ept2) + + if step_monitor: + step_monitor(href, stats) + + if not solver.successful(): + break + + if convergence_check is not None and href.o0 - Ept2 < convergence_check: + break + + return solver.successful(), omega, href, stats + + +def integrate_magnus_dopri(href0, s_max, generator, reference, step_monitor=None, convergence_check=None): + from scipy.integrate import ode + + stats = IntegrateStats([], [], [], []) + + sdbasis = SDBasis(reference.basis, reference.nparticles) + + def solout(s, y): + nonlocal stats, sdbasis + + omega = Operator.unpack(y, reference, symmetry='antihermitian') + href = href0.bch(omega) + Ept2 = mbpt2(href) + + sdmatrix = SDMatrix(sdbasis, href.normalorder(reference.basis.vacuum)) + + stats.s.append(s) + stats.fd.append(sdmatrix.eigenvalues()) + stats.E0.append(href.o0) + stats.E2.append(Ept2) + + if step_monitor: + step_monitor(href, stats) + + if convergence_check is not None and href.o0 - Ept2 < convergence_check: + return -1 + + solver = ode(_magnus_rhs(generator, href0, reference)) + solver.set_integrator('dop853', max_step=0.1) + solver.set_solout(solout) + solver.set_initial_value(Operator.zero(reference).pack(symmetry='antihermitian')) + solver.set_f_params(href0) + + y = solver.integrate(s_max) + omega = Operator.unpack(y, reference, symmetry='antihermitian') + href = href0.bch(omega) + return solver.successful(), omega, href, stats diff --git a/imsrg/ci.py b/imsrg/ci.py new file mode 100644 index 0000000..8695a5d --- /dev/null +++ b/imsrg/ci.py @@ -0,0 +1,97 @@ + +import scipy.linalg as scila +import numpy as np + +class Configuration: + def __init__(self, cf): + self.cf = tuple(cf) + + def __len__(self): + return len(self.cf) + def __getitem__(self, key): + return self.cf[key] + def __iter__(self): + return iter(self.cf) + + def max_coincidence(self, other): + nparticles = len(self) + if nparticles != len(other): + raise ValueError('Configurations must have the same number of particles') + + common = [] + d1 = [] + d2 = [] + phase = 1 + + for i, k in enumerate(self): + for j, l in enumerate(other): + if k == l: + p = len(common) + common.append(k) + phase *= (-1)**(i-p) * (-1)**(j-p) + break + else: + d1.append(k) + phase *= (-1)**(nparticles - len(d1) - i) + + for j, l in enumerate(other): + if l not in common: + d2.append(l) + phase *= (-1)**(nparticles - len(d2) - j) + + return common, d1, d2, phase + + +class SDBasis: + def __init__(self, basis, nparticles): + from itertools import combinations + + def levels_to_comb(lvls): + comb = [] + for l in lvls: + comb += [ 2*l, 2*l+1 ] + return comb + + self.cfs = tuple( Configuration(levels_to_comb(lvls)) for lvls in combinations(range(basis.nspstates//2), nparticles//2) ) + self.basis = basis + self.nparticles = nparticles + +class SDMatrix: + def __init__(self, sdbasis, hvac): + from itertools import combinations + + dim = len(sdbasis.cfs) + mat = np.zeros((dim, dim)) + + for I, cf1 in enumerate(sdbasis.cfs): + for J, cf2 in enumerate(sdbasis.cfs): + common, d1, d2, phase = cf1.max_coincidence(cf2) + + me = 0. + + d = len(d1) + if d > 2: + continue + elif d == 2: + (i,ph_i), (j, ph_j) = sdbasis.basis.get_tpi(*d1), sdbasis.basis.get_tpi(*d2) + me += ph_i * ph_j * phase * hvac.o2[i,j] + elif d == 1: + for q in common: + (i,ph_i), (j, ph_j) = sdbasis.basis.get_tpi(q, d1[0]), sdbasis.basis.get_tpi(q, d2[0]) + me += ph_i * ph_j * phase * hvac.o2[i,j] + me += hvac.o1[d1[0],d2[0]] + else: # d == 0 + for q, qq in combinations(common, 2): + i,ph_i = sdbasis.basis.get_tpi(q, qq) + me += phase * ph_i**2 * hvac.o2[i,i] + for q in common: + me += phase * hvac.o1[q,q] + me += hvac.o0 + + if me != 0.: + mat[I,J] = me + + self.cfmat = mat + + def eigenvalues(self): + return scila.eigvalsh(self.cfmat) diff --git a/imsrg/operator.py b/imsrg/operator.py new file mode 100644 index 0000000..4331af6 --- /dev/null +++ b/imsrg/operator.py @@ -0,0 +1,349 @@ + +import numpy as np + +class Basis: + def __init__(self, nlevels): + from itertools import product, combinations + + self.nlevels = nlevels + self.spb = tuple( (lvl, ud) for lvl, ud in product(range(nlevels), 'ud') ) + self.tpb = tuple(combinations(range(len(self.spb)), r=2)) + + self.nspstates = len(self.spb) + self.ntpstates = len(self.tpb) + + self._tpb_lut = { state: (i, +1) for i, state in enumerate(self.tpb) } + self._tpb_lut.update({ (q,p): (i,-1) for i, (p,q) in enumerate(self.tpb) }) + self._tpb_lut.update({ (p,p): (0,0) for p in range(len(self.spb)) }) + + self.vacuum = Reference.vacuum(self) + + def get_tpi(self, s1, s2): + return self._tpb_lut[s1,s2] + + + +class Reference: + def __init__(self, basis, gam1, gam2=None): + self.basis = basis + self.gam1 = np.asarray(gam1) + if gam2: + self.gam2 = np.asarray(gam2) + else: + self.gam2 = self._calc_gam2(self.basis, self.gam1) + + self.nparticles = int(round(np.sum(gam1))) + + def is_vacuum(self, tol=1e-6): + return np.all(np.abs(self.gam1) < tol) + + def gam2_single(self, hole=False): + return self._calc_gam2(self.basis, self.gam1, hole) + + def classifiers(self, hole_levels=None): + if hole_levels is not None: + hole_levels_set = set(hole_levels) + else: + hole_levels_set = set( lvl for i, (lvl, ud) in enumerate(self.basis.spb) if self.gam1[i] == 1. ) + + particles = tuple( i for i, (lvl, ud) in enumerate(self.basis.spb) if lvl not in hole_levels_set ) + holes = tuple( i for i, (lvl, ud) in enumerate(self.basis.spb) if lvl in hole_levels_set ) + + check_hh = lambda p,q: p in holes and q in holes + check_ph = lambda p,q: (p in holes and q in particles) or (p in particles and q in holes) + check_pp = lambda p,q: p in particles and q in particles + + return holes, particles, check_hh, check_ph, check_pp + + @staticmethod + def vacuum(basis): + return Reference(basis, np.array([ 0. for i in range(basis.nspstates) ])) + + @staticmethod + def single(basis, fermilevel): + return Reference(basis, np.array([ 1. if lvl < fermilevel else 0. for lvl, ud in basis.spb ])) + + @staticmethod + def _calc_gam2(basis, gam1, hole=False): + if hole: + gam1 = gam1.copy() + gam1 = 1. - gam1 + + gam2 = np.zeros((basis.ntpstates, basis.ntpstates)) + for I, (s1, s2) in enumerate(basis.tpb): + for J, (ss1, ss2) in enumerate(basis.tpb): + if s1 == ss1 and s2 == ss2: + gam2[I,J] = gam1[s1]*gam1[s2] + if s1 == ss2 and s2 == ss1: + gam2[I,J] -= gam1[s1]*gam1[s2] + return gam2 + + +class Operator: + def __init__(self, o0, o1, o2, ref): + self.o0 = o0 + self.o1 = o1 + self.o2 = o2 + self.ref = ref + self.basis = ref.basis + + @staticmethod + def zero(ref): + o0 = 0. + o1 = np.zeros((ref.basis.nspstates,ref.basis.nspstates)) + o2 = np.zeros((ref.basis.ntpstates,ref.basis.ntpstates)) + + return Operator(o0, o1, o2, ref) + + def pack(self, symmetry=None): + if symmetry == 'hermitian': + packed = [(self.o0,)] + for i in range(self.o1.shape[0]): + packed.append(self.o1[i,i:]) + for i in range(self.o2.shape[0]): + packed.append(self.o2[i,i:]) + return np.concatenate(packed) + elif symmetry == 'antihermitian': + packed = [] + for i in range(self.o1.shape[0]-1): + packed.append(self.o1[i,i+1:]) + for i in range(self.o2.shape[0]-1): + packed.append(self.o2[i,i+1:]) + return np.concatenate(packed) + elif symmetry is None or symmetry == 'none': + return np.concatenate(([self.o0], self.o1.ravel(), self.o2.ravel())) + else: + raise ValueError('Unknown symmetry: {}'.format(symmetry)) + + @staticmethod + def unpack(y, ref, symmetry=None): + if symmetry == 'hermitian': + n = 0 + o0 = y[0] + n += 1 + + o1 = np.zeros((ref.basis.nspstates,ref.basis.nspstates)) + for i in range(ref.basis.nspstates): + o1[i,i:] = y[n:n+ref.basis.nspstates-i] + o1[i:,i] = y[n:n+ref.basis.nspstates-i] + n += ref.basis.nspstates-i + + o2 = np.zeros((ref.basis.ntpstates,ref.basis.ntpstates)) + for i in range(ref.basis.ntpstates): + o2[i,i:] = y[n:n+ref.basis.ntpstates-i] + o2[i:,i] = y[n:n+ref.basis.ntpstates-i] + n += ref.basis.ntpstates-i + if symmetry == 'antihermitian': + n = 0 + o0 = 0 + + o1 = np.zeros((ref.basis.nspstates,ref.basis.nspstates)) + for i in range(ref.basis.nspstates-1): + o1[i,i+1:] = y[n:n+ref.basis.nspstates-i-1] + o1[i+1:,i] = -y[n:n+ref.basis.nspstates-i-1] + n += ref.basis.nspstates-i-1 + + o2 = np.zeros((ref.basis.ntpstates,ref.basis.ntpstates)) + for i in range(ref.basis.ntpstates-1): + o2[i,i+1:] = y[n:n+ref.basis.ntpstates-i-1] + o2[i+1:,i] = -y[n:n+ref.basis.ntpstates-i-1] + n += ref.basis.ntpstates-i-1 + elif symmetry is None or symmetry == 'none': + i = 0 + + o0 = y[0] + i += 1 + + o1 = y[i:i+ref.basis.nspstates**2].reshape(ref.basis.nspstates,ref.basis.nspstates) + i += ref.basis.nspstates**2 + + o2 = y[i:].reshape(ref.basis.ntpstates, ref.basis.ntpstates) + + return Operator(o0, o1, o2, ref) + + def normalorder(self, newref): + from itertools import product + dim2 = self.basis.ntpstates + + if self.ref.is_vacuum(): + v0, v1 = self.o0, self.o1 + else: + v0 = self.o0 - np.einsum('ii,i', self.o1, self.ref.gam1) - np.einsum('ij,ij', self.o2, self.ref.gam2 - 2 * self.ref.gam2_single()) + v1 = self.o1.copy() + for (I, (p,r)), (J, (q,s)) in product(enumerate(self.basis.tpb), repeat=2): + if r == s: + v1[p,q] -= self.o2[I,J] * self.ref.gam1[r] + if r == q: + v1[p,s] += self.o2[I,J] * self.ref.gam1[r] + if p == s: + v1[r,q] += self.o2[I,J] * self.ref.gam1[p] + if p == q: + v1[r,s] -= self.o2[I,J] * self.ref.gam1[p] + + if newref.is_vacuum(): + return Operator(v0, v1, self.o2, newref) + else: + o0 = v0 + np.einsum('ii,i', v1, newref.gam1) + np.einsum('ij,ij', self.o2, newref.gam2) + o1 = v1.copy() + for (I, (p,r)), (J, (q,s)) in product(enumerate(self.basis.tpb), repeat=2): + if r == s: + o1[p,q] += self.o2[I,J] * newref.gam1[r] + if r == q: + o1[p,s] -= self.o2[I,J] * newref.gam1[r] + if p == s: + o1[r,q] -= self.o2[I,J] * newref.gam1[p] + if p == q: + o1[r,s] += self.o2[I,J] * newref.gam1[p] + return Operator(o0, o1, self.o2, newref) + + def comm(self, other): + from itertools import product + + if self.ref is not other.ref: + raise ValueError('Reference states must be identical') + + gam1 = self.ref.gam1 + gam2s = self.ref.gam2_single() + gambar2s = self.ref.gam2_single(hole=True) + # lam2 = self.ref.gam2 - gam2s + + one_minus_nn = np.array([ 1 - gam1[t] - gam1[v] for t, v in self.basis.tpb ]) + + o1o1_comm = np.dot(self.o1, other.o1) - np.dot(other.o1, self.o1) + + c2 = np.zeros((self.basis.ntpstates,self.basis.ntpstates)) + def _calc_c2(): + nonlocal c2 + + # Embed 1B part into two-body space + o12 = np.zeros((self.basis.ntpstates,self.basis.ntpstates)) + oo12 = np.zeros((self.basis.ntpstates,self.basis.ntpstates)) + for (I, (p,r)), (J, (q,s)) in product(enumerate(self.basis.tpb), repeat=2): + if r == s: + o12[I,J] += self.o1[p,q] + oo12[I,J] += other.o1[p,q] + if p == q: + o12[I,J] += self.o1[r,s] + oo12[I,J] += other.o1[r,s] + if p == s: + o12[I,J] -= self.o1[r,q] + oo12[I,J] -= other.o1[r,q] + if r == q: + o12[I,J] -= self.o1[p,s] + oo12[I,J] -= other.o1[p,s] + + c2 += np.dot(self.o2, oo12) - np.dot(oo12, self.o2) + c2 += np.dot(o12, other.o2) - np.dot(other.o2, o12) + + o2_ph = np.zeros((self.basis.nspstates,)*4) + oo2_ph = np.zeros((self.basis.nspstates,)*4) + for (I, (p,r)), (J, (q,s)) in product(enumerate(self.basis.tpb), repeat=2): + o2_ph[p,s,q,r] = -self.o2[I,J] + o2_ph[r,s,q,p] = self.o2[I,J] + o2_ph[p,q,s,r] = self.o2[I,J] + o2_ph[r,q,s,p] = -self.o2[I,J] + + oo2_ph[p,s,q,r] = -other.o2[I,J] + oo2_ph[r,s,q,p] = other.o2[I,J] + oo2_ph[p,q,s,r] = other.o2[I,J] + oo2_ph[r,q,s,p] = -other.o2[I,J] + + o2_ph.shape = (self.basis.nspstates**2, self.basis.nspstates**2) + oo2_ph.shape = (self.basis.nspstates**2, self.basis.nspstates**2) + + nt_nv = gam1[:,np.newaxis] - gam1[np.newaxis,:] + nt_nv.shape = (self.basis.nspstates**2,) + + comm_ph = np.einsum('ij,j,jk', o2_ph, nt_nv, oo2_ph) - np.einsum('ij,j,jk', oo2_ph, nt_nv, o2_ph) + comm_ph.shape = (self.basis.nspstates,)*4 + + for (I, (p,r)), (J, (q,s)) in product(enumerate(self.basis.tpb), repeat=2): + c2[I,J] += comm_ph[p,s,q,r] - comm_ph[p,q,s,r] + + c2 += np.einsum('ij,j,jk', self.o2, one_minus_nn, other.o2) - np.einsum('ij,j,jk', other.o2, one_minus_nn, self.o2) + _calc_c2() + + c1_intermediate_gam = np.einsum('ij,jk,kl', self.o2, gam2s, other.o2) - np.einsum('ij,jk,kl', other.o2, gam2s, self.o2) + c1_intermediate_gambar = np.einsum('ij,jk,kl', self.o2, gambar2s, other.o2) - np.einsum('ij,jk,kl', other.o2, gambar2s, self.o2) + + # c1 multi-ref terms not implemented. + c1 = o1o1_comm.copy() + def _calc_c1(): + nonlocal c1 + for (I, (p,r)), (J, (q,t)) in product(enumerate(self.basis.tpb), repeat=2): + c1[p,q] += (self.o2[I,J]*other.o1[r,t] - other.o2[I,J]*self.o1[r,t]) * (gam1[r] - gam1[t]) + c1[r,q] -= (self.o2[I,J]*other.o1[p,t] - other.o2[I,J]*self.o1[p,t]) * (gam1[p] - gam1[t]) + c1[p,t] -= (self.o2[I,J]*other.o1[r,q] - other.o2[I,J]*self.o1[r,q]) * (gam1[r] - gam1[q]) + c1[r,t] += (self.o2[I,J]*other.o1[p,q] - other.o2[I,J]*self.o1[p,q]) * (gam1[p] - gam1[q]) + + if r == t: + c1[p,q] += c1_intermediate_gam[I,J] * (1 - gam1[r]) + c1_intermediate_gambar[I,J] * gam1[r] + if r == q: + c1[p,t] -= c1_intermediate_gam[I,J] * (1 - gam1[r]) + c1_intermediate_gambar[I,J] * gam1[r] + if p == t: + c1[r,q] -= c1_intermediate_gam[I,J] * (1 - gam1[p]) + c1_intermediate_gambar[I,J] * gam1[p] + if p == q: + c1[r,t] += c1_intermediate_gam[I,J] * (1 - gam1[p]) + c1_intermediate_gambar[I,J] * gam1[p] + _calc_c1() + + # c0 multi-ref terms not implemented but simple: + np.einsum('ij,ji', c2, lam2) + c0 = np.einsum('ii,i', o1o1_comm, gam1) + np.einsum('ij,ji', c1_intermediate_gambar, gam2s) + + return Operator(c0, c1, c2, self.ref) + + def bch(self, omega, rtol=1e-8): + res = self.copy() + term = self.copy() + fct = 1 + m = 1 + + termnorm0 = res.norm() + termnorm = 1. + + while termnorm > rtol: + term = omega.comm(term) + termnorm = (term.norm() / fct) / termnorm0 + res += term / fct + + m += 1 + fct *= m + + return res + + def copy(self): + return Operator(self.o0, self.o1.copy(), self.o2.copy(), self.ref) + + def norm(self): + return abs(self.o0) + np.linalg.norm(self.o1) + np.linalg.norm(self.o2) + + def __iadd__(self, other): + self.o0 += other.o0 + self.o1 += other.o1 + self.o2 += other.o2 + return self + def __isub__(self, other): + self.o0 -= other.o0 + self.o1 -= other.o1 + self.o2 -= other.o2 + return self + def __imul__(self, alpha): + self.o0 *= alpha + self.o1 *= alpha + self.o2 *= alpha + return self + def __itruediv__(self, alpha): + self.o0 /= alpha + self.o1 /= alpha + self.o2 /= alpha + return self + + def __add__(self, other): + return Operator(self.o0, self.o1, self.o2, self.ref).__iadd__(other) + def __sub__(self, other): + return Operator(self.o0, self.o1, self.o2, self.ref).__isub__(other) + def __mul__(self, alpha): + return Operator(self.o0, self.o1, self.o2, self.ref).__imul__(alpha) + def __rmul__(self, alpha): + return self * alpha + def __truediv__(self, alpha): + return Operator(self.o0, self.o1, self.o2, self.ref).__itruediv__(alpha) diff --git a/pair.py b/pair.py new file mode 100644 index 0000000..eb52532 --- /dev/null +++ b/pair.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +import numpy as np + +from imsrg.operator import Basis, Reference, Operator +from imsrg import wegner_generator, white_generator, integrate_direct, integrate_magnus, integrate_magnus_dopri +from imsrg.ci import SDBasis, SDMatrix + +def pairing_hamiltonian(basis, g, *, energies=None, delta_e=None): + from itertools import product + + if energies is None and delta_e is None: + raise ValueError('Either energies or delta_e must be given.') + if energies is not None and delta_e is not None: + raise ValueError('Only one of energies and delta_e can be given.') + + if energies is not None and len(energies != basis.nlevels): + raise ValueError('The energy array must have length = basis.nlevels') + + if delta_e is not None: + energies = [ i*delta_e for i in range(basis.nlevels) ] + + h0 = 0. + + h1 = np.zeros((basis.nspstates, basis.nspstates)) + for i, (lvl, ud) in enumerate(basis.spb): + h1[i,i] = energies[lvl] + + h2 = np.zeros((basis.ntpstates, basis.ntpstates)) + for (I, (p,r)), (J, (q,s)) in product(enumerate(basis.tpb), repeat=2): + plvl, pud = basis.spb[p] + rlvl, rud = basis.spb[r] + qlvl, qud = basis.spb[q] + slvl, sud = basis.spb[s] + + if plvl == rlvl and pud != rud and qlvl == slvl and qud != sud: + h2[I,J] = -g/2 + + return Operator(h0, h1, h2, basis.vacuum) + + +basis = Basis(4) +fermilevel = 1 +sMax = 15 + +ref = Reference.single(basis, fermilevel) + +hvac = pairing_hamiltonian(basis, g=0.5, delta_e=1) +href = hvac.normalorder(ref) + +hh_classifier, ph_classifier, pp_classifier = ref.classifiers()[2:5] +perm = sorted(list(range(basis.ntpstates)), key=lambda i: 0 if hh_classifier(*basis.tpb[i]) else (1 if ph_classifier(*basis.tpb[i]) else 2)) + +first_ph = next(filter(lambda i: ph_classifier(*basis.tpb[perm[i]]), range(basis.ntpstates)), None) +first_pp = next(filter(lambda i: pp_classifier(*basis.tpb[perm[i]]), range(basis.ntpstates)), None) + +import matplotlib.pyplot as plt + +#plt.matshow(np.abs(href.o2[np.ix_(perm, perm)])) +#plt.axhline(first_ph-0.5) +#plt.axvline(first_ph-0.5) +#plt.axhline(first_pp-0.5) +#plt.axvline(first_pp-0.5) +#plt.colorbar() +#plt.show() + +sdbasis = SDBasis(basis, 2*fermilevel) +sdmat = SDMatrix(sdbasis, hvac) + +print(len(sdbasis.cfs)) +print(sdmat.cfmat) +print(sdmat.eigenvalues()) + +#y = href.pack(symmetry='hermitian') +#print(y) +#htest = Operator.unpack(y, ref, symmetry='hermitian') +#print(htest.o0) +#print(htest.o1) +#print(htest.o2) + +#plt.matshow(np.abs(htest.o2[np.ix_(perm, perm)])) +#plt.axhline(first_ph-0.5) +#plt.axvline(first_ph-0.5) +#plt.axhline(first_pp-0.5) +#plt.axvline(first_pp-0.5) +#plt.colorbar() +#plt.show() + +def step_monitor(href, stats): + print('s = {:.6f}, e = {:.6f}, E(2) = {:.6f}'.format(stats.s[-1], stats.E0[-1], stats.E2[-1])) + +#success, omega, href, stats = integrate_magnus(href, sMax, white_generator, ref, step_monitor) +success, href, stats = integrate_direct(href, sMax, white_generator, ref, step_monitor) + +sdmat = SDMatrix(sdbasis, href.normalorder(basis.vacuum)) +print(sdmat.cfmat) + + +plt.matshow(np.abs(href.o2[np.ix_(perm, perm)])) +plt.axhline(first_ph-0.5) +plt.axvline(first_ph-0.5) +plt.axhline(first_pp-0.5) +plt.axvline(first_pp-0.5) +plt.colorbar() + +plt.figure() +plt.plot(stats.s, stats.E0) +plt.plot(stats.s, stats.E2) + +plt.figure() +plt.plot(stats.s, stats.fd) +plt.show()