Source code for pyamptools.utility.load_parameters
import numpy as np
############################################################
# This file holds scripts to load in amptools information
############################################################
[docs]
class LoadParameters:
    """
    Class to extract amplitude parameters from an AmpTools FitResults or ConfigurationInfo object
       Parameters (like production coefficients) can then be formatted (complex -> real, imag) for input to other minimization algorithms
    """
    def __init__(self, cfg):
        self.load_cfg(cfg)
[docs]
    def load_cfg(self, cfg):  # cfg: [FitResults, ConfigurationInfo]
        """
        Get a map of unique (parameter: value) pairs excluding redundant constrained ones
            These include production parameters and amplitude parameters
        Args:
            cfg: If cfg is a FitResults object set values to fitted results. For a ConfigurationInfo object set values to initial values
        """
        ######## GET UNIQUE AMPLITUDES' PRODUCTION PARAMETERS ########
        results = None
        if hasattr(cfg, "configInfo"):  # input was a FitResults object
            # print("Input was a FitResults object. Take actions accordingly...")
            results, cfg = cfg, cfg.configInfo()
            ftype = "FitResults"
        elif hasattr(cfg, "constraintMap"):
            # print("Input was a ConfigurationInfo object. Take actions accordingly...")
            ftype = "ConfigurationInfo"  # input was a ConfigurationInfo object
        else:
            raise ValueError("Input must be a FitResults or ConfigurationInfo object")
        self.results = results
        self.cfg = cfg
        constraintMap = cfg.constraintMap()
        constraintIndex = {}
        uniqueProdPars = []
        allProdPars = set()
        i = 0
        for k, vs in constraintMap:
            allProdPars.add(k)
            if k not in constraintIndex and not cfg.amplitude(k).fixed():
                uniqueProdPars.append(k)
                constraintIndex[k] = i  # uniqueProdPars[i]
            for v in vs:
                if v not in constraintIndex and not cfg.amplitude(v).fixed():
                    # str(v) needed! appears that inserting v alone will go out of scope
                    constraintIndex[str(v)] = i  # uniqueProdPars[i]
            i += 1
        ######## GET VALUES / REALNESS OF UNIQUE AMPLITUDES' PRODUCTION PARAMETERS ########
        self.uniqueProdPars = {k: (results.ampProdParMap()[k] if ftype == "FitResults" else cfg.amplitude(k).value()) for k in uniqueProdPars}
        self.uniqueProdIsReal = {k: cfg.amplitude(k).real() for k in uniqueProdPars}  # check if amplitude is set to be real
        ####### GET AMPLITUDE PARAMETERS ########
        # parameters associated with amplitudes (i.e. masses, widths, etc)
        ampPars = cfg.parameterList()  # ParameterInfo*
        self.ampPars = {}
        for par in ampPars:
            if not par.fixed():
                self.ampPars[par.parName()] = results.ampParMap()[par.parName()] if ftype == "FitResults" else par.value()
        self.nAmpPars = len(ampPars)
        ####### MERGE DICTIONARIES ########
        self.params = self.uniqueProdPars | self.ampPars  # python 3.9 - merge dictionaries
        self.paramsIsReal = self.uniqueProdIsReal | {k: True for k in self.ampPars.keys()}  # all amp params are real
        ####### ADDITIONAL TRACKING #######
        self.allProdPars = list(allProdPars) 
[docs]
    def flatten_parameters(self, params={}):
        """
        Flatten amplitude parameters (complex-> real, imag) skipping imaginary parts of amplitudes fixed to be real. If no arguments are passed, use the uniqueProdPars and uniqueProdIsReal from the last call to load_cfg(). Can also format any dictionary pair into flat format.
            Dictionary to List
        Args:
            params (dict): dictionary of parameters to flatten
        Returns:
            parameters (list): list of flattened parameters
            keys (list): list of keys (parameter names) corresponding to parameters
            names (list): list of parameter names expanding complex parameters into Re[par] and Im[par]
        """
        parameters = []
        keys = []  # lose information on flatten, use key to keep track of provenance
        names = []  # track parameters in a prettier format
        if len(params) == 0:
            params = self.params
        for k, v in params.items():
            assert k in self.paramsIsReal.keys(), f"Parameter {k} not found in parameter list! Is parameter fixed?"
            pk = k.split("::")[-1]  # Production amplitudes take form "Reaction::Sum::Amp" grab Amp, pretty_k = pk
            real_value = v.real if k in self.uniqueProdPars else v
            names.append(f"Re[{pk}]" if k in self.uniqueProdPars else pk)
            parameters.append(real_value)
            # Production coefficients will have :: in their name. Normal parameters will not
            if "::" in k:
                keys.append(k + "_re")  # MUST match notation in AmpTools' parameterManager
            else:
                keys.append(k)
            # If parameters are complex, we will include their imaginary components
            if not self.paramsIsReal[k]:
                parameters.append(v.imag)
                keys.append(k + "_im")
                names.append(f"Im[{pk}]")
        self.keys = keys
        self.names = names
        self.parmameters = parameters
        return parameters, keys, names 
[docs]
    def unflatten_parameters(self, parameters, keys=[]):
        """
        Unflatten parameters forming complex values (for production parameters) when requested.
            List to Dictionary
        Args:
            parameters (list): list of flattened parameters. Arrays work also, ensure you loop over parameters (columns of the mcmc samples)
            keys (list): list of keys (parameter names) corresponding to parameters. When empty will load from last flatten_parameters call
        Returns:
            paramDict (dict): dictionary of parameters
        """
        if len(keys) == 0 and len(parameters) != 0 and len(parameters) == len(self.parmameters):
            print(f"Bool 1 {len(keys)==0 and len(parameters)!=0}")
            print(f"Bool 2 {len(parameters) == len(self.parmameters)} {len(parameters)} {len(self.parmameters)}")
            keys = self.keys
        paramDict = {}
        for k, param in zip(keys, parameters):
            amp = k[:-3]
            part = 1 if k[-3:] == "_re" else 1j
            if amp not in paramDict:
                paramDict[amp] = np.complex64(part * param)
            else:
                paramDict[amp] += np.complex64(part * param)
        for k in paramDict.keys():
            paramDict[k] = np.round(paramDict[k], 10)
        return paramDict 
 
def createMovesMixtureFromDict(moves_dict):
    """
    Creates a mixture of moves for the emcee sampler
    Args:
        moves_dict { move: {kwargs: {}, probability} }: Dictionary of moves and their kwargs and probability
    Returns:
        moves_mixture [ (emcee.moves.{move}(kwargs), probability) ]: List of tuples of moves (with kwargs pluggin in) and their probability
    """
    moves_mixture = []
    for move, moveDict in moves_dict.items():
        move = eval(f"emcee.moves.{move}")  # convert string to class
        kwargs = moveDict["kwargs"]
        prob = moveDict["prob"]
        moves_mixture.append((move(**kwargs), prob))
    return moves_mixture