Changeset 0edb6c1 in sasmodels for sasmodels/mixture.py
- Timestamp:
- Sep 12, 2017 3:12:03 AM (7 years ago)
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/mixture.py
r765eb0e r0edb6c1 25 25 pass 26 26 27 def make_mixture_info(parts ):27 def make_mixture_info(parts, operation='+'): 28 28 # type: (List[ModelInfo]) -> ModelInfo 29 29 """ 30 30 Create info block for mixture model. 31 31 """ 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 = flatten39 40 32 # Build new parameter list 41 33 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: 43 81 # Parameter prefix per model, A_, B_, ... 44 82 # Note that prefix must also be applied to id and length_control 45 83 # 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) 50 110 for p in part.parameters.kernel_parameters: 51 111 p = copy(p) … … 67 127 68 128 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) + ')' 71 132 model_info.filename = None 72 133 model_info.title = 'Mixture model with ' + model_info.name … … 121 182 self.kernels = kernels 122 183 self.dtype = self.kernels[0].dtype 184 self.operation = model_info.operation 123 185 self.results = [] # type: List[np.ndarray] 124 186 … … 129 191 # remember the parts for plotting later 130 192 self.results = [] # type: List[np.ndarray] 131 offset = 2 # skip scale & background132 193 parts = MixtureParts(self.info, self.kernels, call_details, values) 133 194 for kernel, kernel_details, kernel_values in parts: 134 195 #print("calling kernel", kernel.info.name) 135 196 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 138 206 self.results.append(result) 139 207 … … 176 244 177 245 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 179 249 self.mag_index += 3 * len(info.parameters.magnetism_index) 180 250 … … 187 257 # which includes the initial scale and background parameters. 188 258 # 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) 194 265 length = full.length[index] 195 266 offset = full.offset[index] … … 201 272 def _part_values(self, info, par_index, mag_index): 202 273 # 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] 206 278 nmagnetic = len(info.parameters.magnetism_index) 207 279 if nmagnetic:
Note: See TracChangeset
for help on using the changeset viewer.