def _test_op_construction(self, array, op): a = op(array) v = isle.Vector(op(array)) self.assertTrue( core.isEqual(a, v), msg="Failed check for scalar type: {typ} and operation: {op}". format(typ=array.dtype, op=op))
def hmc(lat, params, file, log): LATTICE = lat.name nt = lat.nt() discretization = DISC(params.hopping) rng = isle.random.NumpyRNG(1075) hmcState = isle.drivers.hmc.newRun(lat, params, rng, makeAction, file, False) # Generate a random initial condition. phi = isle.Vector( rng.normal(0, params.tilde("U", lat)**(1 / 2), lat.lattSize()) + 0j) log.info(f"Thermalizing {LATTICE} nt={nt} {discretization}") evolver = isle.evolver.LinearStepLeapfrog(hmcState.action, (1, 1), (20, 5), 99, rng) evStage = hmcState(phi, evolver, 1000, saveFreq=0, checkpointFreq=0) hmcState.resetIndex() log.info(f"Producing {LATTICE} nt={nt} {discretization}") evolver = isle.evolver.ConstStepLeapfrog(hmcState.action, 1, 5, rng) hmcState(evStage, evolver, 100000, saveFreq=10, checkpointFreq=100)
def main(): args = _init() # Load the spatial lattice. lat = isle.fileio.yaml.loadLattice(LATTICE) # Store nt. lat.nt(NT) # Set up a random number generator. rng = isle.random.NumpyRNG(1337) # Set up a fresh HMC driver. hmcState = isle.drivers.hmc.newRun(lat, PARAMS, rng, makeAction, args.outfile, args.overwrite) # Generate a random initial condition. # Note that configurations must be vectors of complex numbers. phi = isle.Vector( rng.normal(0, PARAMS.tilde("U", lat)**(1 / 2), lat.lattSize()) + 0j) # Run thermalization. getLogger(__name__).info("Thermalizing") # Pick an evolver which linearly decreases the number of MD steps from 20 to 5. evolver = isle.evolver.LinearStepLeapfrog(hmcState.action, (1, 1), (20, 5), args.ntrajectories - 1, rng) # Thermalize configuration using the command line arguments. hmcState(phi, evolver, args.ntrajectories, saveFreq=args.save_freq, checkpointFreq=args.checkpoint_freq)
def __call__(self, phi, action, itr): """!Record the single-particle correlators.""" nt = int(len(phi) / self.hfm.nx()) # Create a large set of sources: rhss = [isle.Vector(spaceToSpacetime(irrep, time, nt)) for irrep in self.irreps for time in range(nt)] # For the j^th spacetime vector of the i^th state, go to self.rhss[i * nt + j] # In other words, time is the faster running index. # Solve M*x = b for all right-hand sides: if self._alpha == 1: res = np.array(isle.solveM(self.hfm, phi, self.species, rhss), copy=False) else: res = np.linalg.solve(isle.Matrix(self.hfm.M(-1j*phi, self.species)), np.array(rhss).T).T propagators = res.reshape([len(self.irreps), nt, nt, len(self.irreps)]) # The logic for the one-liner goes as: # For each source irrep we need to apply a sink for every irrep. # This produces a big cross-correlator. # For each source time we need to roll the vector such that the # source lives on timeslice 0. # Finally, we need to average over all the source correlators with # the same source irrep but different timeslices. self.corr.append(np.mean( np.array([ [ [rollTemporally(np.dot(propagators[i, src], np.conj(irrepj)), -src) for src in range(nt)] for i in range(len(self.irreps))] for irrepj in self.irreps]), axis=2))
def tune(rng): """ Thermalize and then tune a leapfrog integrator. """ # Get a logger. Use this instead of print() to output any and all information. log = getLogger("HMC") # Load the spatial lattice. lat = isle.LATTICES[LATTICE] # Lattice files usually only contain information on the spatial lattice # to be more flexible. Set the number of time slices here. lat.nt(NT) # Set up a fresh HMC driver. # It handles all HMC evolution as well as I/O. # Last argument forbids the driver to overwrite any existing data. hmcState = isle.drivers.hmc.newRun(lat, PARAMS, rng, makeAction, TUNEFILE, True) # Generate a random initial condition. # Note that configurations must be vectors of complex numbers. phi = isle.Vector( rng.normal(0, PARAMS.tilde("U", lat)**(1 / 2), lat.lattSize()) + 0j) # Run thermalization; tuning works best on thermalized configurations. log.info("Thermalizing") # Pick an evolver which linearly decreases the number of MD steps from 20 to 5. # The number of steps (99) must be one less than the number of trajectories below. evolver = isle.evolver.LinearStepLeapfrog(hmcState.action, (1, 1), (20, 5), 299, rng) # Thermalize configuration for 100 trajectories without saving anything. evStage = hmcState(phi, evolver, 300, saveFreq=0, checkpointFreq=0) # Reset the internal counter so we start saving configs at index 0. hmcState.resetIndex() log.info("Tuning") # Construct the auto-tuner with default settings, see the documentation of the # constructor for a list of all supported parameters. # Start with an nstep of 1 which should be sufficiently small to produce an acceptance # rate close to zero in this case. # This is good as it anchors the fit, starting close to the expected optimum can make # the fit fail and slow down tuning. evolver = isle.evolver.LeapfrogTuner(hmcState.action, 1, 1, rng, TUNEFILE, targetAccRate=0.7, targetConfIntProb=0.0001, runsPerParam=(50, 500), maxRuns=50) # Run evolution with the tuner for an indefinite number of trajectories, # LeapfrogTuner is in charge of terminating the run. # Checkpointing is not supported by the tuner, so checkpointFreq must be 0. hmcState(evStage, evolver, None, saveFreq=1, checkpointFreq=0) print(evolver.currentParams()) print(evolver.tunedParameters())
def _randomPhi(n, realOnly): "Return a normally distributed random complex vector of n elements." real = np.random.normal(RAND_MEAN, RAND_STD, n) if not realOnly: imag = np.random.normal(RAND_MEAN, RAND_STD, n) else: imag = 0 return isle.Vector(real + 1j * imag)
def makeStartPhi(lat, params, starty, rng): """ Generate a single configuration on the real plane. """ sigma = rng.uniform(np.sqrt(params.tilde("U", lat) / (1 + 16 / lat.nt())), np.sqrt(params.tilde("U", lat) / 1.0), 1)[0] return isle.Vector( np.random.normal(0, sigma, lat.lattSize()) + 1j * starty), sigma
def main(): # Initialize Isle. # This sets up the command line interface, defines a barebones argument parser, # and parses and returns parsed arguments. # More complex parsers can be automatically defined or passed in manually. # See, e.g., `hmcThermalization.py` or `measure.py` examples. isle.initialize("default") # Get a logger. Use this instead of print() to output any and all information. log = getLogger("HMC") # Load the spatial lattice. # Note: This command loads a lattice that is distributed together with Isle. # In order to load custom lattices from a file, use # either isle.LATTICES.loadExternal(filename) # or isle.fileio.yaml.loadLattice(filename) lat = isle.LATTICES[LATTICE] # Lattice files usually only contain information on the spatial lattice # to be more flexible. Set the number of time slices here. lat.nt(NT) # Set up a random number generator. rng = isle.random.NumpyRNG(1075) # Set up a fresh HMC driver. # It handles all HMC evolution as well as I/O. # Last argument forbids the driver to overwrite any existing data. hmcState = isle.drivers.hmc.newRun(lat, PARAMS, rng, makeAction, OUTFILE, False) # Generate a random initial condition. # Note that configurations must be vectors of complex numbers. phi = isle.Vector(rng.normal(0, PARAMS.tilde("U", lat)**(1/2), lat.lattSize()) +0j) # Run thermalization. log.info("Thermalizing") # Pick an evolver which linearly decreases the number of MD steps from 20 to 5. # The number of steps (99) must be one less than the number of trajectories below. evolver = isle.evolver.LinearStepLeapfrog(hmcState.action, (1, 1), (20, 5), 99, rng) # Thermalize configuration for 100 trajectories without saving anything. evStage = hmcState(phi, evolver, 100, saveFreq=0, checkpointFreq=0) # Reset the internal counter so we start saving configs at index 0. hmcState.resetIndex() # Run production. log.info("Producing") # Pick a new evolver with a constant number of steps to get a reproducible ensemble. evolver = isle.evolver.ConstStepLeapfrog(hmcState.action, 1, 5, rng) # Produce configurations and save in intervals of 2 trajectories. # Place a checkpoint every 10 trajectories. hmcState(evStage, evolver, 100, saveFreq=2, checkpointFreq=10)
def makePhi(lattice, params): # phi = isle.Vector(np.zeros(LATTICE.lattSize()) + .257541462j) phi = isle.Vector( np.random.normal(0, params.tilde("U", lattice)**(1 / 2), lattice.lattSize()) + 0j) # phix = np.random.normal(0, params.tilde("U", lattice)**(1/2), lattice.nx()) # phi = isle.Vector(np.tile(phix, lattice.nt()) + 0j) return phi
def nt_scaling(): "Benchmark scaling with Nt." with open(str(BENCH_PATH / "../resources/lattices/c60_ipr.yml"), "r") as yamlf: lat = yaml.safe_load(yamlf) nx = lat.nx() functions = { "dense": (isle.logdet, "fn(Q)", "Q = isle.Matrix(hfm.Q(phi))"), "logdetQ": (isle.logdetQ, "fn(hfm, phi)", ""), "logdetM": (isle.logdetM, "fn(hfm, phi, isle.Species.PARTICLE)+fn(hfm, phi, isle.Species.HOLE)", ""), } times = {key: [] for key in functions} for nt in NTS: print("nt = {}".format(nt)) # make random auxilliary field and HFM phi = isle.Vector( np.random.randn(nx * nt) + 1j * np.random.randn(nx * nt)) hfm = isle.HubbardFermiMatrixDia(lat.hopping() / nt, 0, -1) # do the benchmarks for name, (function, execStr, setupStr) in functions.items(): if nt > 12 and name == "dense": # this is just too slow continue times[name].append( timeit.timeit(execStr, setup=setupStr, globals={ "fn": function, "hfm": hfm, "phi": phi, "isle": isle }, number=NREP) / NREP) # save benchmark to file pickle.dump( { "xlabel": "Nt", "ylabel": "time / s", "xvalues": NTS, "results": times }, open("logdet.ben", "wb"))
def RK4(phi, epsilon, action, n, direction): if n == 0: omega1 = 1. / 6 omega2 = 1. / 3 omega3 = 1. / 3 omega4 = 1. / 6 beta21 = 0.5 beta31 = 0.0 beta32 = 0.5 beta41 = 0.0 beta42 = 0.0 beta43 = 1.0 elif n == 1: omega1 = .125 omega2 = .375 omega3 = .375 omega4 = .125 beta21 = 1 / 3. beta31 = -1. / 3 beta32 = 1.0 beta41 = 1.0 beta42 = -1.0 beta43 = 1.0 k1 = -direction * isle.Vector(np.conj(action.force(phi))) k2 = -direction * isle.Vector( np.conj(action.force(phi + epsilon * beta21 * k1))) k3 = -direction * isle.Vector( np.conj(action.force(phi + epsilon * (beta31 * k1 + beta32 * k2)))) k4 = -direction * isle.Vector( np.conj( action.force(phi + epsilon * (beta41 * k1 + beta42 * k2 + beta43 * k3)))) return phi + epsilon * (omega1 * k1 + omega2 * k2 + omega3 * k3 + omega4 * k4)
def test_2_eval(self): "Test eval function of isle.action.SumAction." logger = getLogger(__name__) logger.info("Testing SumAction.eval()") for rep in range(N_REP): sact = isle.action.SumAction(_DummyAction(), isle.action.HubbardGaugeAction(1)) phi = np.random.normal(RAND_MEAN, RAND_STD, 1000) \ + 1j*np.random.normal(RAND_MEAN, RAND_STD, 1000) sacteval = sact.eval(isle.Vector(phi)) manualeval = np.sum(phi) + np.dot(phi, phi)/2 self.assertAlmostEqual(sacteval, manualeval, places=10, msg="Failed check of SumAction.eval in repetition {}\n".format(rep)\ + f"with sacteval = {sacteval}, manualeval = {manualeval}")
def _getRHSs(self, nt): """! Get all right hand side vectors as a matrix. For the j^th spacetime vector of the i^th state, go to self.rhss[i * nt + j] In other words, time is the faster running index. """ if self._rhss is None or self._rhss.rows() != nt * self.hfm.nx(): # Create a large set of sources: self._rhss = isle.Matrix( np.array([ isle.Vector( self.rng.normal(0, 1, nt * self.hfm.nx()) + 0j) for _ in range(self.nsamples) ])) return self._rhss
def tuneForEnsemble(latticeYAML, paramsYAML, clArgs): """ Tune the leapfrog integrator for ont ensemble with given lattice and parameters. """ rank = MPI.COMM_WORLD.rank log = getLogger(f"{__name__}[{rank}]") # Re-construct the actual Objects from strings here in the worker task. lattice = yaml.safe_load(latticeYAML) params = yaml.safe_load(paramsYAML) # A unique output file name for this set or parameters. outputFile = "mpiexample" + fnameSuffix(lattice, params) # Set up a random number generator. rng = isle.random.NumpyRNG(rank + 831) # Set up a fresh HMC driver just for this ensemble. hmcState = isle.drivers.hmc.newRun(lattice, params, rng, makeAction, outputFile, clArgs.overwrite) # Generate a random initial condition. phi = isle.Vector( rng.normal(0, params.tilde("U", lattice)**(1 / 2), lattice.lattSize()) + 0j) # Run thermalization. log.info("Thermalizing") # Pick an evolver which linearly decreases the number of MD steps from 20 to 5. # The number of steps (99) must be one less than the number of trajectories below. evolver = isle.evolver.LinearStepLeapfrog(hmcState.action, (1, 1), (20, 5), 99, rng) # Thermalize configuration for 100 trajectories without saving anything. stage = hmcState(phi, evolver, 100, saveFreq=0, checkpointFreq=0) # Reset the internal counter so we start saving configs at index 0. hmcState.resetIndex() log.info("Tuning") # Construct the auto-tuner with default settings, see the documentation of the # constructor for a list of all supported parameters. evolver = isle.evolver.LeapfrogTuner(hmcState.action, 1, 1, rng, outputFile) # Run evolution with the tuner for an indefinite number of trajectories, hmcState(stage, evolver, None, saveFreq=1, checkpointFreq=0)
def test_3_force(self): "Test force function of isle.action.SumAction." logger = getLogger(__name__) logger.info("Testing SumAction.force()") for rep in range(N_REP): sact = isle.action.SumAction(_DummyAction(), isle.action.HubbardGaugeAction(1)) phi = np.random.normal(RAND_MEAN, RAND_STD, 1000) \ + 1j*np.random.normal(RAND_MEAN, RAND_STD, 1000) sactforce = sact.force(isle.Vector(phi)) manualforce = phi - phi / 1 self.assertAlmostEqual(np.linalg.norm(sactforce-manualforce), 0., places=10, msg="Failed check of SumAction.force in repetition {}\n".format(rep)\ + f"with sactforce = {sactforce}, manualforce = {manualforce}")
def nx_scaling(): "Benchmark scaling with Nx." lattices = [ isle.yamlio.loadLattice(fname) for fname in (BENCH_PATH / "../resources/lattices").iterdir() ] lattices = sorted(lattices, key=lambda lat: lat.nx()) NT = 16 times = {"logdetM": []} nxs = [] for lat in lattices: print(f"lat = {lat.name}") nx = lat.nx() if nx in nxs: continue nxs.append(nx) lat.nt(NT) # make random auxilliary field and HFM phi = isle.Vector( np.random.randn(nx * NT) + 1j * np.random.randn(nx * NT)) hfm = isle.HubbardFermiMatrixDia(lat.hopping() / NT, 0, -1) times["logdetM"].append( timeit.timeit("isle.logdetM(hfm, phi, isle.Species.PARTICLE)", globals={ "hfm": hfm, "phi": phi, "isle": isle }, number=NREP) / NREP) # save benchmark to file pickle.dump( { "xlabel": "Nx", "ylabel": "time / s", "xvalues": nxs, "results": times }, open("logdet.ben", "wb"))
def _randomPhi(n): "Return a normally distributed random complex vector of n elements." real = np.random.normal(RAND_MEAN, RAND_STD, n) imag = np.random.normal(RAND_MEAN, RAND_STD, n) return isle.Vector(real + 1j*imag)
def generate(overwrite): """ Generate and store a single dataset. """ log = getLogger(f"{__name__}") lattice = isle.LATTICES[LATTICE] lattice.nt(NT) params = PARAMS flowParams = preprocessFlowParams(lattice, params) outfname = initFile(lattice,params,flowParams,overwrite) #DATADIR/formatFname("train", lattice, params, flowParams) # The default RNG should not have been seeded... rng = isle.random.NumpyRNG(random.randint(0, 10000)) action = makeAction(lattice, params) # Set up a fresh HMC driver. # It handles all HMC evolution as well as I/O. # Last argument forbids the driver to overwrite any existing data. #hmcState = isle.drivers.hmc.newRun(lat, params, rng, makeAction, outfile_fn, overwrite) hmcStage = isle.drivers.hmc.HMC(lattice,params,rng,action,outfname,0) # Generate a random initial condition. # Note that configurations must be vectors of complex numbers. phi_unflowed = isle.Vector(rng.normal(0, params.tilde("U", lattice)**(1/2), lattice.lattSize())+0j) log.info("Thermalizing") # Pick an evolver which linearly decreases the number of MD steps from 20 to 5. # The number of steps (99) must be one less than the number of trajectories below. evolver = isle.evolver.LinearStepLeapfrog(action, (1, 1), (20, 5), 99, rng) phi_unflowed = hmcStage(phi_unflowed,evolver,1000,0,0).phi # Run production. log.info("Producing") # Pick a new evolver with a constant number of steps to get a reproducible ensemble. evolver = isle.evolver.ConstStepLeapfrog(action, 1, 5, rng) # Produce configurations and save in intervals of 2 trajectories. # Place a checkpoint every 10 trajectories. flowTime = [] fields_flowed = np.empty((NSAMPLES, lattice.lattSize()), dtype=complex) fields_unflowed = np.empty((NSAMPLES, lattice.lattSize()), dtype=complex) actions = np.empty(NSAMPLES, dtype=complex) isample = 0 tries = 0 while isample < NSAMPLES: tries += 1 # create the next 100 trajectories and return the last as new configuration phi_unflowed = hmcStage(phi_unflowed,evolver,100,0,0).phi # Solve the flow equation phi_flowed, actVal, actualFlowTime = isle.rungeKutta4Flow(phi_unflowed, action, flowParams.flowTime, flowParams.stepSize, imActTolerance=1e-6) if actualFlowTime < flowParams.minFlowTime: log.info("Failed to generate phi at sample %d, reached flow time: %f",isample, actualFlowTime) else: # store the fields fields_flowed[isample, :] = phi_flowed fields_unflowed[isample, :] = phi_unflowed actions[isample] = actVal flowTime.append(actualFlowTime) isample += 1 # write the data to file with h5.File(str(outfname), "a") as h5f: h5f["phi_flowed"] = fields_flowed h5f["phi_unflowed"] = fields_unflowed h5f["action_flowed"] = actions grp = h5f.create_group("flow_diagnostics") grp["flow_time"] = np.array(flowTime)
def _randomPhi(n, real=True, imag=True): "Return a normally distributed random complex vector of n elements." r = np.random.normal(RAND_MEAN, RAND_STD, n) if real else 0 i = np.random.normal(RAND_MEAN, RAND_STD, n) if imag else 0 return isle.Vector(r + 1j*i)
def calcPhiNorm(x): phi = isle.Vector(x[0] * np.ones(lattice.lattSize()) + 1j * x[1]) phi = isle.Vector(np.array(action.force(phi))) return phi[0].real * phi[0].real + phi[0].imag * phi[0].imag
def generate(overwrite, tangent): """ Generate and store a single dataset. """ log = getLogger(f"{__name__}") lattice = isle.LATTICES[LATTICE] lattice.nt(Nt) params = PARAMS flowParams = preprocessFlowParams(lattice, params) outfname = initFile(lattice, params, flowParams, overwrite) # The default RNG should not have been seeded... rng = isle.random.NumpyRNG(random.randint(0, 10000)) action = makeAction(lattice, params) def calcPhiNorm(x): phi = isle.Vector(x[0] * np.ones(lattice.lattSize()) + 1j * x[1]) phi = isle.Vector(np.array(action.force(phi))) return phi[0].real * phi[0].real + phi[0].imag * phi[0].imag starty = 0.0 if tangent: # First find critical point assuming constant phi x0 = np.array([startRePhi, startImPhi]) res = minimize(calcPhiNorm, x0, method='nelder-mead', options={ 'xtol': 1e-14, 'disp': True }) print('# Critical phi @ ', res.x) phi = isle.Vector(res.x[0] * np.ones(lattice.lattSize()) + 1j * res.x[1]) print('# S = ', action.eval(phi)) starty = res.x[1] flowTime = [] width = [] conf_gauss = np.empty((NSAMPLES, lattice.lattSize()), dtype=complex) conf_flowed = np.empty((NSAMPLES, lattice.lattSize()), dtype=complex) actions = np.empty(NSAMPLES, dtype=complex) with isle.cli.trackProgress(NSAMPLES) as pbar: isample = 0 tries = 0 while isample < NSAMPLES: tries += 1 phi_gauss, sigma = makeStartPhi(lattice, params, starty, rng) phi_flowed, actVal, actualFlowTime = isle.rungeKutta4Flow( phi_gauss, action, flowParams.flowTime, flowParams.stepSize, imActTolerance=1e-6) if actualFlowTime < flowParams.minFlowTime: log.info( "Failed to generate phi at sample %d, reached flow time: %f", isample, actualFlowTime) else: conf_gauss[isample, :] = phi_gauss conf_flowed[isample, :] = phi_flowed actions[isample] = actVal flowTime.append(actualFlowTime) width.append(sigma) isample += 1 pbar.advance() pbar._message = f"Success rate: {isample/tries:.3f}" with h5.File(str(outfname), "a") as h5f: h5f["phi_flowed"] = conf_flowed h5f["phi_gauss"] = conf_gauss h5f["action_flowed"] = actions grp = h5f.create_group("flow_diagnostics") grp["flow_time"] = np.array(flowTime) grp["width"] = np.array(width)