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): # check for path listed in param file path = getattr(self._meta, 'path', None) if 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._meta.path = os.path.dirname(proxy_file) # check for path listed in param file formula_importer = getattr(self._meta, 'formula_importer', None) if formula_importer is None: #: formula importer class, default is ``PyModuleImporter`` self._meta.formula_importer = PyModuleImporter meta = getattr(self, '_meta', None) # options for formulas importer_instance = self._meta.formula_importer(self.parameters, meta) #: formulas loaded by the importer using specified parameters self.formulas = importer_instance.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 # 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])