Changeset 0edb6c1 in sasmodels for sasmodels/mixture.py


Ignore:
Timestamp:
Sep 12, 2017 3:12:03 AM (7 years ago)
Author:
lewis
Branches:
master, core_shell_microgels, costrafo411, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
9d1e3e4
Parents:
2ad5d30 (diff), 22e922e (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'ticket-767' of https://github.com/lewisodriscoll/sasmodels into ticket-767

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/mixture.py

    r765eb0e r0edb6c1  
    2525    pass 
    2626 
    27 def make_mixture_info(parts): 
     27def make_mixture_info(parts, operation='+'): 
    2828    # type: (List[ModelInfo]) -> ModelInfo 
    2929    """ 
    3030    Create info block for mixture model. 
    3131    """ 
    32     flatten = [] 
    33     for part in parts: 
    34         if part.composition and part.composition[0] == 'mixture': 
    35             flatten.extend(part.composition[1]) 
    36         else: 
    37             flatten.append(part) 
    38     parts = flatten 
    39  
    4032    # Build new parameter list 
    4133    combined_pars = [] 
    42     for k, part in enumerate(parts): 
     34 
     35    model_num = 0 
     36    all_parts = copy(parts) 
     37    is_flat = False 
     38    while not is_flat: 
     39        is_flat = True 
     40        for part in all_parts: 
     41            if part.composition and part.composition[0] == 'mixture' and \ 
     42                len(part.composition[1]) > 1: 
     43                all_parts += part.composition[1] 
     44                all_parts.remove(part) 
     45                is_flat = False 
     46 
     47    # When creating a mixture model that is a sum of product models (ie (1*2)+(3*4)) 
     48    # the parameters for models 1 & 2 will be prefixed with A & B respectively, 
     49    # but so will the parameters for models 3 & 4. We need to rename models 3 & 4 
     50    # so that they are prefixed with C & D to avoid overlap of parameter names. 
     51    used_prefixes = [] 
     52    for part in parts: 
     53        i = 0 
     54        if part.composition and part.composition[0] == 'mixture': 
     55            npars_list = [info.parameters.npars for info in part.composition[1]] 
     56            for npars in npars_list: 
     57                # List of params of one of the constituent models of part 
     58                submodel_pars = part.parameters.kernel_parameters[i:i+npars] 
     59                # Prefix of the constituent model 
     60                prefix = submodel_pars[0].name[0] 
     61                if prefix not in used_prefixes: # Haven't seen this prefix so far 
     62                    used_prefixes.append(prefix) 
     63                    i += npars 
     64                    continue 
     65                while prefix in used_prefixes: 
     66                    # This prefix has been already used, so change it to the 
     67                    # next letter that hasn't been used 
     68                    prefix = chr(ord(prefix) + 1) 
     69                used_prefixes.append(prefix) 
     70                prefix += "_" 
     71                # Update the parameters of this constituent model to use the 
     72                # new prefix 
     73                for par in submodel_pars: 
     74                    par.id = prefix + par.id[2:] 
     75                    par.name = prefix + par.name[2:] 
     76                    if par.length_control is not None: 
     77                        par.length_control = prefix + par.length_control[2:] 
     78                i += npars 
     79 
     80    for part in parts: 
    4381        # Parameter prefix per model, A_, B_, ... 
    4482        # Note that prefix must also be applied to id and length_control 
    4583        # to support vector parameters 
    46         prefix = chr(ord('A')+k) + '_' 
    47         scale =  Parameter(prefix+'scale', default=1.0, 
    48                            description="model intensity for " + part.name) 
    49         combined_pars.append(scale) 
     84        prefix = '' 
     85        if not part.composition: 
     86            # Model isn't a composition model, so it's parameters don't have a 
     87            # a prefix. Add the next available prefix 
     88            prefix = chr(ord('A')+len(used_prefixes)) 
     89            used_prefixes.append(prefix) 
     90            prefix += '_' 
     91             
     92        if operation == '+': 
     93            # If model is a sum model, each constituent model gets its own scale parameter 
     94            scale_prefix = prefix 
     95            if prefix == '' and part.operation == '*': 
     96                # `part` is a composition product model. Find the prefixes of 
     97                # it's parameters to form a new prefix for the scale, eg: 
     98                # a model with A*B*C will have ABC_scale 
     99                sub_prefixes = [] 
     100                for param in part.parameters.kernel_parameters: 
     101                    # Prefix of constituent model 
     102                    sub_prefix = param.id.split('_')[0] 
     103                    if sub_prefix not in sub_prefixes: 
     104                        sub_prefixes.append(sub_prefix) 
     105                # Concatenate sub_prefixes to form prefix for the scale 
     106                scale_prefix = ''.join(sub_prefixes) + '_' 
     107            scale =  Parameter(scale_prefix + 'scale', default=1.0, 
     108                            description="model intensity for " + part.name) 
     109            combined_pars.append(scale) 
    50110        for p in part.parameters.kernel_parameters: 
    51111            p = copy(p) 
     
    67127 
    68128    model_info = ModelInfo() 
    69     model_info.id = '+'.join(part.id for part in parts) 
    70     model_info.name = ' + '.join(part.name for part in parts) 
     129    model_info.id = operation.join(part.id for part in parts) 
     130    model_info.operation = operation 
     131    model_info.name = '(' + operation.join(part.name for part in parts) + ')' 
    71132    model_info.filename = None 
    72133    model_info.title = 'Mixture model with ' + model_info.name 
     
    121182        self.kernels = kernels 
    122183        self.dtype = self.kernels[0].dtype 
     184        self.operation = model_info.operation 
    123185        self.results = []  # type: List[np.ndarray] 
    124186 
     
    129191        # remember the parts for plotting later 
    130192        self.results = []  # type: List[np.ndarray] 
    131         offset = 2 # skip scale & background 
    132193        parts = MixtureParts(self.info, self.kernels, call_details, values) 
    133194        for kernel, kernel_details, kernel_values in parts: 
    134195            #print("calling kernel", kernel.info.name) 
    135196            result = kernel(kernel_details, kernel_values, cutoff, magnetic) 
    136             #print(kernel.info.name, result) 
    137             total += result 
     197            result = np.array(result).astype(kernel.dtype) 
     198            # print(kernel.info.name, result) 
     199            if self.operation == '+': 
     200                total += result 
     201            elif self.operation == '*': 
     202                if np.all(total) == 0.0: 
     203                    total = result 
     204                else: 
     205                    total *= result 
    138206            self.results.append(result) 
    139207 
     
    176244 
    177245        self.part_num += 1 
    178         self.par_index += info.parameters.npars + 1 
     246        self.par_index += info.parameters.npars 
     247        if self.model_info.operation == '+': 
     248            self.par_index += 1 # Account for each constituent model's scale param 
    179249        self.mag_index += 3 * len(info.parameters.magnetism_index) 
    180250 
     
    187257        # which includes the initial scale and background parameters. 
    188258        # We want the index into the weight length/offset for each parameter. 
    189         # Exclude the initial scale and background, so subtract two, but each 
    190         # component has its own scale factor which we need to skip when 
    191         # constructing the details for the kernel, so add one, giving a 
    192         # net subtract one. 
    193         index = slice(par_index - 1, par_index - 1 + info.parameters.npars) 
     259        # Exclude the initial scale and background, so subtract two. If we're 
     260        # building an addition model, each component has its own scale factor 
     261        # which we need to skip when constructing the details for the kernel, so 
     262        # add one, giving a net subtract one. 
     263        diff = 1 if self.model_info.operation == '+' else 2 
     264        index = slice(par_index - diff, par_index - diff + info.parameters.npars) 
    194265        length = full.length[index] 
    195266        offset = full.offset[index] 
     
    201272    def _part_values(self, info, par_index, mag_index): 
    202273        # type: (ModelInfo, int, int) -> np.ndarray 
    203         #print(info.name, par_index, self.values[par_index:par_index + info.parameters.npars + 1]) 
    204         scale = self.values[par_index] 
    205         pars = self.values[par_index + 1:par_index + info.parameters.npars + 1] 
     274        # Set each constituent model's scale to 1 if this is a multiplication model 
     275        scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 
     276        diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 
     277        pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 
    206278        nmagnetic = len(info.parameters.magnetism_index) 
    207279        if nmagnetic: 
Note: See TracChangeset for help on using the changeset viewer.