Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    def __init__(self,
                 *,
                 space,
                 name=None,
                 formula=None,
                 data=None,
                 base=None,
                 source=None):
        Impl.__init__(self, system=space.system)
        Derivable.__init__(self)

        self._model = space.model
        self.space = self.parent = space
        self.source = source

        if base:
            self.name = base.name
        elif is_valid_name(name):
            self.name = name
        elif formula:
            name = Formula(formula).name
            if is_valid_name(name):
                self.name = name
            else:
                self.name = space.cellsnamer.get_next(space.namespace)
        else:
            self.name = space.cellsnamer.get_next(space.namespace)

        if base:
            self.formula = base.formula
        elif formula is None:
            self.formula = NullFormula(NULL_FORMULA, name=self.name)
        elif isinstance(formula, Formula):
            self.formula = formula.__class__(formula, name=self.name)
        else:
            self.formula = Formula(formula, name=self.name)

        self.data = {}
        if data is None:
            data = {}
        self.data.update(data)

        self._namespace_impl = self.space._namespace_impl
        self.altfunc = BoundFunction(self)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
class CellsImpl(Derivable, Impl):
    """
    Data container optionally with a formula to set its own values.

    **Creation**

    **Deletion**

    * Values dependent on the cell are deleted clear_all()
    * Values dependent on the derived cells of the cells are deleted
    * Derived cells are deleted _del_derived
    * The cells is deleted _del_self

    **Changing formula**
    clear_all
    _set_formula
    _clear_all_derived
    _set_formula_derived

    **Changing allow_none**

    **Setting Values**
    clear()
    _set

    **Getting Values**

    **Deleting Values**
    clear(params)
    clear_all
    _clear_all_derived()

    Args:
        space: Space to contain the cell.
        name: Cell's name.
        func: Python function or Formula
        data: array-like, dict, pandas.DataSeries or scalar values.
    """

    if_cls = Cells

    state_attrs = [
        "_model",
        "space",
        "formula",
        "name",
        "data",
        "_namespace_impl",
        "altfunc",
    ] + Derivable.state_attrs + Impl.state_attrs

    assert len(state_attrs) == len(set(state_attrs))

    def __init__(self,
                 *,
                 space,
                 name=None,
                 formula=None,
                 data=None,
                 base=None,
                 source=None):
        Impl.__init__(self, system=space.system)
        Derivable.__init__(self)

        self._model = space.model
        self.space = self.parent = space
        self.source = source

        if base:
            self.name = base.name
        elif is_valid_name(name):
            self.name = name
        elif formula:
            name = Formula(formula).name
            if is_valid_name(name):
                self.name = name
            else:
                self.name = space.cellsnamer.get_next(space.namespace)
        else:
            self.name = space.cellsnamer.get_next(space.namespace)

        if base:
            self.formula = base.formula
        elif formula is None:
            self.formula = NullFormula(NULL_FORMULA, name=self.name)
        elif isinstance(formula, Formula):
            self.formula = formula.__class__(formula, name=self.name)
        else:
            self.formula = Formula(formula, name=self.name)

        self.data = {}
        if data is None:
            data = {}
        self.data.update(data)

        self._namespace_impl = self.space._namespace_impl
        self.altfunc = BoundFunction(self)

    # ----------------------------------------------------------------------
    # Serialization by pickle

    def __getstate__(self):
        state = {
            key: value
            for key, value in self.__dict__.items() if key in self.state_attrs
        }

        return state

    def __setstate__(self, state):
        self.__dict__.update(state)

    # ----------------------------------------------------------------------
    # Properties

    @property
    def model(self):
        return self._model

    # ----------------------------------------------------------------------
    # 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.space.repr_parent() + "." + self.space.repr_self()

    def has_cell(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, **kwargs):

        if "clear_value" in kwargs:
            clear_value = kwargs["clear_value"]
        else:
            clear_value = True

        if self.bases:
            if clear_value:
                self._model.clear_obj(self)
            self.formula = self.bases[0].formula
            self.altfunc.set_update()

    @property
    def doc(self):
        if self._doc is None:
            return self.formula.func.__doc__
        else:
            return self._doc

    @property
    def module(self):
        return self.formula.module

    @property
    def self_bases(self):
        return []

    @staticmethod
    def _get_members(other):
        return other.cells

    # ----------------------------------------------------------------------
    # Formula operations

    def reload(self, module=None):
        oldsrc = self.formula.source
        newsrc = self.formula._reload(module).source
        if oldsrc != newsrc:
            self._model.clear_obj(self)

    def clear_formula(self):

        if self.is_derived:
            self.is_derived = False

        self.set_formula(NULL_FORMULA)

    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.set_update()
        self._model.spacegraph.update_subspaces_upward(
            self.parent, from_parent=False, event="cells_set_formula")

    # ----------------------------------------------------------------------
    # Value operations

    def on_eval_formula(self, key):

        value = self.altfunc.get_updated().altfunc(*key)

        if self.has_cell(key):
            # Assignment took place inside the cell.
            if value is not None:
                raise ValueError("Duplicate assignment for %s" % key)
            else:
                value = self.data[key]
                del self.data[key]
                value = self._store_value(key, value, False)
        else:
            value = self._store_value(key, value, False)

        return value

    def get_value(self, args, kwargs=None):

        node = get_node(self, *convert_args(args, kwargs))
        key = node[KEY]

        if self.has_cell(key):
            value = self.data[key]
        else:
            value = self.system.execution.eval_cell(node)

        graph = self._model.cellgraph
        if self.system.callstack:
            graph.add_path([node, self.system.callstack.last()])
        else:
            graph.add_node(node)

        return value

    def find_match(self, args, kwargs):

        node = get_node(self, *convert_args(args, kwargs))
        key = node[KEY]
        keylen = len(key)

        if not self.get_property("allow_none"):
            # raise ValueError('Cells %s cannot return None' % self.name)
            tracemsg = self.system.callstack.tracemessage()
            raise NoneReturnedError(node, tracemsg)

        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, *convert_args(args, {}))
        key = node[KEY]

        if self.system.callstack:
            if node == self.system.callstack.last():
                self._store_value(key, value, False)
            else:
                raise KeyError("Assignment in cells other than %s" % key)
        else:
            self._store_value(key, value, True)
            self._model.cellgraph.add_node(node)

    def _store_value(self, key, value, overwrite=False):

        if isinstance(value, Cells):
            if value._impl.is_scalar():
                value = value._impl.single_value

        if not self.has_cell(key) or overwrite:

            if overwrite:
                self.clear_value(*key)

            if value is not None:
                self.data[key] = value
            elif self.get_property("allow_none"):
                self.data[key] = value
            else:
                tracemsg = self.system.callstack.tracemessage()
                raise NoneReturnedError(get_node(self, key, None), tracemsg)

        else:
            raise ValueError("Value already exists for %s" % key)

        return value

    def clear_value(self, *args, **kwargs):
        if args == () and kwargs == {} and not self.is_scalar():
            self.clear_all_values()
        else:
            node = get_node(self, *convert_args(args, kwargs))
            if self.has_cell(node[KEY]):
                self._model.clear_descendants(node)

    def clear_all_values(self):
        for args in list(self.data):
            self.clear_value(*args)

    # ----------------------------------------------------------------------
    # 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)

    # ----------------------------------------------------------------------
    # Dependency

    def predecessors(self, args, kwargs):
        node = get_node(self, *convert_args(args, kwargs))
        preds = self._model.cellgraph.predecessors(node)
        return [CellNode(n) for n in preds]

    def successors(self, args, kwargs):
        node = get_node(self, *convert_args(args, kwargs))
        succs = self._model.cellgraph.successors(node)
        return [CellNode(n) for n in succs]
