def test_lazy_loop_calculator_cls(): """Test the lazy loop calculator class.""" calc = { 'formula': 'pythagorian_thm', 'args': { 'data': { 'adjacent': 'a', 'opposite': 'b' }, 'outputs': {} }, 'returns': ['c'] } formula_reg = FormulaRegistry() formula_reg.register( {'pythagorian_thm': UREG.wraps(*PYTHAGOREAN_UNITS)(f_pythagorian_thm)}, args={'pythagorian_thm': ['adjacent', 'opposite']}, units={'pythagorian_thm': PYTHAGOREAN_UNITS}, isconstant={'pythagorian_thm': None}) data_reg = DataRegistry() data_reg.register( { 'a': [3., 5., 7., 9., 11.] * UREG('cm'), 'b': [4., 12., 24., 40., 60.] * UREG('cm') }, uncertainty=None, variance=None, isconstant={ 'a': True, 'b': True }) out_reg = OutputRegistry() out_reg.register({'c': np.zeros(5) * UREG.m}) # repeat args are listed as formula names, not data reg names! calculator = LazyLoopingCalculator(repeat_args=['adjacent', 'opposite']) calculator.calculate(calc, formula_reg, data_reg, out_reg) assert np.allclose(out_reg['c'].m, PYTHAGOREAN_TRIPLES) # check magnitudes assert out_reg['c'].u == UREG.m # output units are meters return out_reg
def __init__(self): if hasattr(self, 'param_file'): # read and load JSON parameter map file as "parameters" with open(self.param_file, 'r') as fp: #: dictionary of parameters for reading formula source file self.parameters = json.load(fp) else: #: parameter file self.param_file = None # check for path listed in param file if 'path' in self.parameters and self.parameters.get('path') is None: proxy_file = self.param_file if self.param_file else __file__ # use the same path as the param file or this file if no param file self.parameters['path'] = os.path.dirname(proxy_file) #: formulas loaded by the importer using specified parameters self.formulas = self.formula_importer(self.parameters).import_formulas() #: linearity determined by each data source? self.islinear = {} #: positional arguments self.args = {} #: expected units of returns and arguments as pair of tuples self.units = {} #: constant arguments that are not included in covariance calculation self.isconstant = {} # sequence of formulas, don't propagate uncertainty or units for f in self.formulas: self.islinear[f] = True self.args[f] = inspect.getargspec(self.formulas[f]).args formula_param = self.parameters.get('formulas') # formulas key # if formulas is a list or if it can't be iterated as a dictionary # then log warning and return try: formula_param_generator = formula_param.iteritems() except AttributeError as err: LOGGER.warning('Attribute Error: %s', err.message) return # formula dictionary for k, v in formula_param_generator: if not v: # skip formula if attributes are null or empty continue # get islinear formula attribute is_linear = v.get('islinear') if is_linear is not None: self.islinear[k] = is_linear # get positional arguments f_args = v.get('args') if f_args is not None: self.args[k] = f_args # get constant arguments to exclude from covariance self.isconstant[k] = v.get('isconstant') if self.isconstant[k] is not None: argn = [n for n, a in enumerate(self.args[k]) if a not in self.isconstant[k]] LOGGER.debug('%s arg nums: %r', k, argn) self.formulas[k] = unc_wrapper_args(*argn)(self.formulas[k]) # get units of returns and arguments self.units[k] = v.get('units') if self.units[k] is not None: # append units for covariance and Jacobian if all args # constant and more than one return output if self.isconstant[k] is not None: # check if retval units is a string or None before adding # extra units for Jacobian and covariance ret_units = self.units[k][0] if isinstance(ret_units, basestring) or ret_units is None: self.units[k][0] = [ret_units] try: self.units[k][0] += [None, None] except TypeError: self.units[k][0] += (None, None) # wrap function with Pint's unit wrapper self.formulas[k] = UREG.wraps(*self.units[k])( self.formulas[k] )