wilson.wcxf.classes module
import json import yaml import logging from collections import OrderedDict, Counter import tempfile import shutil import os import subprocess from pandas import DataFrame import random # the following is necessary to get pretty representations of # OrderedDict and defaultdict instances in YAML def _represent_dict_order(self, data): return self.represent_mapping('tag:yaml.org,2002:map', data.items()) yaml.add_representer(OrderedDict, _represent_dict_order) # the following is necessary to have Null values be represented by # emptyness rather than 'null' def represent_none(self, _): return self.represent_scalar('tag:yaml.org,2002:null', '') yaml.add_representer(type(None), represent_none) # the following is necessary to load YAML mappings as OrderedDicts _mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG def _dict_constructor(loader, node): return OrderedDict(loader.construct_pairs(node)) yaml.add_constructor(_mapping_tag, _dict_constructor) def _load_yaml_json(stream, **kwargs): """Load a JSON or YAML file from a string or stream.""" if isinstance(stream, str): ss = stream else: ss = stream.read() try: return json.loads(ss, **kwargs) except ValueError: return yaml.safe_load(ss, **kwargs) def _dump_json(d, stream=None, **kwargs): """Dump to a JSON string (if `stream` is None) or stream.""" if stream is not None: return json.dump(d, stream, **kwargs) else: return json.dumps(d, **kwargs) def _yaml_to_json(stream_in, stream_out, **kwargs): d = yaml.safe_load(stream_in) return _dump_json(d, stream_out, **kwargs) def _json_to_yaml(stream_in, stream_out, **kwargs): d = json.load(stream_in) return yaml.dump(d, stream_out, **kwargs) def _testtex(s, delete=True): """Function that takes a string and tries to compile a LaTeX file with the string as document body. Returns a dictionary with keys 'success' (True or False) and 'log', trying to extract the first error message.""" _preamble = r"""\documentclass{article} \usepackage{amsmath,amssymb} \begin{document} """ _enddoc = r""" \end{document}""" doc = _preamble + s + _enddoc tmpd = tempfile.mkdtemp() tmpf = os.path.join(tmpd, 'textest.tex') with open(tmpf, 'w') as f: f.write(doc) try: p = subprocess.run(['latex', '-halt-on-error', '-output-directory', tmpd, tmpf], stdout=subprocess.DEVNULL) except FileNotFoundError: logging.warn('latex executable not found. Cannot check tex code') return {'success': True} if p.returncode == 0: res = {'success': True} else: res = {'success': False} logf = os.path.join(tmpd, 'textest.log') res['log'] = '' try: with open(logf) as f: logl = f.readlines() except FileNotFoundError: pass fail = False for i, l in enumerate(logl): if 'Undefined control sequence' in l or 'Emergency stop' in l: fail = True break if fail: l = logl[i] while l.strip() != '': res['log'] += logl[i] i += 1 try: l = logl[i] except IndexError: break if delete: shutil.rmtree(tmpd) return res class NamedInstanceMetaclass(type): # this is just needed to implement the getitem method on NamedInstanceClass # to allow the syntax MyClass['instancename'] as shorthand for # MyClass.get_instance('instancename'); same for # del MyClass['instancename'] instead of MyClass.del_instance('instancename') def __getitem__(cls, item): return cls.get_instance(item) def __delitem__(cls, item): return cls.del_instance(item) class NamedInstanceClass(metaclass=NamedInstanceMetaclass): """Base class for classes that have named instances that can be accessed by their name. Parameters ---------- - name: string Methods ------- - del_instance(name) Delete an instance - get_instance(name) Get an instance - set_description(description) Set the description """ def __init__(self, _name): if not hasattr(self.__class__, 'instances'): self.__class__.instances = OrderedDict() self.__class__.instances[_name] = self self._name = _name @classmethod def get_instance(cls, _name): return cls.instances[_name] @classmethod def del_instance(cls, _name): del cls.instances[_name] @classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict() class WCxf: """Base class for WCxf files (not meant to be used directly).""" @classmethod def load(cls, stream, **kwargs): """Load the object data from a JSON or YAML file.""" wcxf = _load_yaml_json(stream, **kwargs) return cls(**wcxf) def dump(self, stream=None, fmt='json', **kwargs): """Dump the object data to a JSON or YAML file. Optional arguments: - `stream`: if None (default), return a string. Otherwise, should be a writable file-like object - `fmt`: format, should be 'json' (default) or 'yaml' Additional keyword arguments will be passed to the `json.dump(s)` or `yaml.dump` methods. """ d = {k: v for k,v in self.__dict__.items() if k[0] != '_'} if fmt.lower() == 'json': # set indent=2 unless specified otherwise indent = kwargs.pop('indent', 2) return _dump_json(d, stream=stream, indent=indent, **kwargs) elif fmt.lower() == 'yaml': # set default_flow_style=False unless specified otherwise default_flow_style = kwargs.pop('default_flow_style', False) return yaml.dump(d, stream, default_flow_style=default_flow_style, **kwargs) else: raise ValueError(f"Format {fmt} unknown: use 'json' or 'yaml'.") class EFT(WCxf, NamedInstanceClass): """Class representing EFT files.""" def __init__(self, eft, sectors, **kwargs): """Instantiate the EFT file object.""" self.eft = eft self.sectors = sectors super().__init__(eft) for k, v in kwargs.items(): setattr(self, k, v) @property def known_bases(self): """Return a list of known bases for this EFT.""" return tuple(basis[1] for basis in Basis.instances if basis[0] == self.eft) @property def known_translators(self): """Return a list of known translators between bases of this EFT.""" return tuple(t for t in Translator.instances if t[0] == self.eft) class Basis(WCxf, NamedInstanceClass): """Class representing basis files.""" def __init__(self, eft, basis, sectors, **kwargs): """Instantiate the basis file object.""" self.eft = eft self.basis = basis super().__init__((eft, basis)) for k, v in kwargs.items(): setattr(self, k, v) if hasattr(self, 'parent'): try: self.sectors = Basis[self.eft, self.parent].sectors.copy() self.sectors.update(sectors) except (AttributeError, KeyError): raise ValueError(f"Parent basis {self.parent} not found") else: self.sectors = sectors self._all_wcs = None @property def known_translators(self): """Return a list of known translators to and from this basis.""" kt = {} kt['from'] = tuple(t for t in Translator.instances if t[0] == self.eft and t[1] == self.basis) kt['to'] = tuple(t for t in Translator.instances if t[0] == self.eft and t[2] == self.basis) return kt @property def all_wcs(self): """Return a list with all Wilson coefficients defined in this basis.""" if self._all_wcs is None: self._all_wcs = [wc for sector, wcs in self.sectors.items() for wc in wcs] return self._all_wcs def validate(self): """Validate the basis file.""" try: eft_instance = EFT[self.eft] except (AttributeError, KeyError): raise ValueError(f"EFT {self.eft} not defined") unknown_sectors = set(self.sectors.keys()) - set(eft_instance.sectors.keys()) if unknown_sectors: raise ValueError(f"Unknown sectors: {unknown_sectors}") all_keys = [k for s in self.sectors.values() for k, v in s.items()] if len(all_keys) != len(set(all_keys)): # we have duplicate keys! cnt = Counter(all_keys) dupes = [k for k, v in cnt.items() if v > 1] raise ValueError("Duplicate coefficients in different sectors:" " {}".format(dupes)) # check for LaTeX errors # string with all tex values alltex = '${}$'.format('$, $'.join([d.get('tex', '~') for c in self.sectors.values() for d in c.values() if d is not None])) res = _testtex(alltex) if not res['success']: raise ValueError("Validation of basis {}/{}: " .format(self.eft, self.basis) + "LaTeX compilation errors encountered:\n" + "{}".format(res['log'])) def __repr__(self): return f"wcxf.Basis('{self.eft}', '{self.basis}', {{...}})" def _repr_markdown_(self): md = f"# Basis `{self.basis}` (EFT `{self.eft}`)\n\n" if hasattr(self, 'metadata') and 'description' in self.metadata: md += self.metadata['description'] + "\n\n" return md def _markdown_tables(self): md = "## Sectors\n\n" md += "The effective Lagrangian is defined as\n" # general definition of Leff according to WCxf papaer md += (r"$$\mathcal L_\text{eff} = -\mathcal H_\text{eff} =" r"\sum_{O_i= O_i^\dagger} C_i \, O_i + \sum_{O_i\neq O_i^\dagger}" r"\left( C_i \, O_i + C^*_i \, O^\dagger_i\right).$$") md += "\n\n" for s, wcs in self.sectors.items(): md += f"### `{s}`\n\n" if wcs: md += "| WC name | Operator | Type |\n" # NB: this is meant for pandoc; it computes column widths # in latex by counting the number of "-" separators as # fractions of line width (default: 72) md += "|" + 18 * "-" + "|" + 48 * "-" + "|" + 6 * "-" + "|\n" for name, d in wcs.items(): if d is None: d = {} if 'real' not in d or not d['real']: t = 'C' else: t = 'R' md += "| `{}` | ${}$ | {} |\n".format(name, d.get('tex', '~'), t) return md def __str__(self): md = self._repr_markdown_() md += self._markdown_tables() return md class WC(WCxf): """Class representing Wilson coefficient files.""" def __init__(self, eft, basis, scale, values, **kwargs): """Instantiate the Wilson coefficient file object.""" self.eft = eft self.basis = basis self.scale = float(scale) self.values = values self._dict = None self._df = None self._hash = hash(random.random()) super().__init__() for k, v in kwargs.items(): setattr(self, k, v) def __hash__(self): return self._hash @staticmethod def _to_number(v): """Turn a Wilson coefficient value - that could be a number or a Re/Im dict - into a number.""" if isinstance(v, dict): return float(v.get('Re', 0)) + 1j*float(v.get('Im', 0)) else: return float(v) @staticmethod def _to_complex_dict(v): """Turn a numeric Wilson coefficient value into a Re/Im dict if it is complex.""" if v.imag != 0: return {'Re': float(v.real), 'Im': float(v.imag)} else: return float(v.real) @classmethod def dict2values(cls, d): return {k: cls._to_complex_dict(v) for k, v in d.items()} def __getitem__(self, key): try: return self.dict[key] except KeyError: return 0 def validate(self): """Validate the Wilson coefficient file.""" try: eft_instance = EFT[self.eft] except (AttributeError, KeyError): raise ValueError(f"EFT {self.eft} not defined") try: basis_instance = Basis[self.eft, self.basis] except (AttributeError, KeyError): raise ValueError(f"Basis {self.basis} not defined for EFT {self.eft}") unknown_keys = set(self.values.keys()) - set(basis_instance.all_wcs) assert unknown_keys == set(), \ "Wilson coefficients do not exist in this basis: " + str(unknown_keys) @property def dict(self): """Return a dictionary with the Wilson coefficient values. The dictionary will be cached when called for the first time.""" if self._dict is None: self._dict = {k: self._to_number(v) for k, v in self.values.items()} return self._dict @property def df(self): """Return a pandas.DataFrame with the Wilson coefficient values split by real and imaginary part. The DataFrame will be cached when called for the first time.""" if self._df is None: re = [self.dict[k].real for k in self.values] im = [self.dict[k].imag for k in self.values] self._df = DataFrame({'Re': re, 'Im': im}, index=self.values, columns=('Re', 'Im')) return self._df def __repr__(self): return ("wcxf.WC(eft='{}', basis='{}', scale='{}', values={{...}})" .format(self.eft, self.basis, self.scale)) def __str__(self): return self._repr_markdown_() def _repr_markdown_(self): md = "## WCxf Wilson coefficients\n\n" md += f"**EFT:** `{self.eft}`\n\n" md += f"**Basis:** `{self.basis}`\n\n" md += f"**Scale:** {self.scale} GeV\n\n" md += "### Values\n\n" md += "| WC name | Value |\n" # NB: this is meant for pandoc; it computes column widths # in latex by counting the number of "-" separators as # fractions of line width (default: 72) md += "|" + 20 * "-" + "|" + 52 * "-" + "|\n" for name, v in self.dict.items(): md += f"| `{name}` | {v} |\n" return md def _repr_html_(self): html = "<h3>WCxf Wilson coefficients</h3>\n\n" html += """<table> <thead> <tr> <th>EFT</th> <th>Basis</th> <th>scale</th> </tr> </thead> <tbody> <tr> <td><code>{}</code></td> <td><code>{}</code></td> <td>{} GeV</td> </tr> </tbody> </table> """.format(self.eft, self.basis, self.scale) html += "<h4>Values</h4>\n\n" html += self.df._repr_html_() return html def translate(self, to_basis, parameters=None, sectors=None): """Translate the Wilson coefficients to a different basis. Returns a WC instance. Parameters: - `to_basis`: name of output basis - parameters: an optional dictionary of parameters specific to the translation function - sectors: an optional iterable of sector names of interest that the translator function may choose (but is not obliged) to limit itself to in the output.""" if to_basis == self.basis: return self # nothing to do try: translator = Translator[self.eft, self.basis, to_basis] except (KeyError, AttributeError): raise ValueError(f"No translator from basis {self.basis} to {to_basis} found.") return translator.translate(self, parameters=parameters, sectors=sectors) def match(self, to_eft, to_basis, parameters=None): """Match the Wilson coefficients to a different EFT. Returns a WC instance.""" if to_eft == self.eft and to_basis == self.basis: return self # nothing to do try: matcher = Matcher[self.eft, self.basis, to_eft, to_basis] except (KeyError, AttributeError): raise ValueError(f"No matcher from EFT {self.eft} in basis {self.basis} to EFT {to_eft} in basis {to_basis} found.") return matcher.match(self, parameters=parameters) class Translator(NamedInstanceClass): """Class for translating between different bases of the same EFT.""" def __init__(self, eft, from_basis, to_basis, function): """Initialize the Translator instance.""" super().__init__((eft, from_basis, to_basis)) self.eft = eft self.from_basis = from_basis self.to_basis = to_basis self.function = function def translate(self, WC_in, parameters=None, sectors=None): r"""Translate a WC object from `from_basis` to `to_basis`. Parameters: - `WC_in`: the input `WC` instance - parameters: an optional dictionary of parameters specific to the translation function - sectors: an optional iterable of sector names of interest that the translator function may choose (but is not obliged) to limit itself to in the output.""" if sectors is None: dict_out = self.function(WC_in.dict, WC_in.scale, parameters) else: dict_out = self.function(WC_in.dict, WC_in.scale, parameters, sectors=sectors) # filter out zero values dict_out = {k: v for k, v in dict_out.items() if v != 0} values = WC.dict2values(dict_out) WC_out = WC(self.eft, self.to_basis, WC_in.scale, values) return WC_out class Matcher(NamedInstanceClass): """Class for matching from a UV to an IR EFT.""" def __init__(self, from_eft, from_basis, to_eft, to_basis, function): """Initialize the Matcher instance.""" super().__init__((from_eft, from_basis, to_eft, to_basis)) self.from_eft = from_eft self.from_basis = from_basis self.to_eft = to_eft self.to_basis = to_basis self.function = function def match(self, WC_in, parameters=None): """Translate a WC object in EFT `from_eft` and basis `from_basis` to EFT `to_eft` and basis `to_basis`.""" dict_out = self.function(WC_in.dict, WC_in.scale, parameters) # filter out zero values dict_out = {k: v for k, v in dict_out.items() if v != 0} values = WC.dict2values(dict_out) WC_out = WC(self.to_eft, self.to_basis, WC_in.scale, values) return WC_out def parametrized(dec): """Decorator for a decorator allowing it to take arguments. See https://stackoverflow.com/a/26151604.""" def layer(*args, **kwargs): def repl(f): return dec(f, *args, **kwargs) return repl return layer @parametrized def translator(func, eft, from_basis, to_basis): """Decorator for basis translation functions. Usage: ```python @translator('myEFT', 'myBasis_from', 'myBasis_to') def myFunction(wc_dict_from): ... # do something return wc_dict_to ``` """ Translator(eft, from_basis, to_basis, func) return func @parametrized def matcher(func, from_eft, from_basis, to_eft, to_basis): """Decorator for matching functions. Usage: ```python @matcher('myEFT_from', 'myBasis_from', 'myEFT_to', 'myBasis_to') def myFunction(wc_dict_from): ... # do something return wc_dict_to ``` """ Matcher(from_eft, from_basis, to_eft, to_basis, func) return func
Functions
def matcher(
*args, **kwargs)
def layer(*args, **kwargs): def repl(f): return dec(f, *args, **kwargs) return repl
def parametrized(
dec)
Decorator for a decorator allowing it to take arguments. See https://stackoverflow.com/a/26151604.
def parametrized(dec): """Decorator for a decorator allowing it to take arguments. See https://stackoverflow.com/a/26151604.""" def layer(*args, **kwargs): def repl(f): return dec(f, *args, **kwargs) return repl return layer
def represent_none(
self, _)
def represent_none(self, _): return self.represent_scalar('tag:yaml.org,2002:null', '')
def translator(
*args, **kwargs)
def layer(*args, **kwargs): def repl(f): return dec(f, *args, **kwargs) return repl
Classes
class Basis
Class representing basis files.
class Basis(WCxf, NamedInstanceClass): """Class representing basis files.""" def __init__(self, eft, basis, sectors, **kwargs): """Instantiate the basis file object.""" self.eft = eft self.basis = basis super().__init__((eft, basis)) for k, v in kwargs.items(): setattr(self, k, v) if hasattr(self, 'parent'): try: self.sectors = Basis[self.eft, self.parent].sectors.copy() self.sectors.update(sectors) except (AttributeError, KeyError): raise ValueError(f"Parent basis {self.parent} not found") else: self.sectors = sectors self._all_wcs = None @property def known_translators(self): """Return a list of known translators to and from this basis.""" kt = {} kt['from'] = tuple(t for t in Translator.instances if t[0] == self.eft and t[1] == self.basis) kt['to'] = tuple(t for t in Translator.instances if t[0] == self.eft and t[2] == self.basis) return kt @property def all_wcs(self): """Return a list with all Wilson coefficients defined in this basis.""" if self._all_wcs is None: self._all_wcs = [wc for sector, wcs in self.sectors.items() for wc in wcs] return self._all_wcs def validate(self): """Validate the basis file.""" try: eft_instance = EFT[self.eft] except (AttributeError, KeyError): raise ValueError(f"EFT {self.eft} not defined") unknown_sectors = set(self.sectors.keys()) - set(eft_instance.sectors.keys()) if unknown_sectors: raise ValueError(f"Unknown sectors: {unknown_sectors}") all_keys = [k for s in self.sectors.values() for k, v in s.items()] if len(all_keys) != len(set(all_keys)): # we have duplicate keys! cnt = Counter(all_keys) dupes = [k for k, v in cnt.items() if v > 1] raise ValueError("Duplicate coefficients in different sectors:" " {}".format(dupes)) # check for LaTeX errors # string with all tex values alltex = '${}$'.format('$, $'.join([d.get('tex', '~') for c in self.sectors.values() for d in c.values() if d is not None])) res = _testtex(alltex) if not res['success']: raise ValueError("Validation of basis {}/{}: " .format(self.eft, self.basis) + "LaTeX compilation errors encountered:\n" + "{}".format(res['log'])) def __repr__(self): return f"wcxf.Basis('{self.eft}', '{self.basis}', {{...}})" def _repr_markdown_(self): md = f"# Basis `{self.basis}` (EFT `{self.eft}`)\n\n" if hasattr(self, 'metadata') and 'description' in self.metadata: md += self.metadata['description'] + "\n\n" return md def _markdown_tables(self): md = "## Sectors\n\n" md += "The effective Lagrangian is defined as\n" # general definition of Leff according to WCxf papaer md += (r"$$\mathcal L_\text{eff} = -\mathcal H_\text{eff} =" r"\sum_{O_i= O_i^\dagger} C_i \, O_i + \sum_{O_i\neq O_i^\dagger}" r"\left( C_i \, O_i + C^*_i \, O^\dagger_i\right).$$") md += "\n\n" for s, wcs in self.sectors.items(): md += f"### `{s}`\n\n" if wcs: md += "| WC name | Operator | Type |\n" # NB: this is meant for pandoc; it computes column widths # in latex by counting the number of "-" separators as # fractions of line width (default: 72) md += "|" + 18 * "-" + "|" + 48 * "-" + "|" + 6 * "-" + "|\n" for name, d in wcs.items(): if d is None: d = {} if 'real' not in d or not d['real']: t = 'C' else: t = 'R' md += "| `{}` | ${}$ | {} |\n".format(name, d.get('tex', '~'), t) return md def __str__(self): md = self._repr_markdown_() md += self._markdown_tables() return md
Ancestors (in MRO)
- Basis
- WCxf
- NamedInstanceClass
- builtins.object
Class variables
var instances
Static methods
def __init__(
self, eft, basis, sectors, **kwargs)
Instantiate the basis file object.
def __init__(self, eft, basis, sectors, **kwargs): """Instantiate the basis file object.""" self.eft = eft self.basis = basis super().__init__((eft, basis)) for k, v in kwargs.items(): setattr(self, k, v) if hasattr(self, 'parent'): try: self.sectors = Basis[self.eft, self.parent].sectors.copy() self.sectors.update(sectors) except (AttributeError, KeyError): raise ValueError(f"Parent basis {self.parent} not found") else: self.sectors = sectors self._all_wcs = None
def dump(
self, stream=None, fmt='json', **kwargs)
Dump the object data to a JSON or YAML file.
Optional arguments:
stream
: if None (default), return a string. Otherwise, should be a writable file-like objectfmt
: format, should be 'json' (default) or 'yaml'
Additional keyword arguments will be passed to the json.dump(s)
or yaml.dump
methods.
def dump(self, stream=None, fmt='json', **kwargs): """Dump the object data to a JSON or YAML file. Optional arguments: - `stream`: if None (default), return a string. Otherwise, should be a writable file-like object - `fmt`: format, should be 'json' (default) or 'yaml' Additional keyword arguments will be passed to the `json.dump(s)` or `yaml.dump` methods. """ d = {k: v for k,v in self.__dict__.items() if k[0] != '_'} if fmt.lower() == 'json': # set indent=2 unless specified otherwise indent = kwargs.pop('indent', 2) return _dump_json(d, stream=stream, indent=indent, **kwargs) elif fmt.lower() == 'yaml': # set default_flow_style=False unless specified otherwise default_flow_style = kwargs.pop('default_flow_style', False) return yaml.dump(d, stream, default_flow_style=default_flow_style, **kwargs) else: raise ValueError(f"Format {fmt} unknown: use 'json' or 'yaml'.")
def validate(
self)
Validate the basis file.
def validate(self): """Validate the basis file.""" try: eft_instance = EFT[self.eft] except (AttributeError, KeyError): raise ValueError(f"EFT {self.eft} not defined") unknown_sectors = set(self.sectors.keys()) - set(eft_instance.sectors.keys()) if unknown_sectors: raise ValueError(f"Unknown sectors: {unknown_sectors}") all_keys = [k for s in self.sectors.values() for k, v in s.items()] if len(all_keys) != len(set(all_keys)): # we have duplicate keys! cnt = Counter(all_keys) dupes = [k for k, v in cnt.items() if v > 1] raise ValueError("Duplicate coefficients in different sectors:" " {}".format(dupes)) # check for LaTeX errors # string with all tex values alltex = '${}$'.format('$, $'.join([d.get('tex', '~') for c in self.sectors.values() for d in c.values() if d is not None])) res = _testtex(alltex) if not res['success']: raise ValueError("Validation of basis {}/{}: " .format(self.eft, self.basis) + "LaTeX compilation errors encountered:\n" + "{}".format(res['log']))
Instance variables
var all_wcs
Return a list with all Wilson coefficients defined in this basis.
var basis
var eft
var known_translators
Return a list of known translators to and from this basis.
Methods
def clear_all(
cls)
Delete all instances.
@classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
def del_instance(
cls, _name)
@classmethod def del_instance(cls, _name): del cls.instances[_name]
def get_instance(
cls, _name)
@classmethod def get_instance(cls, _name): return cls.instances[_name]
def load(
cls, stream, **kwargs)
Load the object data from a JSON or YAML file.
@classmethod def load(cls, stream, **kwargs): """Load the object data from a JSON or YAML file.""" wcxf = _load_yaml_json(stream, **kwargs) return cls(**wcxf)
class EFT
Class representing EFT files.
class EFT(WCxf, NamedInstanceClass): """Class representing EFT files.""" def __init__(self, eft, sectors, **kwargs): """Instantiate the EFT file object.""" self.eft = eft self.sectors = sectors super().__init__(eft) for k, v in kwargs.items(): setattr(self, k, v) @property def known_bases(self): """Return a list of known bases for this EFT.""" return tuple(basis[1] for basis in Basis.instances if basis[0] == self.eft) @property def known_translators(self): """Return a list of known translators between bases of this EFT.""" return tuple(t for t in Translator.instances if t[0] == self.eft)
Ancestors (in MRO)
- EFT
- WCxf
- NamedInstanceClass
- builtins.object
Class variables
var instances
Static methods
def __init__(
self, eft, sectors, **kwargs)
Instantiate the EFT file object.
def __init__(self, eft, sectors, **kwargs): """Instantiate the EFT file object.""" self.eft = eft self.sectors = sectors super().__init__(eft) for k, v in kwargs.items(): setattr(self, k, v)
def dump(
self, stream=None, fmt='json', **kwargs)
Dump the object data to a JSON or YAML file.
Optional arguments:
stream
: if None (default), return a string. Otherwise, should be a writable file-like objectfmt
: format, should be 'json' (default) or 'yaml'
Additional keyword arguments will be passed to the json.dump(s)
or yaml.dump
methods.
def dump(self, stream=None, fmt='json', **kwargs): """Dump the object data to a JSON or YAML file. Optional arguments: - `stream`: if None (default), return a string. Otherwise, should be a writable file-like object - `fmt`: format, should be 'json' (default) or 'yaml' Additional keyword arguments will be passed to the `json.dump(s)` or `yaml.dump` methods. """ d = {k: v for k,v in self.__dict__.items() if k[0] != '_'} if fmt.lower() == 'json': # set indent=2 unless specified otherwise indent = kwargs.pop('indent', 2) return _dump_json(d, stream=stream, indent=indent, **kwargs) elif fmt.lower() == 'yaml': # set default_flow_style=False unless specified otherwise default_flow_style = kwargs.pop('default_flow_style', False) return yaml.dump(d, stream, default_flow_style=default_flow_style, **kwargs) else: raise ValueError(f"Format {fmt} unknown: use 'json' or 'yaml'.")
Instance variables
var eft
var known_bases
Return a list of known bases for this EFT.
var known_translators
Return a list of known translators between bases of this EFT.
var sectors
Methods
def clear_all(
cls)
Delete all instances.
@classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
def del_instance(
cls, _name)
@classmethod def del_instance(cls, _name): del cls.instances[_name]
def get_instance(
cls, _name)
@classmethod def get_instance(cls, _name): return cls.instances[_name]
def load(
cls, stream, **kwargs)
Load the object data from a JSON or YAML file.
@classmethod def load(cls, stream, **kwargs): """Load the object data from a JSON or YAML file.""" wcxf = _load_yaml_json(stream, **kwargs) return cls(**wcxf)
class Matcher
Class for matching from a UV to an IR EFT.
class Matcher(NamedInstanceClass): """Class for matching from a UV to an IR EFT.""" def __init__(self, from_eft, from_basis, to_eft, to_basis, function): """Initialize the Matcher instance.""" super().__init__((from_eft, from_basis, to_eft, to_basis)) self.from_eft = from_eft self.from_basis = from_basis self.to_eft = to_eft self.to_basis = to_basis self.function = function def match(self, WC_in, parameters=None): """Translate a WC object in EFT `from_eft` and basis `from_basis` to EFT `to_eft` and basis `to_basis`.""" dict_out = self.function(WC_in.dict, WC_in.scale, parameters) # filter out zero values dict_out = {k: v for k, v in dict_out.items() if v != 0} values = WC.dict2values(dict_out) WC_out = WC(self.to_eft, self.to_basis, WC_in.scale, values) return WC_out
Ancestors (in MRO)
- Matcher
- NamedInstanceClass
- builtins.object
Class variables
var instances
Static methods
def __init__(
self, from_eft, from_basis, to_eft, to_basis, function)
Initialize the Matcher instance.
def __init__(self, from_eft, from_basis, to_eft, to_basis, function): """Initialize the Matcher instance.""" super().__init__((from_eft, from_basis, to_eft, to_basis)) self.from_eft = from_eft self.from_basis = from_basis self.to_eft = to_eft self.to_basis = to_basis self.function = function
def match(
self, WC_in, parameters=None)
Translate a WC object in EFT from_eft
and basis from_basis
to EFT to_eft
and basis to_basis
.
def match(self, WC_in, parameters=None): """Translate a WC object in EFT `from_eft` and basis `from_basis` to EFT `to_eft` and basis `to_basis`.""" dict_out = self.function(WC_in.dict, WC_in.scale, parameters) # filter out zero values dict_out = {k: v for k, v in dict_out.items() if v != 0} values = WC.dict2values(dict_out) WC_out = WC(self.to_eft, self.to_basis, WC_in.scale, values) return WC_out
Instance variables
var from_basis
var from_eft
var function
var to_basis
var to_eft
Methods
def clear_all(
cls)
Delete all instances.
@classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
def del_instance(
cls, _name)
@classmethod def del_instance(cls, _name): del cls.instances[_name]
def get_instance(
cls, _name)
@classmethod def get_instance(cls, _name): return cls.instances[_name]
class NamedInstanceClass
Base class for classes that have named instances that can be accessed by their name.
Parameters
- name: string
Methods
- del_instance(name) Delete an instance
- get_instance(name) Get an instance
- set_description(description) Set the description
class NamedInstanceClass(metaclass=NamedInstanceMetaclass): """Base class for classes that have named instances that can be accessed by their name. Parameters ---------- - name: string Methods ------- - del_instance(name) Delete an instance - get_instance(name) Get an instance - set_description(description) Set the description """ def __init__(self, _name): if not hasattr(self.__class__, 'instances'): self.__class__.instances = OrderedDict() self.__class__.instances[_name] = self self._name = _name @classmethod def get_instance(cls, _name): return cls.instances[_name] @classmethod def del_instance(cls, _name): del cls.instances[_name] @classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
Ancestors (in MRO)
- NamedInstanceClass
- builtins.object
Static methods
def __init__(
self, _name)
Initialize self. See help(type(self)) for accurate signature.
def __init__(self, _name): if not hasattr(self.__class__, 'instances'): self.__class__.instances = OrderedDict() self.__class__.instances[_name] = self self._name = _name
Methods
def clear_all(
cls)
Delete all instances.
@classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
def del_instance(
cls, _name)
@classmethod def del_instance(cls, _name): del cls.instances[_name]
def get_instance(
cls, _name)
@classmethod def get_instance(cls, _name): return cls.instances[_name]
class NamedInstanceMetaclass
type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type
class NamedInstanceMetaclass(type): # this is just needed to implement the getitem method on NamedInstanceClass # to allow the syntax MyClass['instancename'] as shorthand for # MyClass.get_instance('instancename'); same for # del MyClass['instancename'] instead of MyClass.del_instance('instancename') def __getitem__(cls, item): return cls.get_instance(item) def __delitem__(cls, item): return cls.del_instance(item)
Ancestors (in MRO)
- NamedInstanceMetaclass
- builtins.type
- builtins.object
class Translator
Class for translating between different bases of the same EFT.
class Translator(NamedInstanceClass): """Class for translating between different bases of the same EFT.""" def __init__(self, eft, from_basis, to_basis, function): """Initialize the Translator instance.""" super().__init__((eft, from_basis, to_basis)) self.eft = eft self.from_basis = from_basis self.to_basis = to_basis self.function = function def translate(self, WC_in, parameters=None, sectors=None): r"""Translate a WC object from `from_basis` to `to_basis`. Parameters: - `WC_in`: the input `WC` instance - parameters: an optional dictionary of parameters specific to the translation function - sectors: an optional iterable of sector names of interest that the translator function may choose (but is not obliged) to limit itself to in the output.""" if sectors is None: dict_out = self.function(WC_in.dict, WC_in.scale, parameters) else: dict_out = self.function(WC_in.dict, WC_in.scale, parameters, sectors=sectors) # filter out zero values dict_out = {k: v for k, v in dict_out.items() if v != 0} values = WC.dict2values(dict_out) WC_out = WC(self.eft, self.to_basis, WC_in.scale, values) return WC_out
Ancestors (in MRO)
- Translator
- NamedInstanceClass
- builtins.object
Class variables
var instances
Static methods
def __init__(
self, eft, from_basis, to_basis, function)
Initialize the Translator instance.
def __init__(self, eft, from_basis, to_basis, function): """Initialize the Translator instance.""" super().__init__((eft, from_basis, to_basis)) self.eft = eft self.from_basis = from_basis self.to_basis = to_basis self.function = function
def translate(
self, WC_in, parameters=None, sectors=None)
Translate a WC object from from_basis
to to_basis
.
Parameters:
- WC_in
: the input WC
instance
- parameters: an optional dictionary of parameters specific to the
translation function
- sectors: an optional iterable of sector names of interest that the
translator function may choose (but is not obliged) to limit itself
to in the output.
def translate(self, WC_in, parameters=None, sectors=None): r"""Translate a WC object from `from_basis` to `to_basis`. Parameters: - `WC_in`: the input `WC` instance - parameters: an optional dictionary of parameters specific to the translation function - sectors: an optional iterable of sector names of interest that the translator function may choose (but is not obliged) to limit itself to in the output.""" if sectors is None: dict_out = self.function(WC_in.dict, WC_in.scale, parameters) else: dict_out = self.function(WC_in.dict, WC_in.scale, parameters, sectors=sectors) # filter out zero values dict_out = {k: v for k, v in dict_out.items() if v != 0} values = WC.dict2values(dict_out) WC_out = WC(self.eft, self.to_basis, WC_in.scale, values) return WC_out
Instance variables
var eft
var from_basis
var function
var to_basis
Methods
def clear_all(
cls)
Delete all instances.
@classmethod def clear_all(cls): """Delete all instances.""" cls.instances = OrderedDict()
def del_instance(
cls, _name)
@classmethod def del_instance(cls, _name): del cls.instances[_name]
def get_instance(
cls, _name)
@classmethod def get_instance(cls, _name): return cls.instances[_name]
class WC
Class representing Wilson coefficient files.
class WC(WCxf): """Class representing Wilson coefficient files.""" def __init__(self, eft, basis, scale, values, **kwargs): """Instantiate the Wilson coefficient file object.""" self.eft = eft self.basis = basis self.scale = float(scale) self.values = values self._dict = None self._df = None self._hash = hash(random.random()) super().__init__() for k, v in kwargs.items(): setattr(self, k, v) def __hash__(self): return self._hash @staticmethod def _to_number(v): """Turn a Wilson coefficient value - that could be a number or a Re/Im dict - into a number.""" if isinstance(v, dict): return float(v.get('Re', 0)) + 1j*float(v.get('Im', 0)) else: return float(v) @staticmethod def _to_complex_dict(v): """Turn a numeric Wilson coefficient value into a Re/Im dict if it is complex.""" if v.imag != 0: return {'Re': float(v.real), 'Im': float(v.imag)} else: return float(v.real) @classmethod def dict2values(cls, d): return {k: cls._to_complex_dict(v) for k, v in d.items()} def __getitem__(self, key): try: return self.dict[key] except KeyError: return 0 def validate(self): """Validate the Wilson coefficient file.""" try: eft_instance = EFT[self.eft] except (AttributeError, KeyError): raise ValueError(f"EFT {self.eft} not defined") try: basis_instance = Basis[self.eft, self.basis] except (AttributeError, KeyError): raise ValueError(f"Basis {self.basis} not defined for EFT {self.eft}") unknown_keys = set(self.values.keys()) - set(basis_instance.all_wcs) assert unknown_keys == set(), \ "Wilson coefficients do not exist in this basis: " + str(unknown_keys) @property def dict(self): """Return a dictionary with the Wilson coefficient values. The dictionary will be cached when called for the first time.""" if self._dict is None: self._dict = {k: self._to_number(v) for k, v in self.values.items()} return self._dict @property def df(self): """Return a pandas.DataFrame with the Wilson coefficient values split by real and imaginary part. The DataFrame will be cached when called for the first time.""" if self._df is None: re = [self.dict[k].real for k in self.values] im = [self.dict[k].imag for k in self.values] self._df = DataFrame({'Re': re, 'Im': im}, index=self.values, columns=('Re', 'Im')) return self._df def __repr__(self): return ("wcxf.WC(eft='{}', basis='{}', scale='{}', values={{...}})" .format(self.eft, self.basis, self.scale)) def __str__(self): return self._repr_markdown_() def _repr_markdown_(self): md = "## WCxf Wilson coefficients\n\n" md += f"**EFT:** `{self.eft}`\n\n" md += f"**Basis:** `{self.basis}`\n\n" md += f"**Scale:** {self.scale} GeV\n\n" md += "### Values\n\n" md += "| WC name | Value |\n" # NB: this is meant for pandoc; it computes column widths # in latex by counting the number of "-" separators as # fractions of line width (default: 72) md += "|" + 20 * "-" + "|" + 52 * "-" + "|\n" for name, v in self.dict.items(): md += f"| `{name}` | {v} |\n" return md def _repr_html_(self): html = "<h3>WCxf Wilson coefficients</h3>\n\n" html += """<table> <thead> <tr> <th>EFT</th> <th>Basis</th> <th>scale</th> </tr> </thead> <tbody> <tr> <td><code>{}</code></td> <td><code>{}</code></td> <td>{} GeV</td> </tr> </tbody> </table> """.format(self.eft, self.basis, self.scale) html += "<h4>Values</h4>\n\n" html += self.df._repr_html_() return html def translate(self, to_basis, parameters=None, sectors=None): """Translate the Wilson coefficients to a different basis. Returns a WC instance. Parameters: - `to_basis`: name of output basis - parameters: an optional dictionary of parameters specific to the translation function - sectors: an optional iterable of sector names of interest that the translator function may choose (but is not obliged) to limit itself to in the output.""" if to_basis == self.basis: return self # nothing to do try: translator = Translator[self.eft, self.basis, to_basis] except (KeyError, AttributeError): raise ValueError(f"No translator from basis {self.basis} to {to_basis} found.") return translator.translate(self, parameters=parameters, sectors=sectors) def match(self, to_eft, to_basis, parameters=None): """Match the Wilson coefficients to a different EFT. Returns a WC instance.""" if to_eft == self.eft and to_basis == self.basis: return self # nothing to do try: matcher = Matcher[self.eft, self.basis, to_eft, to_basis] except (KeyError, AttributeError): raise ValueError(f"No matcher from EFT {self.eft} in basis {self.basis} to EFT {to_eft} in basis {to_basis} found.") return matcher.match(self, parameters=parameters)
Ancestors (in MRO)
Static methods
def __init__(
self, eft, basis, scale, values, **kwargs)
Instantiate the Wilson coefficient file object.
def __init__(self, eft, basis, scale, values, **kwargs): """Instantiate the Wilson coefficient file object.""" self.eft = eft self.basis = basis self.scale = float(scale) self.values = values self._dict = None self._df = None self._hash = hash(random.random()) super().__init__() for k, v in kwargs.items(): setattr(self, k, v)
def dump(
self, stream=None, fmt='json', **kwargs)
Dump the object data to a JSON or YAML file.
Optional arguments:
stream
: if None (default), return a string. Otherwise, should be a writable file-like objectfmt
: format, should be 'json' (default) or 'yaml'
Additional keyword arguments will be passed to the json.dump(s)
or yaml.dump
methods.
def dump(self, stream=None, fmt='json', **kwargs): """Dump the object data to a JSON or YAML file. Optional arguments: - `stream`: if None (default), return a string. Otherwise, should be a writable file-like object - `fmt`: format, should be 'json' (default) or 'yaml' Additional keyword arguments will be passed to the `json.dump(s)` or `yaml.dump` methods. """ d = {k: v for k,v in self.__dict__.items() if k[0] != '_'} if fmt.lower() == 'json': # set indent=2 unless specified otherwise indent = kwargs.pop('indent', 2) return _dump_json(d, stream=stream, indent=indent, **kwargs) elif fmt.lower() == 'yaml': # set default_flow_style=False unless specified otherwise default_flow_style = kwargs.pop('default_flow_style', False) return yaml.dump(d, stream, default_flow_style=default_flow_style, **kwargs) else: raise ValueError(f"Format {fmt} unknown: use 'json' or 'yaml'.")
def match(
self, to_eft, to_basis, parameters=None)
Match the Wilson coefficients to a different EFT. Returns a WC instance.
def match(self, to_eft, to_basis, parameters=None): """Match the Wilson coefficients to a different EFT. Returns a WC instance.""" if to_eft == self.eft and to_basis == self.basis: return self # nothing to do try: matcher = Matcher[self.eft, self.basis, to_eft, to_basis] except (KeyError, AttributeError): raise ValueError(f"No matcher from EFT {self.eft} in basis {self.basis} to EFT {to_eft} in basis {to_basis} found.") return matcher.match(self, parameters=parameters)
def translate(
self, to_basis, parameters=None, sectors=None)
Translate the Wilson coefficients to a different basis. Returns a WC instance.
Parameters:
- to_basis
: name of output basis
- parameters: an optional dictionary of parameters specific to the
translation function
- sectors: an optional iterable of sector names of interest that the
translator function may choose (but is not obliged) to limit itself
to in the output.
def translate(self, to_basis, parameters=None, sectors=None): """Translate the Wilson coefficients to a different basis. Returns a WC instance. Parameters: - `to_basis`: name of output basis - parameters: an optional dictionary of parameters specific to the translation function - sectors: an optional iterable of sector names of interest that the translator function may choose (but is not obliged) to limit itself to in the output.""" if to_basis == self.basis: return self # nothing to do try: translator = Translator[self.eft, self.basis, to_basis] except (KeyError, AttributeError): raise ValueError(f"No translator from basis {self.basis} to {to_basis} found.") return translator.translate(self, parameters=parameters, sectors=sectors)
def validate(
self)
Validate the Wilson coefficient file.
def validate(self): """Validate the Wilson coefficient file.""" try: eft_instance = EFT[self.eft] except (AttributeError, KeyError): raise ValueError(f"EFT {self.eft} not defined") try: basis_instance = Basis[self.eft, self.basis] except (AttributeError, KeyError): raise ValueError(f"Basis {self.basis} not defined for EFT {self.eft}") unknown_keys = set(self.values.keys()) - set(basis_instance.all_wcs) assert unknown_keys == set(), \ "Wilson coefficients do not exist in this basis: " + str(unknown_keys)
Instance variables
var basis
var df
Return a pandas.DataFrame with the Wilson coefficient values split by real and imaginary part. The DataFrame will be cached when called for the first time.
var dict
Return a dictionary with the Wilson coefficient values. The dictionary will be cached when called for the first time.
var eft
var scale
var values
Methods
def dict2values(
cls, d)
@classmethod def dict2values(cls, d): return {k: cls._to_complex_dict(v) for k, v in d.items()}
def load(
cls, stream, **kwargs)
Load the object data from a JSON or YAML file.
@classmethod def load(cls, stream, **kwargs): """Load the object data from a JSON or YAML file.""" wcxf = _load_yaml_json(stream, **kwargs) return cls(**wcxf)
class WCxf
Base class for WCxf files (not meant to be used directly).
class WCxf: """Base class for WCxf files (not meant to be used directly).""" @classmethod def load(cls, stream, **kwargs): """Load the object data from a JSON or YAML file.""" wcxf = _load_yaml_json(stream, **kwargs) return cls(**wcxf) def dump(self, stream=None, fmt='json', **kwargs): """Dump the object data to a JSON or YAML file. Optional arguments: - `stream`: if None (default), return a string. Otherwise, should be a writable file-like object - `fmt`: format, should be 'json' (default) or 'yaml' Additional keyword arguments will be passed to the `json.dump(s)` or `yaml.dump` methods. """ d = {k: v for k,v in self.__dict__.items() if k[0] != '_'} if fmt.lower() == 'json': # set indent=2 unless specified otherwise indent = kwargs.pop('indent', 2) return _dump_json(d, stream=stream, indent=indent, **kwargs) elif fmt.lower() == 'yaml': # set default_flow_style=False unless specified otherwise default_flow_style = kwargs.pop('default_flow_style', False) return yaml.dump(d, stream, default_flow_style=default_flow_style, **kwargs) else: raise ValueError(f"Format {fmt} unknown: use 'json' or 'yaml'.")
Ancestors (in MRO)
- WCxf
- builtins.object
Static methods
def dump(
self, stream=None, fmt='json', **kwargs)
Dump the object data to a JSON or YAML file.
Optional arguments:
stream
: if None (default), return a string. Otherwise, should be a writable file-like objectfmt
: format, should be 'json' (default) or 'yaml'
Additional keyword arguments will be passed to the json.dump(s)
or yaml.dump
methods.
def dump(self, stream=None, fmt='json', **kwargs): """Dump the object data to a JSON or YAML file. Optional arguments: - `stream`: if None (default), return a string. Otherwise, should be a writable file-like object - `fmt`: format, should be 'json' (default) or 'yaml' Additional keyword arguments will be passed to the `json.dump(s)` or `yaml.dump` methods. """ d = {k: v for k,v in self.__dict__.items() if k[0] != '_'} if fmt.lower() == 'json': # set indent=2 unless specified otherwise indent = kwargs.pop('indent', 2) return _dump_json(d, stream=stream, indent=indent, **kwargs) elif fmt.lower() == 'yaml': # set default_flow_style=False unless specified otherwise default_flow_style = kwargs.pop('default_flow_style', False) return yaml.dump(d, stream, default_flow_style=default_flow_style, **kwargs) else: raise ValueError(f"Format {fmt} unknown: use 'json' or 'yaml'.")
Methods
def load(
cls, stream, **kwargs)
Load the object data from a JSON or YAML file.
@classmethod def load(cls, stream, **kwargs): """Load the object data from a JSON or YAML file.""" wcxf = _load_yaml_json(stream, **kwargs) return cls(**wcxf)