Changeset a86f6c0 in sasmodels


Ignore:
Timestamp:
Apr 8, 2016 3:22:43 PM (8 years ago)
Author:
Paul Kienzle <pkienzle@…>
Branches:
master, core_shell_microgels, costrafo411, magnetic_model, release_v0.94, release_v0.95, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
7edce22
Parents:
2afc26d
Message:

optional typing for modelinfo module

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/modelinfo.py

    r4bfd277 ra86f6c0  
    55 
    66from .details import mono_details 
     7 
     8# Optional typing 
     9try: 
     10    from typing import Tuple, List, Union, Dict, Optional, Any, Callable 
     11except ImportError: 
     12    pass 
     13else: 
     14    from .details import CallDetails 
     15    Limits = Tuple[float, float] 
     16    LimitsOrChoice = Union[Limits, Tuple[str]] 
     17    ParameterDef = Tuple[str, str, float, LimitsOrChoice, str, str] 
     18    ParameterSetUser = Dict[str, Union[float, List[float]]] 
     19    ParameterSet = Dict[str, float] 
     20    TestInput = Union[str, float, List[float], Tuple[float, float], List[Tuple[float, float]]] 
     21    TestValue = Union[float, List[float]] 
     22    TestCondition = Tuple[ParameterSetUser, TestInput, TestValue] 
    723 
    824MAX_PD = 4 
     
    2541# depends on the some polydisperse parameter with the current implementation 
    2642 
     43 
    2744def make_parameter_table(pars): 
     45    # type: (List[ParameterDefinition) -> ParameterTable 
    2846    processed = [] 
    2947    for p in pars: 
    30         if not isinstance(p, list) or len(p) != 6: 
     48        if not isinstance(p, (list, tuple)) or len(p) != 6: 
    3149            raise ValueError("Parameter should be [name, units, default, limits, type, desc], but got %r" 
    3250                             %str(p)) 
     
    3553    return partable 
    3654 
    37 def parse_parameter(name, units='', default=None, 
    38                     limits=(-np.inf, np.inf), type='', description=''): 
     55def parse_parameter(name, units='', default=np.NaN, 
     56                    limits=(-np.inf, np.inf), ptype='', description=''): 
     57    # type: (str, str, float, LimitsOrChoice, str, str) -> Parameter 
    3958    # Parameter is a user facing class.  Do robust type checking. 
    4059    if not isstr(name): 
     
    6887                         % (default, name)) 
    6988 
    70     if type not in ("volume", "orientation", "sld", "magnetic", ""): 
    71         raise ValueError("unexpected type %r for %s" % (type, name)) 
     89    if ptype not in ("volume", "orientation", "sld", "magnetic", ""): 
     90        raise ValueError("unexpected type %r for %s" % (ptype, name)) 
    7291 
    7392    if not isstr(description): 
     
    86105 
    87106    # automatically identify sld types 
    88     if type=='' and (pid.startswith('sld') or pid.endswith('sld')): 
    89         type = 'sld' 
     107    if ptype== '' and (pid.startswith('sld') or pid.endswith('sld')): 
     108        ptype = 'sld' 
    90109 
    91110    # Check if using a vector definition, name[k], as the parameter name 
     
    96115            length = int(ref) 
    97116            control = None 
    98         except Exception: 
     117        except ValueError: 
    99118            length = None 
    100119            control = ref 
     
    105124    # Build the parameter 
    106125    parameter = Parameter(name=name, units=units, default=default, 
    107                           limits=limits, type=type, description=description) 
     126                          limits=limits, ptype=ptype, description=description) 
    108127 
    109128    # TODO: need better control over whether a parameter is polydisperse 
    110     parameter.polydisperse = type in ('orientation', 'volume') 
    111     parameter.relative_pd = type in ('volume') 
     129    parameter.polydisperse = ptype in ('orientation', 'volume') 
     130    parameter.relative_pd = ptype == 'volume' 
    112131    parameter.choices = choices 
    113132    parameter.length = length 
     
    118137 
    119138def expand_pars(partable, pars): 
     139    # type: (ParameterTable, ParameterSetUser) ->  ParameterSet 
    120140    """ 
    121141    Create demo parameter set from key-value pairs. 
     
    159179 
    160180def prefix_parameter(par, prefix): 
     181    # type: (Parameter, str) -> Parameter 
    161182    """ 
    162183    Return a copy of the parameter with its name prefixed. 
     
    167188 
    168189def suffix_parameter(par, suffix): 
     190    # type: (Parameter, str) -> Parameter 
    169191    """ 
    170192    Return a copy of the parameter with its name prefixed. 
     
    233255    """ 
    234256    def __init__(self, name, units='', default=None, limits=(-np.inf, np.inf), 
    235                  type='', description=''): 
    236         self.id = name.split('[')[0].strip() 
    237         self.name = name 
    238         self.units = units 
    239         self.default = default 
    240         self.limits = limits 
    241         self.type = type 
    242         self.description = description 
     257                 ptype='', description=''): 
     258        # type: (str, str, float, Limits, str, str) 
     259        self.id = name.split('[')[0].strip() # type: str 
     260        self.name = name                     # type: str 
     261        self.units = units                   # type: str 
     262        self.default = default               # type: float 
     263        self.limits = limits                 # type: Limits 
     264        self.type = ptype                    # type: str 
     265        self.description = description       # type: str 
    243266 
    244267        # Length and length_control will be filled in once the complete 
    245268        # parameter table is available. 
    246         self.length = 1 
    247         self.length_control = None 
    248         self.is_control = False 
     269        self.length = 1                      # type: int 
     270        self.length_control = None           # type: Optional[str] 
     271        self.is_control = False              # type: bool 
    249272 
    250273        # TODO: need better control over whether a parameter is polydisperse 
    251         self.polydisperse = False 
    252         self.relative_pd = False 
     274        self.polydisperse = False            # type: bool 
     275        self.relative_pd = False             # type: bool 
    253276 
    254277        # choices are also set externally. 
    255         self.choices = [] 
     278        self.choices = []                    # type: List[str] 
    256279 
    257280    def as_definition(self): 
     281        # type: () -> str 
    258282        """ 
    259283        Declare space for the variable in a parameter structure. 
     
    269293 
    270294    def as_function_argument(self): 
     295        # type: () -> str 
    271296        """ 
    272297        Declare the variable as a function argument. 
     
    282307 
    283308    def as_call_reference(self, prefix=""): 
     309        # type: () -> str 
    284310        # Note: if the parameter is a struct type, then we will need to use 
    285311        # &prefix+id.  For scalars and vectors we can just use prefix+id. 
     
    287313 
    288314    def __str__(self): 
     315        # type: () -> str 
    289316        return "<%s>"%self.name 
    290317 
    291318    def __repr__(self): 
     319        # type: () -> str 
    292320        return "P<%s>"%self.name 
    293321 
     
    355383 
    356384    def __init__(self, parameters): 
     385        # type: (List[Parameter]) -> None 
    357386        self.kernel_parameters = parameters 
    358387        self._set_vector_lengths() 
    359         self._make_call_parameter_list() 
    360         self._categorize_parameters() 
    361         self._set_defaults() 
     388        self.call_parameters = self._get_call_parameters() 
     389        self.defaults = self._get_defaults() 
    362390        #self._name_table= dict((p.id, p) for p in parameters) 
    363391 
     392        # Set the kernel parameters.  Assumes background and scale are the 
     393        # first two parameters in the parameter list, but these are not sent 
     394        # to the underlying kernel functions. 
     395        self.iq_parameters = [p for p in self.kernel_parameters 
     396                              if p.type not in ('orientation', 'magnetic')] 
     397        self.iqxy_parameters = [p for p in self.kernel_parameters 
     398                                if p.type != 'magnetic'] 
     399        self.form_volume_parameters = [p for p in self.kernel_parameters 
     400                                       if p.type == 'volume'] 
     401 
     402        # Theta offset 
     403        offset = 0 
     404        for p in self.kernel_parameters: 
     405            if p.name == 'theta': 
     406                self.theta_offset = offset 
     407                break 
     408            offset += p.length 
     409        else: 
     410            self.theta_offset = -1 
     411 
     412        # number of polydisperse parameters 
     413        num_pd = sum(p.length for p in self.kernel_parameters if p.polydisperse) 
     414        # Don't use more polydisperse parameters than are available in the model 
     415        # Note: we can do polydispersity on arbitrary parameters, so it is not 
     416        # clear that this is a good idea; it does however make the poly_details 
     417        # code easier to write, so we will leave it in for now. 
     418        self.max_pd = min(num_pd, MAX_PD) 
     419 
     420        self.npars = sum(p.length for p in self.kernel_parameters) 
     421 
     422        # true if has 2D parameters 
     423        self.has_2d = any(p.type in ('orientation', 'magnetic') 
     424                          for p in self.kernel_parameters) 
     425 
     426        self.pd_1d = set(p.name for p in self.call_parameters 
     427                         if p.polydisperse and p.type not in ('orientation', 'magnetic')) 
     428        self.pd_2d = set(p.name for p in self.call_parameters 
     429                         if p.polydisperse and p.type != 'magnetic') 
     430 
     431 
    364432    def _set_vector_lengths(self): 
     433        # type: () -> None 
     434        """ 
     435        Walk the list of kernel parameters, setting the length field of the 
     436        vector parameters from the upper limit of the reference parameter. 
     437 
     438        This needs to be done once the entire parameter table is available 
     439        since the reference may still be undefined when the parameter is 
     440        initially created. 
     441 
     442        Note: This modifies the underlying parameter object. 
     443        """ 
    365444        # Sort out the length of the vector parameters such as thickness[n] 
    366445        for p in self.kernel_parameters: 
     
    374453                ref.is_control = True 
    375454                low, high = ref.limits 
    376                 if int(low) != low or int(high) != high or low<0 or high>20: 
     455                if int(low) != low or int(high) != high or low < 0 or high > 20: 
    377456                    raise ValueError("expected limits on %s to be within [0, 20]" 
    378457                                     % ref.name) 
     458                # TODO: may want to make a copy of the parameter before updating 
     459                # this introduces other potential problems, since the same 
     460                # parameter may be referenced elsewhere 
    379461                p.length = high 
    380462 
    381     def _set_defaults(self): 
     463    def _get_defaults(self): 
     464        # type: () -> ParameterSet 
     465        """ 
     466        Get a list of parameter defaults from the parameters. 
     467 
     468        Expands vector parameters into parameter id+number. 
     469        """ 
    382470        # Construct default values, including vector defaults 
    383471        defaults = {} 
     
    388476                for k in range(1, p.length+1): 
    389477                    defaults["%s%d"%(p.id, k)] = p.default 
    390         self.defaults = defaults 
    391  
    392     def _make_call_parameter_list(self): 
     478        return defaults 
     479 
     480    def _get_call_parameters(self): 
     481        # type: () -> List[Parameter] 
    393482        full_list = self.COMMON[:] 
    394483        for p in self.kernel_parameters: 
     
    402491                    pk.relative_pd = p.relative_pd 
    403492                    full_list.append(pk) 
    404         self.call_parameters = full_list 
    405  
    406     def _categorize_parameters(self): 
    407         # Set the kernel parameters.  Assumes background and scale are the 
    408         # first two parameters in the parameter list, but these are not sent 
    409         # to the underlying kernel functions. 
    410         self.iq_parameters = [p for p in self.kernel_parameters 
    411                               if p.type not in ('orientation', 'magnetic')] 
    412         self.iqxy_parameters = [p for p in self.kernel_parameters 
    413                                 if p.type != 'magnetic'] 
    414         self.form_volume_parameters = [p for p in self.kernel_parameters 
    415                                        if p.type == 'volume'] 
    416  
    417         # Theta offset 
    418         offset = 0 
    419         for p in self.kernel_parameters: 
    420             if p.name == 'theta': 
    421                 self.theta_offset = offset 
    422                 break 
    423             offset += p.length 
    424         else: 
    425             self.theta_offset = -1 
    426  
    427         # number of polydisperse parameters 
    428         num_pd = sum(p.length for p in self.kernel_parameters if p.polydisperse) 
    429         # Don't use more polydisperse parameters than are available in the model 
    430         # Note: we can do polydispersity on arbitrary parameters, so it is not 
    431         # clear that this is a good idea; it does however make the poly_details 
    432         # code easier to write, so we will leave it in for now. 
    433         self.max_pd = min(num_pd, MAX_PD) 
    434  
    435         self.npars = sum(p.length for p in self.kernel_parameters) 
    436  
    437         # true if has 2D parameters 
    438         self.has_2d = any(p.type in ('orientation', 'magnetic') 
    439                           for p in self.kernel_parameters) 
    440  
    441         self.pd_1d = set(p.name for p in self.call_parameters 
    442                 if p.polydisperse and p.type not in ('orientation', 'magnetic')) 
    443         self.pd_2d = set(p.name for p in self.call_parameters 
    444                          if p.polydisperse and p.type != 'magnetic') 
     493        return full_list 
    445494 
    446495    def user_parameters(self, pars={}, is2d=True): 
     496        # type: (Dict[str, float], bool) -> List[str] 
    447497        """ 
    448498        Return the list of parameters for the given data type. 
     
    530580 
    531581def isstr(x): 
     582    # type: (Any) -> bool 
    532583    # TODO: 2-3 compatible tests for str, including unicode strings 
    533584    return isinstance(x, str) 
     
    557608    info.structure_factor = getattr(kernel_module, 'structure_factor', False) 
    558609    info.profile_axes = getattr(kernel_module, 'profile_axes', ['x','y']) 
    559     info.variant_info = getattr(kernel_module, 'invariant_info', None) 
    560     info.demo = getattr(kernel_module, 'demo', None) 
     610    info.variant_info = getattr(kernel_module, 'variant_info', None) 
    561611    info.source = getattr(kernel_module, 'source', []) 
    562612    info.tests = getattr(kernel_module, 'tests', []) 
     
    613663      build complete product and mixture models from just the info. 
    614664    """ 
    615     id = None 
    616     filename = None 
    617     name = None 
    618     title = None 
    619     description = None 
    620     parameters = None 
    621     demo = None 
    622     composition = None 
    623     docs = None 
    624     category = None 
    625     single = None 
    626     structure_factor = None 
    627     profile_axes = None 
    628     variant_info = None 
    629     demo = None 
    630     source = None 
    631     tests = None 
    632     ER = None 
    633     VR = None 
    634     form_volume = None 
    635     Iq = None 
    636     Iqxy = None 
     665    id = None               # type: str 
     666    filename = None         # type: str 
     667    name = None             # type: str 
     668    title = None            # type: str 
     669    description = None      # type: str 
     670    parameters = None       # type: ParameterTable 
     671    demo = None             # type: Dict[str, float] 
     672    composition = None      # type: Optional[Tuple[str, List[ModelInfo]]] 
     673    docs = None             # type: str 
     674    category = None         # type: Optional[str] 
     675    single = None           # type: bool 
     676    structure_factor = None # type: bool 
     677    profile_axes = None     # type: Tuple[str, str] 
     678    variant_info = None     # type: Optional[List[str]] 
     679    source = None           # type: List[str] 
     680    tests = None            # type: List[TestCondition] 
     681    ER = None               # type: Optional[Callable[[np.ndarray, ...], float]] 
     682    VR = None               # type: Optional[Callable[[np.ndarray, ...], float]] 
     683    form_volume = None      # type: Optional[Callable[[np.ndarray, ...], float]] 
     684    Iq = None               # type: Optional[Callable[[np.ndarray, ...], np.ndarray]] 
     685    Iqxy = None             # type: Optional[Callable[[np.ndarray, ...], np.ndarray]] 
    637686    profile = None 
    638687    sesans = None 
    639     mono_details = None 
     688    mono_details = None     # type: CallDetails 
    640689 
    641690    def __init__(self): 
     691        # type: () -> None 
    642692        pass 
    643693 
Note: See TracChangeset for help on using the changeset viewer.