dmrg_postprocessing.py
1 """@package dmrg_postprocessing
2 @brief Python module for post-processing data files produced by a DMRG.x application.
3 See @ref Postprocessing module for more info.
4 
5 @defgroup Postprocessing
6 @brief Python module for post-processing data files produced by a DMRG.x application.
7 
8 Usage:
9  >>> import sys
10  >>> sys.path.append("/path/to/DMRG.x/postproc")
11  >>> import dmrg_postprocessing as dmrg
12 
13 """
14 
15 ## @addtogroup Postprocessing
16 # @{
17 
18 import os
19 import numpy as np
20 import matplotlib.pyplot as plt
21 import copy
22 import dmrg_json_utils as ju
23 
24 class Data:
25  """
26  Post-processing of a single DMRG run.
27  """
28 
29  def __init__(self, base_dir, jobs_dir='', label=None):
30  """Initializes the Data object.
31 
32  This initializer simply fills in the base directory and prepares the private
33  variables which will store the corresponding data. The variables are first
34  set to None. Whenever some data is requested, these variables will then be filled.
35 
36  Args:
37  base_dir: directory containing the data files
38 
39  Kwargs:
40  jobs_dir: path to be prepended to base_dir
41  label: label for plots with this data object
42 
43  Example:
44  >>> data = dmrg.Data("data_dir",jobs_dir="/tmp/data/",label="Data1")
45  """
46  self._base_dir = os.path.join(jobs_dir,base_dir)
47  self._label = label
48  self._run = None
49  self._sweepIdx = None
50  self._steps = None
51  self._timings = None
52  self._hamPrealloc = None
53  self._entSpectra = None
54  self._corr = None
55  self._corrLookup = None
56  self._color = None
57 
58  def PathToFile(self,filename=""):
59  """Returns the path to a filename prepended with the base directory
60 
61  Args:
62  filename: filename to append to the base directory
63  Returns:
64  A string combining the path and filename
65  """
66  return os.path.join(self._base_dir,filename)
67 
68  def Label(self):
69  """
70  Returns the label of the Data object
71  """
72  return self._label
73 
74 
80 
81  def _LoadRun(self):
82  """
83  Loads the DMRGRun.json data file, if not loaded, and returns its contents
84  """
85  if self._run is None:
86  self._run = ju.LoadJSONDict(self.PathToFile('DMRGRun.json'))
87  return self._run
88 
89  def RunData(self):
90  """
91  Returns the contents of the DMRGRun.json data file
92  """
93  return self._LoadRun()
94 
95 
97 
98 
104 
105  def _LoadSteps(self):
106  """Loads data from DMRGSteps.json, if not loaded, and extracts the indices
107  for required data columns.
108  """
109  if self._steps is None:
110  steps = ju.LoadJSONTable(self.PathToFile('DMRGSteps.json'))
111  self._stepsHeaders = steps['headers']
112  self._idxEnergy = self._stepsHeaders.index('GSEnergy')
113  self._idxNSysEnl = self._stepsHeaders.index('NSites_SysEnl')
114  self._idxNEnvEnl = self._stepsHeaders.index('NSites_EnvEnl')
115  self._idxLoopidx = self._stepsHeaders.index('LoopIdx')
116  self._idxNStatesH = self._stepsHeaders.index('NumStates_H')
117  self._steps = steps['table']
118 
119  def Steps(self,header=None):
120  """Returns a specific column of data of DMRGSteps.json according to the
121  header string or returns a deepcopy of the entire data table
122 
123  Kwargs:
124  header: string corresponding to the 'header' of DMRGSteps.json
125  """
126  self._LoadSteps()
127  if header is None:
128  return copy.deepcopy(self._steps)
129  else:
130  idx = self._stepsHeaders.index(header)
131  return np.array([row[idx] for row in self._steps])
132 
133  def StepsHeaders(self):
134  """Returns the headers of the table in DMRGSteps.json
135  """
136  self._LoadSteps()
137  return self._stepsHeaders
138 
139  def SweepIdx(self,show_all=False):
140  """Returns the step indices corresponding to the end of a sweep.
141 
142  By default this function looks for the maximum LoopIdx in a range.
144  Args:
145  show_all: If set to `True`, all step indices will be given.
146  If `False`, return only those that correspond to where the
147  number of sites in the system is equal to the number of sites in
148  the environment.
149 
150  Returns:
151  List of indices corresponding to `GlobIdx`
152  """
153  NSites_Sys = self.Steps("NSites_Sys")
154  NSites_Env = self.Steps("NSites_Env")
155 
156  if self._sweepIdx is None:
157  StepIdx = self.Steps("StepIdx")
158  LoopIdx = self.Steps("LoopIdx")
159  # Look for the maximum position for each loop index
160  self._sweepIdx = [max(np.where(LoopIdx==i)[0]) for i in list(set(LoopIdx))]
161 
162  if show_all:
163  return self._sweepIdx
164  else:
165  # Include only the positions where the number of sites in the system == environment
166  return [ j for j in self._sweepIdx if NSites_Sys[j]==NSites_Env[j]]
167 
168  def EnergyPerSite(self):
169  """Calculates the energy per site of the superblock Hamiltonian for all steps
170 
171  Returns:
172  Numpy array containing the energy per site at each step
173  """
174  self._LoadSteps()
175  return np.array([row[self._idxEnergy]/(row[self._idxNSysEnl]+row[self._idxNEnvEnl]) for row in self._steps])
176 
177  def NumStatesSuperblock(self,n=None):
178  """Determines the number of states in the superblock Hamiltonian.
179 
180  If `n` is `None`, the number of sates for all steps will be returned.
182  Args:
183  n: Index of the step.
184 
185  Returns:
186  Single integer or array representing the number/s of states
187  """
188  self._LoadSteps()
189  if n is None:
190  self._NStatesH = np.array([row[self._idxNStatesH] for row in self._steps])
191  return self._NStatesH
192  else:
193  return self._steps[n][self._idxNStatesH]
194 
195  def PlotEnergyPerSite(self,**kwargs):
196  """Plots the energy per site of the superblock Hamiltonian as a function of DMRG steps.
197  The given label and all kwargs will be passed to the plt.plot function.
198 
199  Example:
200  >>> data = dmrg.Data("data_dir",jobs_dir="/tmp/data/",label="Data1")
201  >>> data.PlotEnergyPerSite(marker='.',color='r')
202  >>> plt.show()
203  """
204  self._LoadSteps()
205  energy_iter = np.array(self.EnergyPerSite())
206  self._p = plt.plot(energy_iter,label=self._label,**kwargs)
207  self._color = self._p[-1].get_color()
208  plt.xlabel('DMRG Steps')
209  plt.ylabel(r'$E_0/N$')
210 
211  def PlotErrorEnergyPerSite(self,which='abs',compare_with='min',**kwargs):
212  """Plots the error in the energy per site of the superblock Hamiltonian as a function of DMRG steps.
213  Kwargs will be passed to the plt.plot function.
214 
215  Example:
216  >>> data = dmrg.Data("data_dir",jobs_dir="/tmp/data/",label="Data1")
217  >>> data.PlotErrorEnergyPerSite(marker='.',which='rel',color='r')
218  >>> plt.show()
219 
220  Args:
221  which: Show either the absolute `'abs'` or relative `'rel'` error
222  compare_with: Compare against the minimum `'min'` or `'last'` obtained value
223  **kwargs: keyword arguments to be passed to plt.semilogy
224 
225  Raises:
226  ValueError: When options are not matched
227  """
228  self._LoadSteps()
229  if compare_with=='min':
230  best = lambda energy_iter: np.min(energy_iter)
231  elif compare_with=='last':
232  best = lambda energy_iter: energy_iter[-1]
233  else:
234  raise ValueError("The value of compare_with can only be 'min' or 'last'. Got {}".format(compare_with))
235  if which=='abs':
236  energy_iter = np.array(self.EnergyPerSite())
237  energy_iter = np.abs((energy_iter - best(energy_iter)))
238  plt.ylabel(r'$E_0/N - \mathrm{min}(E_0/N)$')
239  elif which=='rel':
240  energy_iter = np.array(self.EnergyPerSite())
241  energy_iter = np.abs((energy_iter - best(energy_iter))/best(energy_iter))
242  plt.ylabel(r'$[E_0/N - \mathrm{min}(E_0/N)] / \mathrm{min}(E_0/N)$')
243  else:
244  raise ValueError("The value of which can only be 'abs' or 'rel'. Got {}".format(which))
245 
246  self._p = plt.semilogy(energy_iter,label=self._label,**kwargs)
247  self._color = self._p[-1].get_color()
248  plt.xlabel('DMRG Steps')
249 
250  def PlotLoopBars(self,my_color=None,**kwargs):
251  """Plot vertical lines corresponding to the end of each sweep
252 
253  Args:
254  my_color: Override default colors
255  **kwargs: keyword arguments to be passed to plt.axvline
256  """
257  self._LoadSteps()
258  LoopIdx = [row[self._idxLoopidx] for row in self._steps]
259  dm = np.where([LoopIdx[i] - LoopIdx[i-1] for i in range(1,len(LoopIdx))])[0]
260  for d in dm:
261  if my_color is not None:
262  plt.axvline(x=d,color=my_color,linewidth=1,**kwargs)
263  elif self._color is None:
264  plt.axvline(x=d,linewidth=1,**kwargs)
265  else:
266  plt.axvline(x=d,color=self._color,linewidth=1,**kwargs)
267 
268  ##
269  # @}
270 
271  #
272  # Timings data
273  #
274  def _LoadTimings(self):
275  if self._timings is None:
276  timings = ju.LoadJSONTable(self.PathToFile('Timings.json'))
277  self._timingsHeaders = timings['headers']
278  self._idxTimeTot = self._timingsHeaders.index('Total')
279  self._timings = timings['table']
280 
281  def Timings(self):
282  self._LoadTimings()
283  return copy.deepcopy(self._timings)
284 
285  def TimingsHeaders(self):
286  self._LoadTimings()
287  return copy.deepcopy(self._timingsHeaders)
288 
289  def TotalTime(self):
290  self._LoadTimings()
291  return np.array([row[self._idxTimeTot] for row in self._timings])
292 
293  def PlotTotalTime(self,which='plot',cumulative=False,units='sec',**kwargs):
294  totTime = self.TotalTime()
295  ylabel = "Time Elapsed per Step"
296 
297  if cumulative:
298  totTime = np.cumsum(totTime)
299  ylabel = "Cumulative Time Elapsed"
300 
301  if units=='min':
302  totTime /= 60.
303  elif units=='hrs':
304  totTime /= 3600.
305  elif units=='sec':
306  pass
307  else:
308  raise ValueError("units='{}' unsupported. Choose among ['sec','min','hrs']".format(units))
309 
310  if which=='plot':
311  self._p = plt.plot(totTime,label=self._label,**kwargs)
312  elif which=='semilogy':
313  self._p = plt.semilogy(totTime,label=self._label,**kwargs)
314  else:
315  raise ValueError("which='{}' unsupported. Choose among ['plot','semilogy']".format(which))
316 
317  self._color = self._p[-1].get_color()
318  plt.xlabel('DMRG Steps')
319  plt.ylabel('{} ({})'.format(ylabel,units))
320 
321  #
322  # Preallocation data
323  #
324  def PreallocData(self,n=None,key=None):
325  if self._hamPrealloc is None:
326  self._hamPrealloc = ju.LoadJSONArray(self.PathToFile('HamiltonianPrealloc.json'))
327  if n is None:
328  return self._hamPrealloc
329  else:
330  if key is None:
331  return self._hamPrealloc[n]
332  else:
333  if key=="Tnnz":
334  return np.array(self._hamPrealloc[n]["Dnnz"]) + np.array(self._hamPrealloc[n]["Onnz"])
335  else:
336  return self._hamPrealloc[n][key]
337 
338  def PlotPreallocData(self,n,totals_only=True,**kwargs):
339  Dnnz = np.array(self.PreallocData(n)["Dnnz"])
340  Onnz = np.array(self.PreallocData(n)["Onnz"])
341  self._p = plt.plot(Dnnz+Onnz,label='Tnnz: {}'.format(n),marker='o',**kwargs)
342  if not totals_only:
343  color = self._p[-1].get_color()
344  self._p = plt.plot(Dnnz,label='Dnnz: {}'.format(n),color=color,marker='s',**kwargs)
345  self._p = plt.plot(Onnz,label='Onnz: {}'.format(n),color=color,marker='^',**kwargs)
346 
347  #
348  # Entanglement Spectra
349  #
350  def _LoadSpectra(self):
351  if self._entSpectra is None:
352  ##
353  # @todo
354  # Change the output of DMRG.x from EntanglementSpectra.json
355  # to RDMEigenvalues.json
356  #
357  spectra = ju.LoadJSONArray(self.PathToFile('EntanglementSpectra.json'))
358  # determine which global indices are the ends of a sweep
359 
360  self._entSpectra = spectra
361 
362  def RDMEigenvalues(self,idx=None):
363  ''' Loads the reduced density matrix eigenvalues at the end of each sweep or for
364  a particular sweep
365 
366  Args:
367  idx: The index of the sweep requested.
368  If None, the data for all indices are returned.
369  Returns:
370  An array of values when idx is None, or a single value otherwise.
371  '''
372  self._LoadSpectra()
373  if idx is None:
374  vals = [self._entSpectra[row]['Sys'] for row in self.SweepIdx()]
375  else:
376  row = self.SweepIdx()[idx]
377  vals = self._entSpectra[row]['Sys']
378  return vals
379 
380  def EntanglementEntropy(self):
381  ''' Calculates the entanglement entropy using eigenvalues from all sectors '''
382  a = self.RDMEigenvalues()
383  l = [np.concatenate([a[i][j]['vals'] for j in range(len(a[i]))]) for i in range(len(a))]
384  return [-np.sum( [np.log(lii)*lii for lii in li if lii > 0] ) for li in l]
385 
386  def PlotEntanglementEntropy(self):
387  ''' Plots the entanglement entropy using eigenvalues from all sectors '''
388  ent = np.array(self.EntanglementEntropy())
389  plt.plot(ent,'-o')
390  plt.xticks(np.arange(ent.shape[0]))
391  plt.xlabel("Sweeps")
392  plt.ylabel(r"Von Neumann Entropy (S)")
394  #
395  # Correlations
396  #
397  def _LoadCorrelations(self):
398  if self._corr is None:
399  self._corr = ju.LoadJSONTable(self.PathToFile('Correlations.json'))
400 
401  def _LoadCorrelationsLookup(self):
402  self._LoadCorrelations()
403  if self._corrLookup is None:
404  self._corrLookup = {}
405  for key in self._corr['info'][0].keys(): self._corrLookup[key] = []
406  for corr in self._corr['info']:
407  for key in corr:
408  self._corrLookup[key].append(corr[key])
409 
410  def Correlations(self):
411  self._LoadCorrelations()
412  return self._corr
413 
414  def CorrelationsInfo(self):
415  return self.Correlations()['info']
416 
417  def CorrelationsValues(self, key=None, labels=None):
418  if labels is None:
419  return np.array(self.Correlations()['values'])
420  elif labels is not None and key is None:
421  raise ValueError('key must be given when labels is not None')
422  else:
423  idx = self.CorrelationsInfoGetIndex(key, labels)
424  return np.array(self.Correlations()['values'])[:,idx]
425 
426  def CorrelationsInfoGetIndex(self, key=None, value=None):
427  self._LoadCorrelationsLookup()
428 
429  if key is None and value is None:
430  return self._corrLookup
431 
432  elif key is not None and value is None:
433  try:
434  return self._corrLookup[key]
435  except KeyError:
436  raise ValueError("Incorrect key='{}'. Choose among {}".format(key, self._corrLookup.keys()))
437 
438  elif key is None and value is not None:
439  raise ValueError('key must be given when value is not None')
440 
441  else: # both key and value are not none
442  if isinstance(value,(list,tuple,np.ndarray)):
443  return [self._corrLookup[key].index(v) for v in value]
444  else:
445  return self._corrLookup[key].index(value)
446 
447 class DataSeries:
448  """
449  Post-processing for multiple DMRG runs
450  """
451 
452  def __init__(self, base_dir_list, label_list=None, *args, **kwargs):
453  if base_dir_list:
454  # if non-empty check whether the first entry is a tuple
455  if len(base_dir_list[0]) == 2:
456  dirs = [dl[0] for dl in base_dir_list]
457  labels = [dl[1] for dl in base_dir_list]
458  base_dir_list = dirs
459  label_list = labels
460  # note: overrides the input label_list
461  if label_list is None:
462  self.DataList = [ Data(base_dir, *args, label=base_dir, **kwargs) for base_dir in base_dir_list ]
463  else:
464  self.DataList = [ Data(base_dir, *args, label=label, **kwargs) for (base_dir, label) in zip(base_dir_list,label_list) ]
465 
466  def Data(self,n):
467  return self.DataList[n]
468 
469  def PlotEnergyPerSite(self,**kwargs):
470  for obj in self.DataList: obj.PlotEnergyPerSite(**kwargs)
471 
472  def PlotErrorEnergyPerSite(self,which='abs',**kwargs):
473  for obj in self.DataList: obj.PlotErrorEnergyPerSite(which='abs',**kwargs)
474 
475  def PlotTotalTime(self,**kwargs):
476  for obj in self.DataList: obj.PlotTotalTime(**kwargs)
477 
478  def PlotLoopBars(self,n=None,**kwargs):
479  if n is None:
480  for obj in self.DataList: obj.PlotLoopBars(**kwargs)
481  else:
482  self.DataList[n].PlotLoopBars(**kwargs)
483 
484 ##
485 # @}
def Steps(self, header=None)
Returns a specific column of data of DMRGSteps.json according to the header string or returns a deepc...
def PlotEnergyPerSite(self, kwargs)
Plots the energy per site of the superblock Hamiltonian as a function of DMRG steps.
def RunData(self)
Returns the contents of the DMRGRun.json data file.
def NumStatesSuperblock(self, n=None)
Determines the number of states in the superblock Hamiltonian.
Post-processing of a single DMRG run.
def __init__(self, base_dir, jobs_dir='', label=None)
Initializes the Data object.
def SweepIdx(self, show_all=False)
Returns the step indices corresponding to the end of a sweep.
def _LoadRun(self)
Loads the DMRGRun.json data file, if not loaded, and returns its contents.
def PathToFile(self, filename="")
Returns the path to a filename prepended with the base directory.
def _LoadSteps(self)
Loads data from DMRGSteps.json, if not loaded, and extracts the indices for required data columns...
def EnergyPerSite(self)
Calculates the energy per site of the superblock Hamiltonian for all steps.
def StepsHeaders(self)
Returns the headers of the table in DMRGSteps.json.
def Label(self)
Returns the label of the Data object.
This site was generated by Sphinx using Doxygen with a customized theme from doxygen-bootstrapped.