def __init__(self, *, space, name=None, formula=None, data=None, base=None, source=None, is_derived=False): # Determine name if base: name = base.name elif is_valid_name(name): pass elif formula: name = Formula(formula).name if is_valid_name(name): pass else: name = space.cellsnamer.get_next(space.namespace) else: name = space.cellsnamer.get_next(space.namespace) Impl.__init__(self, system=space.system, parent=space, name=name) self.spacemgr = space.spacemgr Derivable.__init__(self, is_derived) self.source = source space._cells.set_item(name, self) # Set formula if base: self.formula = base.formula elif formula is None: self.formula = NullFormula(NULL_FORMULA, name=name) elif isinstance(formula, Formula): self.formula = formula.__class__(formula, name=name) else: self.formula = Formula(formula, name=name) # Set data self.data = {} if data is None: data = {} self.data.update(data) self.input_keys = set(data.keys()) CellsNamespaceReferrer.__init__(self, space) self._namespace = self.parent._namespace if base: self.altfunc = BoundFunction(self, base.altfunc.fresh) else: self.altfunc = BoundFunction(self)
def set_formula(self, func): if self.parent.is_dynamic(): raise ValueError("cannot set formula in dynamic space") if self.is_derived: self.is_derived = False self.model.clear_obj(self) if isinstance(func, Formula): cls = func.__class__ else: cls = Formula self.formula = cls(func, name=self.name) self.altfunc = BoundFunction(self) self.model.spacemgr.update_subs(self.parent)
def on_change_formula(self, func): self.model.clear_obj(self) if self.is_derived: self.is_derived = False if isinstance(func, NullFormula): self.formula = NULL_FORMULA else: if isinstance(func, Formula): cls = func.__class__ else: cls = Formula self.formula = cls(func, name=self.name) self.altfunc = BoundFunction(self)
def on_rename(self, name): """Renames the Cells name - Clears DynamicCells of self - Updates the parent namespace - Clears successors - Clears DynamicCells of self - Renames sub Cells (Repeats the above for the sub Cells) """ self.model.clear_obj(self) self.parent.cells.delete_item(self.name) self.name = name # Change function name if not self.formula._is_lambda: if self.is_derived: base = self.bases[0] self.formula = base.formula else: self.formula = Formula(self.formula, name=name) self.altfunc = BoundFunction(self) self.parent.cells.add_item(name, self)
class CellsImpl(CellsNamespaceReferrer, Derivable, ItemFactoryImpl, HasFormula, Impl): """Cells implementation""" interface_cls = Cells __cls_stateattrs = [ "formula", "data", "_namespace", "altfunc", "source", "input_keys" ] def __init__(self, *, space, name=None, formula=None, data=None, base=None, source=None, is_derived=False): # Determine name if base: name = base.name elif is_valid_name(name): pass elif formula: name = Formula(formula).name if is_valid_name(name): pass else: name = space.cellsnamer.get_next(space.namespace) else: name = space.cellsnamer.get_next(space.namespace) Impl.__init__(self, system=space.system, parent=space, name=name) self.spacemgr = space.spacemgr Derivable.__init__(self, is_derived) self.source = source space._cells.set_item(name, self) # Set formula if base: self.formula = base.formula elif formula is None: self.formula = NullFormula(NULL_FORMULA, name=name) elif isinstance(formula, Formula): self.formula = formula.__class__(formula, name=name) else: self.formula = Formula(formula, name=name) # Set data self.data = {} if data is None: data = {} self.data.update(data) self.input_keys = set(data.keys()) CellsNamespaceReferrer.__init__(self, space) self._namespace = self.parent._namespace if base: self.altfunc = BoundFunction(self, base.altfunc.fresh) else: self.altfunc = BoundFunction(self) # ---------------------------------------------------------------------- # Serialization by pickle def __getstate__(self): state = { key: value for key, value in self.__dict__.items() if key in self.stateattrs } return state def __setstate__(self, state): self.__dict__.update(state) # ---------------------------------------------------------------------- # repr methods def repr_self(self, add_params=True): if add_params: return "%s(%s)" % (self.name, ", ".join(self.formula.parameters)) else: return self.name def repr_parent(self): return self.parent.repr_parent() + "." + self.parent.repr_self() def has_node(self, key): return key in self.data def is_scalar(self): # TODO: Move to HasFormula return len(self.formula.parameters) == 0 @property def single_value(self): if self.is_scalar(): return self.get_value(()) else: raise ValueError("%s not a scalar" % self.name) def inherit(self, updater, bases): self.model.clear_obj(self) self.formula = bases[0].formula self.altfunc.set_update() @property def namespace(self): return self._namespace.fresh @property def doc(self): if not self.formula._is_lambda: return self.formula.func.__doc__ else: return self._doc @property def module(self): return self.formula.module @staticmethod def _get_members(other): return other.cells # ---------------------------------------------------------------------- # Get/Set values def on_eval_formula(self, key): value = self.altfunc.fresh.altfunc(*key) if self.has_node(key): # Assignment took place inside the cell. if value is not None: raise ValueError("Duplicate assignment for %s" % key) else: value = self.data[key] else: value = self._store_value(key, value) return value def get_value(self, args, kwargs=None): node = get_node(self, args, kwargs) return self.system.executor.eval_node(node) def get_value_from_key(self, key): return self.system.executor.eval_node(key_to_node(self, key)) def find_match(self, args, kwargs): node = get_node(self, args, kwargs) key = node[KEY] keylen = len(key) if not self.get_property("allow_none"): raise NoneReturnedError(get_node_repr(node)) for match_len in range(keylen, -1, -1): for idxs in combinations(range(keylen), match_len): masked = [None] * keylen for idx in idxs: masked[idx] = key[idx] value = self.get_value(masked) if value is not None: return ArgsValuePair(tuple(masked), value) return ArgsValuePair(None, None) def set_value(self, args, value): node = get_node(self, args, {}) key = node[KEY] if self.system.callstack: if node == self.system.callstack[-1]: self._store_value(key, value) else: raise KeyError("Assignment in cells other than %s" % key) else: if self.system._recalc_dependents: targets = self.model.tracegraph.get_startnodes_from(node) self.clear_value_at(key) self._store_value(key, value) self.model.tracegraph.add_node(node) self.input_keys.add(key) if self.system._recalc_dependents: for trg in targets: trg[OBJ].get_value_from_key(trg[KEY]) def _store_value(self, key, value): if value is not None: self.data[key] = value elif self.get_property("allow_none"): self.data[key] = value else: raise NoneReturnedError(get_node_repr((self, key, None))) return value # ---------------------------------------------------------------------- # Clear value def on_clear_trace(self, key): del self.data[key] if key in self.input_keys: self.input_keys.remove(key) def clear_all_values(self, clear_input): for key in list(self.data): if clear_input: self.clear_value_at(key) else: if key not in self.input_keys: self.clear_value_at(key) def clear_value_at(self, key): if self.has_node(key): self.model.clear_with_descs(key_to_node(self, key)) # ---------------------------------------------------------------------- # Pandas I/O def tuplize_arg_sequence(self, argseq): if len(argseq) == 1: if isinstance(argseq[0], Sequence) and len(argseq[0]) == 0: pass # Empty sequence else: argseq = argseq[0] for arg in argseq: self.get_value(tuplize_key(self, arg, remove_extra=True)) return tuple(tuplize_key(self, arg) for arg in argseq) def to_series(self, args): from modelx.io.pandas import cells_to_series args = self.tuplize_arg_sequence(args) return cells_to_series(self, args) def to_frame(self, args): from modelx.io.pandas import cells_to_dataframe args = self.tuplize_arg_sequence(args) return cells_to_dataframe(self, args) # ---------------------------------------------------------------------- # Sanity Check def check_sanity(self): # Check consistency between data elements and nodes in trace graph nodes = self.model.tracegraph.get_nodes_with(self) assert set(self.data.keys()) == set(n[KEY] for n in nodes) return True