Ejemplo n.º 5
0
def test_lambda_as_param():
    f = Formula(lambdadef2)
    assert f.func(1) == 3
    assert f.source == lambdadef2_extracted
Ejemplo n.º 6
0
def test_lambda_with_name():
    f = Formula(lambdadef1, name="bar")
    assert f.name == "bar"
    assert f.func(1) == 12
    assert f.source == lambdadef1_extracted
Ejemplo n.º 7
0
def test_lambda_no_name():
    f = Formula(lambdadef1)
    assert f.name == "<lambda>"
    assert f.func(1) == 12
    assert f.source == lambdadef1_extracted
Ejemplo n.º 8
0
def test_funcdef_with_name():
    f = Formula(funcdef1, name="bar")
    assert f.name == "bar"
    assert f.func(1) == 2
    assert f.source == funcdef1_renamed
Ejemplo n.º 9
0
def test_funcdef_no_name():
    f = Formula(funcdef1)
    assert f.name == "foo"
    assert f.func(1) == 2
    assert f.source == funcdef1_nodeco
Ejemplo n.º 10
0
class CellsImpl(Derivable, 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):
        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)
        Derivable.__init__(self)
        self.source = source
        space._cells.set_item(self.name, self)

        if base:
            self.formula = base.formula
        elif formula is None:
            self.formula = NullFormula(NULL_FORMULA, name=self.name)
        elif isinstance(formula, Formula):
            self.formula = formula.__class__(formula, name=self.name)
        else:
            self.formula = Formula(formula, name=self.name)

        self.data = {}
        if data is None:
            data = {}
        self.data.update(data)

        self._namespace = self.parent._namespace
        self.altfunc = BoundFunction(self)
        self.is_derived = is_derived
        self.input_keys = set(data.keys())

    # ----------------------------------------------------------------------
    # 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_cell(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, bases, **kwargs):

        if "clear_value" in kwargs:
            clear_value = kwargs["clear_value"]
        else:
            clear_value = True

        if bases:
            if clear_value:
                self.model.clear_obj(self)
            self.formula = bases[0].formula
            self.altfunc.set_update()

    @property
    def namespace(self):
        return self._namespace.refresh.interfaces

    @property
    def doc(self):
        if self._doc is None:
            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

    # ----------------------------------------------------------------------
    # Formula operations

    def reload(self, module=None):
        oldsrc = self.formula.source
        newsrc = self.formula._reload(module).source
        if oldsrc != newsrc:
            self.model.clear_obj(self)

    def clear_formula(self):

        if self.is_derived:
            self.is_derived = False

        self.set_formula(NULL_FORMULA)

    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.set_update()

        self.model.spacemgr.update_subs(self.parent)

    # ----------------------------------------------------------------------
    # Get/Set values

    def on_eval_formula(self, key):

        value = self.altfunc.refresh.altfunc(*key)

        if self.has_cell(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, *convert_args(args, kwargs))
        key = node[KEY]

        if self.has_cell(key):
            value = self.data[key]
        else:
            value = self.system.executor.eval_cell(node)

        graph = self.model.cellgraph
        if self.system.callstack:
            graph.add_path([node, self.system.callstack.last()])
        else:
            graph.add_node(node)

        return value

    def find_match(self, args, kwargs):

        node = get_node(self, *convert_args(args, kwargs))
        key = node[KEY]
        keylen = len(key)

        if not self.get_property("allow_none"):
            # raise ValueError('Cells %s cannot return None' % self.name)
            tracemsg = self.system.callstack.tracemessage()
            raise NoneReturnedError(node, tracemsg)

        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, *convert_args(args, {}))
        key = node[KEY]

        if self.system.callstack:
            if node == self.system.callstack.last():
                self._store_value(key, value)
            else:
                raise KeyError("Assignment in cells other than %s" % key)
        else:
            if self.system._recalc_dependents:
                targets = self.model.cellgraph.get_startnodes_from(node)
            self.clear_value_at(key)
            self._store_value(key, value)
            self.model.cellgraph.add_node(node)
            self.input_keys.add(key)
            if self.system._recalc_dependents:
                for trg in targets:
                    trg[OBJ].get_value(trg[KEY])

    def _store_value(self, key, value):

        if isinstance(value, Cells):
            if value._impl.is_scalar():
                value = value._impl.single_value

        if value is not None:
            self.data[key] = value
        elif self.get_property("allow_none"):
            self.data[key] = value
        else:
            tracemsg = self.system.callstack.tracemessage()
            raise NoneReturnedError(get_node(self, key, None), tracemsg)

        return value

    # ----------------------------------------------------------------------
    # Clear value

    def on_clear_value(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_cell(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)

    # ----------------------------------------------------------------------
    # Dependency

    def predecessors(self, args, kwargs):
        node = get_node(self, *convert_args(args, kwargs))
        preds = self.model.cellgraph.predecessors(node)
        return [CellNode(n) for n in preds]

    def successors(self, args, kwargs):
        node = get_node(self, *convert_args(args, kwargs))
        succs = self.model.cellgraph.successors(node)
        return [CellNode(n) for n in succs]