1 | from __future__ import print_function |
---|
2 | |
---|
3 | import numpy as np # type: ignore |
---|
4 | |
---|
5 | try: |
---|
6 | from typing import List |
---|
7 | except ImportError: |
---|
8 | pass |
---|
9 | else: |
---|
10 | from .modelinfo import ModelInfo |
---|
11 | |
---|
12 | |
---|
13 | class CallDetails(object): |
---|
14 | parts = None # type: List["CallDetails"] |
---|
15 | def __init__(self, model_info): |
---|
16 | # type: (ModelInfo) -> None |
---|
17 | parameters = model_info.parameters |
---|
18 | max_pd = parameters.max_pd |
---|
19 | |
---|
20 | # Structure of the call details buffer: |
---|
21 | # pd_par[max_pd] pd params in order of length |
---|
22 | # pd_length[max_pd] length of each pd param |
---|
23 | # pd_offset[max_pd] offset of pd values in parameter array |
---|
24 | # pd_stride[max_pd] index of pd value in loop = n//stride[k] |
---|
25 | # pd_prod total length of pd loop |
---|
26 | # pd_sum total length of the weight vector |
---|
27 | # num_active number of pd params |
---|
28 | # theta_par parameter number for theta parameter |
---|
29 | self.buffer = np.zeros(4*max_pd + 4, 'i4') |
---|
30 | |
---|
31 | # generate views on different parts of the array |
---|
32 | self._pd_par = self.buffer[0 * max_pd:1 * max_pd] |
---|
33 | self._pd_length = self.buffer[1 * max_pd:2 * max_pd] |
---|
34 | self._pd_offset = self.buffer[2 * max_pd:3 * max_pd] |
---|
35 | self._pd_stride = self.buffer[3 * max_pd:4 * max_pd] |
---|
36 | |
---|
37 | # theta_par is fixed |
---|
38 | self.theta_par = parameters.theta_offset |
---|
39 | |
---|
40 | @property |
---|
41 | def pd_par(self): return self._pd_par |
---|
42 | |
---|
43 | @property |
---|
44 | def pd_length(self): return self._pd_length |
---|
45 | |
---|
46 | @property |
---|
47 | def pd_offset(self): return self._pd_offset |
---|
48 | |
---|
49 | @property |
---|
50 | def pd_stride(self): return self._pd_stride |
---|
51 | |
---|
52 | @property |
---|
53 | def pd_prod(self): return self.buffer[-4] |
---|
54 | @pd_prod.setter |
---|
55 | def pd_prod(self, v): self.buffer[-4] = v |
---|
56 | |
---|
57 | @property |
---|
58 | def pd_sum(self): return self.buffer[-3] |
---|
59 | @pd_sum.setter |
---|
60 | def pd_sum(self, v): self.buffer[-3] = v |
---|
61 | |
---|
62 | @property |
---|
63 | def num_active(self): return self.buffer[-2] |
---|
64 | @num_active.setter |
---|
65 | def num_active(self, v): self.buffer[-2] = v |
---|
66 | |
---|
67 | @property |
---|
68 | def theta_par(self): return self.buffer[-1] |
---|
69 | @theta_par.setter |
---|
70 | def theta_par(self, v): self.buffer[-1] = v |
---|
71 | |
---|
72 | def show(self): |
---|
73 | print("num_active", self.num_active) |
---|
74 | print("pd_prod", self.pd_prod) |
---|
75 | print("pd_sum", self.pd_sum) |
---|
76 | print("theta par", self.theta_par) |
---|
77 | print("pd_par", self.pd_par) |
---|
78 | print("pd_length", self.pd_length) |
---|
79 | print("pd_offset", self.pd_offset) |
---|
80 | print("pd_stride", self.pd_stride) |
---|
81 | |
---|
82 | def mono_details(model_info): |
---|
83 | call_details = CallDetails(model_info) |
---|
84 | call_details.pd_prod = 1 |
---|
85 | return call_details |
---|
86 | |
---|
87 | def poly_details(model_info, weights): |
---|
88 | #print("weights",weights) |
---|
89 | #weights = weights[2:] # Skip scale and background |
---|
90 | |
---|
91 | # Decreasing list of polydispersity lengths |
---|
92 | pd_length = np.array([len(w) for w in weights]) |
---|
93 | num_active = np.sum(pd_length>1) |
---|
94 | if num_active > model_info.parameters.max_pd: |
---|
95 | raise ValueError("Too many polydisperse parameters") |
---|
96 | |
---|
97 | pd_offset = np.cumsum(np.hstack((0, pd_length))) |
---|
98 | # Note: the reversing view, x[::-1], does not require a copy |
---|
99 | idx = np.argsort(pd_length)[::-1][:num_active] |
---|
100 | par_length = np.array([len(w) for w in weights]) |
---|
101 | pd_stride = np.cumprod(np.hstack((1, par_length[idx]))) |
---|
102 | |
---|
103 | call_details = CallDetails(model_info) |
---|
104 | call_details.pd_par[:num_active] = idx - 2 # skip background & scale |
---|
105 | call_details.pd_length[:num_active] = pd_length[idx] |
---|
106 | call_details.pd_offset[:num_active] = pd_offset[idx] |
---|
107 | call_details.pd_stride[:num_active] = pd_stride[:-1] |
---|
108 | call_details.pd_prod = pd_stride[-1] |
---|
109 | call_details.pd_sum = np.sum(par_length) |
---|
110 | call_details.num_active = num_active |
---|
111 | #call_details.show() |
---|
112 | return call_details |
---|