diff --git a/subtrees/dagflow/.gitignore b/subtrees/dagflow/.gitignore deleted file mode 100644 index f11cd0888ffc8c5026b90c05182545a12c042b41..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/.gitignore +++ /dev/null @@ -1,54 +0,0 @@ -output -build -_build -__pycache__ - -# Local configuration files and folders -config_local -.local/ -.vscode -.direnv/ -.envrc -.fish_functions -matplotlibrc -.coverage -cov.* - -# Transient files (vim, etc) -*~ -*.swp -\#* -.\#* -.cache -.lark_cache* -.lark-cache* -*.bak -*.backup - -# vim -UltiSnips/* -.viminfo -.vimrc -.nvimrc -*.vim -.ycm_extra_conf.py - -# Latex -*.aux -*.pda -*.toc -*.log -*.fdb* -*.out -*.pdf -*.png -*.blg -*.snm -*.nav -# code - -# Code -tags -*.pyc -*.o -coverage.json diff --git a/subtrees/dagflow/.gitlab-ci.yml b/subtrees/dagflow/.gitlab-ci.yml deleted file mode 100644 index cd58f936bd5dacc3d4934dfc95b53924ea02d579..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/.gitlab-ci.yml +++ /dev/null @@ -1,24 +0,0 @@ -stages: - - tests - -tests: - image: git.jinr.ru:5005/gna/gna-base-docker-image:latest - stage: tests - - script: - - python3 -m pip install -r requirements.txt - - coverage run --source=. -m pytest - - coverage report - - coverage xml - coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' - artifacts: - paths: - - test/output - reports: - coverage_report: - coverage_format: cobertura - path: coverage.xml - only: - - master - - update-to-data-preservation - - merge_requests diff --git a/subtrees/dagflow/README.md b/subtrees/dagflow/README.md deleted file mode 100644 index d691822eb40f841818fa3a1b0abe16aeb134022b..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Summary - -[](https://www.python.org/) -[](https://git.jinr.ru/dag-computing/dag-flow/commits/master) -[](https://git.jinr.ru/dag-computing/dag-flow/-/commits/master) -<!--- Uncomment here after adding docs! -[](http://dag-computing.pages.jinr.ru/dag-flow) ---> - -DAGFlow is python implementation of dataflow programming with lazy graph evaluation. - -Main goals: -* Lazy evaluated directed acyclic graph -* Concise connection syntax -* Plotting with graphviz -* Flexibility. The goal of DAGFlow is not to be efficient, but rather flexible. - -Here is an animation, showing the process of the graph evaluation - - - -# Minimal example -An example of small, graph calculating the formula (n1 + n2 + n3) * n4 may be -found in the [example](example/example.py): -```python -#!/usr/bin/env python - -from dagflow.node_deco import NodeClass -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.input_extra import MissingInputAddOne -import numpy as N - -# Node functions -@NodeClass(output='array') -def Array(node, inputs, output): - """Creates a note with single data output with predefined array""" - outputs[0].data = N.arange(5, dtype='d') - -@NodeClass(missing_input_handler=MissingInputAddOne(output_fmt='result')) -def Adder(node, inputs, output): - """Adds all the inputs together""" - out = None - for input in inputs: - if out is None: - out=outputs[0].data = input.data.copy() - else: - out+=input.data - -@NodeClass(missing_input_handler=MissingInputAddOne(output_fmt='result')) -def Multiplier(node, inputs, output): - """Multiplies all the inputs together""" - out = None - for input in inputs: - if out is None: - out = outputs[0].data = input.data.copy() - else: - out*=input.data - -# The actual code -with Graph() as graph: - (in1, in2, in3, in4) = [Array(name) for name in ['n1', 'n2', 'n3', 'n4']] - s = Adder('add') - m = Multiplier('mul') - -(in1, in2, in3) >> s -(in4, s) >> m - -print('Result is:', m.outputs["result"].data) -savegraph(graph, 'output/dagflow_example.png') -``` - -The code produces the following graph: - - - -For `n=[1, 2, 3, 4]` the output is: -``` -Result is: [ 0. 3. 12. 27. 48.] -``` diff --git a/subtrees/dagflow/TODO.md b/subtrees/dagflow/TODO.md deleted file mode 100644 index 502b50a7ae594b309f53aa52e46c1efab793367a..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/TODO.md +++ /dev/null @@ -1,72 +0,0 @@ -# Update to Daya Bay data preservation - -## Common tasks - -- [x] Input renaming: `output -> input`, `corresponding_output -> output` -- [x] Automatic creation of outputs is **restricted** -- [x] Parentheses operator `()` as getter `[]` of inputs, but with creation -of the input, instead of `KeyError` -- [x] Implementing of flexible shift operators `>>` and `<<` or *using current*? - - Now using curren implementation. -- [x] Implement `hooks`: - - At an input connection - - At a function evaluation -- [x] Two types of `Exceptions`: - - connection and type checking (`non-critical` exception) - - call function (`critical`) -- [x] Recursive close of a graph -- [x] Solve troubles with a connection of an input or output and closure -- [x] Implement 2 descriptors for the `Output`: - - `Shape` and `dtype` - - `Allocation` and `view` -- [x] Move `handlers` to the `binding` stage -- [x] Memory allocation: - - See `core/transformation/TransformationEntry.cc` method `updateTypes()` -- [x] Datatype: `allocatable`, `non-alloc` -- [x] Datadescr: `dtype`, `shape` -- [x] Dict as `kwargs`: - - `ws = WeightedSum()"`; - -`{'weight' : data} >> ws` is the same as `data >> ws('weight')` -- [x] Logging -- [x] Inputs problem: there is a difference between node and output inputs -- [x] Update naming for the second order `input` and `output`: `parent`, `child` -- [x] `iinput` is a meta data, do not use in allocation and closure; -use `Node` to do this stuff; do not use second order `input` and `output` -- [x] Loops scheme: - 1) Close: - - Typing: - - Update types - - Update shapes - - Allocation - 2) Graph: - - Node: - - Inputs - - Outputs - 3) See <https://hackmd.io/mMNrlOp7Q7i9wkVFvP4W4Q> -- [x] `Tainted` -- [x] Fix decorators -- [x] Move common checks in `typefunc` into standalone module -- [ ] Update wrapping - -## Transformations - -- [x] Implementing of some simple transformations with only `args` in function: -`Sum`, `Product`, `Division`, ... -- [x] Implementing of some simple transformations with `args` and `kwargs`: -`WeightedSum` with `weight`, ... -- [x] Check the style of the implementation -- [x] Update the inputs checks before evaluation -- [x] Concatenation -- [x] Update `WeightedSum` -- [ ] Implement `Integrator` - -## Tests - -- [x] Test the graph workflow with transformations -- [x] Test of open and closure of the several graphs - -## Questions and suggestions - -- [x] Should we use only `numpy.ndarray` or also `numpy.number` for single element: - 1) only `numpy.ndarray`! -- [] Should we implement `zero`, `unity` objects with automatic dimension? diff --git a/subtrees/dagflow/conftest.py b/subtrees/dagflow/conftest.py deleted file mode 100644 index 1e7b36a0544b50a29f1af6cfdeef050ee9cc1d22..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/conftest.py +++ /dev/null @@ -1,37 +0,0 @@ -from os import chdir, getcwd, mkdir -from os.path import isdir - -from pytest import fixture, skip - - -def pytest_sessionstart(session): - """ - Called after the Session object has been created and - before performing collection and entering the run test loop. - - Automatic change path to the `dag-flow/test` and create `test/output` dir - """ - path = getcwd() - lastdir = path.split("/")[-1] - if lastdir == "dag-flow": # rootdir - chdir("./test") - elif lastdir in ( - "dagflow", - "example", - "doc", - "docs", - "source", - "sources", - ): # childdir - chdir("../test") - if not isdir("output"): - mkdir("output") - - -def pytest_addoption(parser): - parser.addoption("--debug_graph", action="store_true", default=False) - - -@fixture(scope="session") -def debug_graph(request): - return request.config.option.debug_graph diff --git a/subtrees/dagflow/dagflow/__init__.py b/subtrees/dagflow/dagflow/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/subtrees/dagflow/dagflow/bundles/__init__.py b/subtrees/dagflow/dagflow/bundles/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/subtrees/dagflow/dagflow/bundles/load_variables.py b/subtrees/dagflow/dagflow/bundles/load_variables.py deleted file mode 100644 index 62e35a4346275fde669cf513e05beef377ae77f5..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/bundles/load_variables.py +++ /dev/null @@ -1,152 +0,0 @@ -from dictwrapper.dictwrapper import DictWrapper -# from storage.storage import Storage # To be used later - -from schema import Schema, Or, Optional, Use, And, Schema, SchemaError - -from ..tools.schema import NestedSchema, LoadFileWithExt, LoadYaml - -class ParsCfgHasProperFormat(object): - def validate(self, data: dict) -> dict: - format = data['format'] - if isinstance(format, str): - nelements = 1 - else: - nelements = len(format) - - dtin = DictWrapper(data) - for key, subdata in dtin['variables'].walkitems(): - if isinstance(subdata, tuple): - if len(subdata)==nelements: continue - else: - if nelements==1: continue - - key = ".".join(str(k) for k in key) - raise SchemaError(f'Key "{key}" has value "{subdata}"" inconsistent with format "{format}"') - - return data - -IsNumber = Or(float, int, error='Invalid number "{}", expect int of float') -IsNumberOrTuple = Or(IsNumber, (IsNumber,), error='Invalid number/tuple {}') -IsLabel = Or({ - 'text': str, - Optional('latex'): str, - Optional('graph'): str, - Optional('mark'): str, - Optional('name'): str - }, - And(str, Use(lambda s: {'text': s}), error='Invalid string: {}') -) -IsValuesDict = NestedSchema(IsNumberOrTuple) -IsLabelsDict = NestedSchema(IsLabel, processdicts=True) -def IsFormatOk(format): - if not isinstance(format, tuple): - return format=='value' - - if len(format)==1: - f1,=format - return f1=='value' - else: - if len(format)==2: - f1, f3 = format - elif len(format)==3: - f1, f2, f3 = format - - if f2 not in ('value', 'central') or f1==f2: - return False - else: - return False - - if f3 not in ('sigma_absolute', 'sigma_relative', 'sigma_percent'): - return False - - return f1 in ('value', 'central') - -IsFormat = Schema(IsFormatOk, error='Invalid variable format "{}".') -IsVarsCfgDict = Schema({ - 'variables': IsValuesDict, - 'labels': IsLabelsDict, - 'format': IsFormat - }, - error = 'Invalid parameters configuration: {}' -) -IsProperVarsCfgDict = And(IsVarsCfgDict, ParsCfgHasProperFormat()) -IsLoadableDict = And( - {'load': str}, - Use(LoadFileWithExt(yaml=LoadYaml, key='load'), error='Failed to load {}'), - IsProperVarsCfgDict - ) -IsProperVarsCfg = Or(IsProperVarsCfgDict, IsLoadableDict) - -def process_var_fixed1(vcfg, _, __): - return {'central': vcfg, 'value': vcfg, 'sigma': None} - -def process_var_fixed2(vcfg, format, hascentral) -> dict: - ret = dict(zip(format, vcfg)) - if hascentral: - ret.setdefault('value', ret['central']) - else: - ret.setdefault('central', ret['value']) - ret['sigma'] = None - return ret - -def process_var_absolute(vcfg, format, hascentral) -> dict: - ret = process_var_fixed2(vcfg, format, hascentral) - ret['sigma'] = ret['sigma_absolute'] - return ret - -def process_var_relative(vcfg, format, hascentral) -> dict: - ret = process_var_fixed2(vcfg, format, hascentral) - ret['sigma'] = ret['sigma_relative']*ret['central'] - return ret - -def process_var_percent(vcfg, format, hascentral) -> dict: - ret = process_var_fixed2(vcfg, format, hascentral) - ret['sigma'] = 0.01*ret['sigma_percent']*ret['central'] - return ret - -def get_format_processor(format): - if isinstance(format, str): - return process_var_fixed1 - - errfmt = format[-1] - if not errfmt.startswith('sigma'): - return process_var_fixed2 - - if errfmt.endswith('_absolute'): - return process_var_absolute - elif errfmt.endswith('_relative'): - return process_var_relative - else: - return process_var_percent - -def iterate_varcfgs(cfg: DictWrapper): - variablescfg = cfg['variables'] - labelscfg = cfg['labels'] - format = cfg['format'] - - hascentral = 'central' in format - process = get_format_processor(format) - - for key, varcfg in variablescfg.walkitems(): - varcfg = process(varcfg, format, hascentral) - try: - varcfg['label'] = labelscfg[key] - except KeyError: - varcfg['label'] = {} - yield key, varcfg - -from dagflow.variable import Parameters - -def load_variables(acfg): - cfg = IsProperVarsCfg.validate(acfg) - cfg = DictWrapper(cfg) - - ret = DictWrapper({}, sep='.') - for key, varcfg in iterate_varcfgs(cfg): - skey = '.'.join(key) - label = varcfg['label'] - label['key'] = skey - label.setdefault('text', skey) - ret[key] = Parameters.from_numbers(**varcfg) - - return ret diff --git a/subtrees/dagflow/dagflow/datadescriptor.py b/subtrees/dagflow/dagflow/datadescriptor.py deleted file mode 100755 index 94cdf5a71d42c52f3c3043a9c4f89037c5dd6d63..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/datadescriptor.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Optional, Tuple -from numpy.typing import DTypeLike - -from .types import ShapeLike, EdgesLike - - -class DataDescriptor: - """ - The data descriptor class stores `dtype`, `shape`, - `axes_edges` and `axes_nodes` information. - """ - - __slots__ = ("dtype", "shape", "axes_edges", "axes_nodes") - dtype: DTypeLike # DTypeLike is already Optional - shape: Optional[ShapeLike] - axes_edges: Optional[Tuple[EdgesLike]] - axes_nodes: Optional[Tuple[EdgesLike]] - - def __init__( - self, - dtype: DTypeLike, # DTypeLike is already Optional - shape: Optional[ShapeLike], - axes_edges: Optional[Tuple[EdgesLike]] = None, - axes_nodes: Optional[Tuple[EdgesLike]] = None, - ) -> None: - """ - Sets the attributes - """ - self.dtype = dtype - self.shape = shape - self.axes_edges = axes_edges - self.axes_nodes = axes_nodes diff --git a/subtrees/dagflow/dagflow/edges.py b/subtrees/dagflow/dagflow/edges.py deleted file mode 100644 index 2a094fdc4e5a3774a6732d7abd1d3f84999c5a92..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/edges.py +++ /dev/null @@ -1,141 +0,0 @@ -from collections.abc import Sequence - -from .exception import CriticalError -from .iter import IsIterable - -from typing import List, Dict, Union - -class EdgeContainer: - _kw_edges: Dict - _pos_edges: List - _all_edges: Dict - _dtype = None - - def __init__(self, iterable=None): - self._kw_edges = {} - self._pos_edges = [] - self._all_edges = {} - if iterable: - self.add(iterable) - - def add(self, value, *, positional: bool=True, keyword: bool=True): - if positional==keyword==False: - raise RuntimeError('Edge should be at least positional or a keyword') - - if IsIterable(value): - for v in value: - self.add(v, positional=positional, keyword=keyword) - return self - if self._dtype and not isinstance(value, self._dtype): - raise RuntimeError( - f"The type {type(value)} of the data doesn't correpond " - f"to {self._dtype}!" - ) - name = value.name - if not name: - raise RuntimeError("May not add objects with undefined name") - if name in self._all_edges: - raise RuntimeError("May not add duplicated items") - - if positional: - self._pos_edges.append(value) - if keyword: - self._kw_edges[name] = value - self._all_edges[name]=value - return self - - def allocate(self) -> bool: - return all(edge.allocate() for edge in self._all_edges.values()) - - def __getitem__(self, key): - if isinstance(key, str): - return self._kw_edges[key] - elif isinstance(key, (int, slice)): - return self._pos_edges[key] - elif isinstance(key, Sequence): - return tuple(self.__getitem__(k) for k in key) - raise TypeError(f"Unsupported key type: {type(key).__name__}") - - def get(self, key, default = None): - try: - return self.__getitem__(key) - except Exception: - return default - - def has_key(self, key: str) -> bool: - return key in self._kw_edges - - def get_pos(self, idx: int): - """Get positional leg""" - return self._pos_edges[idx] - iat = get_pos - - def index(self, arg): - return self._pos_edges.index(arg) - - def get_kw(self, key: str): - """Return keyword leg""" - return self._kw_edges[key] - kat = get_kw - - def len_pos(self) -> int: - """Returns a number of the positional legs""" - return len(self._pos_edges) - __len__ = len_pos - - def len_kw(self) -> int: - """Returns a number of the keyword legs""" - return len(self._kw_edges) - - def len_all(self) -> int: - """Returns a number of the all legs""" - return len(self._all_edges) - - def __iter__(self): - return iter(self._pos_edges) - - def iter_all(self): - return iter(self._all_edges.values()) - - def iter_data(self): - for edge in self._pos_edges: - yield edge.data - - def iter(self, key: Union[int, str, slice, Sequence]): - if isinstance(key, int): - yield self._pos_edges[key] - elif isinstance(key, str): - yield self._kw_edges[key] - elif isinstance(key, slice): - yield from self._pos_edges[key] - elif isinstance(key, Sequence): - for subkey in key: - if isinstance(subkey, int): - yield self._pos_edges[subkey] - elif isinstance(subkey, str): - yield self._kw_edges[subkey] - elif isinstance(subkey, slice): - yield from self._pos_edges[subkey] - else: - raise CriticalError(f'Invalid subkey type {type(subkey).__name__}') - else: - raise CriticalError(f'Invalid key type {type(key).__name__}') - - def __contains__(self, name): - return name in self._all_edges - - def _replace(self, old, new): - replaced = False - - for k, v in self._kw_edges.items(): - if old is v: - self._kw_edges[k] = new - replaced = True - - for i, v in enumerate(self._pos_edges): - if old is v: - self._pos_edges[i] = new - replaced = True - - if not replaced: - raise CriticalError('Unable to replace an output/input (not found)') diff --git a/subtrees/dagflow/dagflow/exception.py b/subtrees/dagflow/dagflow/exception.py deleted file mode 100644 index b57aaf94616a82daa43c318f0667524d6d5beb1c..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/exception.py +++ /dev/null @@ -1,93 +0,0 @@ -from typing import Optional -from .types import NodeT, InputT, OutputT - - -class DagflowError(Exception): - node: Optional[NodeT] - input: Optional[InputT] - output: Optional[OutputT] - - def __init__( - self, - message: str, - node: Optional[NodeT] = None, - *, - input: Optional[InputT] = None, - output: Optional[OutputT] = None, - ): - if node: - message = f"{message} [node={node.name if 'name' in dir(node) else node}]" - if input: - message = f"{message} [input={input.name if 'name' in dir(input) else input}]" - if output: - message = f"{message} [output={output.name if 'name' in dir(output) else output}]" - super().__init__(message) - self.node = node - self.input = input - self.output = output - - if node is not None: - node._exception = message - -class CriticalError(DagflowError): - pass - - -class NoncriticalError(DagflowError): - pass - -class InitializationError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "Wrong initialization!" - super().__init__(message, *args, **kwargs) - - -class AllocationError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "Unable to allocate memory!" - super().__init__(message, *args, **kwargs) - -class ClosingError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "An exception occured during closing procedure!" - super().__init__(message, *args, **kwargs) - -class OpeningError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "An exception occured during opening procedure!" - super().__init__(message, *args, **kwargs) - -class ClosedGraphError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "Unable to modify a closed graph!" - super().__init__(message, *args, **kwargs) - -class UnclosedGraphError(CriticalError): - def __init__(self, message : Optional[str]=None, *args, **kwargs): - if not message: - message = "The graph is not closed!" - super().__init__(message, *args, **kwargs) - - -class TypeFunctionError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "An exception occurred during type function processing!" - super().__init__(message, *args, **kwargs) - -class ReconnectionError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "The object is already connected!" - super().__init__(message, *args, **kwargs) - -class ConnectionError(CriticalError): - def __init__(self, message: Optional[str] = None, *args, **kwargs): - if not message: - message = "An exception occurred during connection!" - super().__init__(message, *args, **kwargs) diff --git a/subtrees/dagflow/dagflow/graph.py b/subtrees/dagflow/dagflow/graph.py deleted file mode 100644 index dd0b9f1c32dca1f8b5f608bd16c48e6b43db506e..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/graph.py +++ /dev/null @@ -1,138 +0,0 @@ -from .exception import ( - UnclosedGraphError, - ClosedGraphError, - InitializationError -) -from .logger import Logger, get_logger -from .node_group import NodeGroup - -from typing import Optional - -class Graph(NodeGroup): - """ - The graph class: - holds nodes as a list, has name, label, logger and uses context - """ - - _context_graph: Optional['Graph'] = None - _label: Optional[str] = None - _name = "graph" - _close: bool = False - _closed: bool = False - _debug: bool = False - _logger: Logger - - def __init__(self, *args, close: bool = False, **kwargs): - super().__init__(*args) - self._label = kwargs.pop("label", None) - self._name = kwargs.pop("name", "graph") - self._debug = kwargs.pop("debug", False) - self._close = close - # init or get default logger - self._logger = get_logger( - filename=kwargs.pop("logfile", None), - debug=self.debug, - console=kwargs.pop("console", True), - formatstr=kwargs.pop("logformat", None), - name=kwargs.pop("loggername", None), - ) - if kwargs: - raise InitializationError(f"Unparsed arguments: {kwargs}!") - - @property - def debug(self) -> bool: - return self._debug - - @property - def logger(self) -> Logger: - return self._logger - - @property - def name(self) -> str: - return self._name - - @property - def closed(self) -> bool: - return self._closed - - def _add_output(self, *args, **kwargs): - """Dummy method""" - pass - - def _add_input(self, *args, **kwargs): - """Dummy method""" - pass - - def label(self): - """Returns formatted label""" - if self._label: - return self._label.format(self._label, nodes=len(self._nodes)) - - def add_node(self, name, **kwargs): - """ - Adds a node, if the graph is opened. - It is possible to pass the node class via the `nodeclass` arg - (default: `FunctionNode`) - """ - if not self.closed: - from .nodes import FunctionNode - return kwargs.pop("nodeclass", FunctionNode)( - name, graph=self, **kwargs - ) - raise ClosedGraphError(node=name) - - def add_nodes(self, nodes, **kwargs): - """Adds nodes""" - if not self.closed: - return (self.add_node(node, **kwargs) for node in nodes) - raise ClosedGraphError(node=nodes) - - def print(self): - print(f"Graph with {len(self._nodes)} nodes") - for node in self._nodes: - node.print() - - @classmethod - def current(cls): - return cls._context_graph - - def __enter__(self): - Graph._context_graph = self - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - Graph._context_graph = None - if exc_val is not None: - raise exc_val - - if self._close: - self.close() - - def close(self, **kwargs) -> bool: - """Closes the graph""" - # TODO: implement cross-closure of several graphs - if self._closed: - return True - self.logger.debug(f"Graph '{self.name}': Closing...") - self.logger.debug(f"Graph '{self.name}': Update types...") - for node in self._nodes: - node.update_types() - self.logger.debug(f"Graph '{self.name}': Allocate memory...") - for node in self._nodes: - node.allocate(**kwargs) - self.logger.debug(f"Graph '{self.name}': Closing nodes...") - self._closed = all(node.close(**kwargs) for node in self._nodes) - if not self._closed: - raise UnclosedGraphError("The graph is still open!") - self.logger.debug(f"Graph '{self.name}': The graph is closed!") - return self._closed - - def open(self, force: bool = False) -> bool: - """Opens the graph recursively""" - if not self._closed and not force: - return True - self.logger.debug(f"Graph '{self.name}': Opening...") - self._closed = not all(node.open(force) for node in self._nodes) - if self._closed: - raise UnclosedGraphError("The graph is still open!") - return not self._closed diff --git a/subtrees/dagflow/dagflow/graphviz.py b/subtrees/dagflow/dagflow/graphviz.py deleted file mode 100644 index a6a9cb74a9bb3f8de874838c7aeaba706456dc16..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/graphviz.py +++ /dev/null @@ -1,373 +0,0 @@ -from .input import Input -from .output import Output -from .printl import printl -from .types import NodeT - -from numpy import square -from collections.abc import Sequence -from typing import Union, Set, Optional, Dict - -try: - import pygraphviz as G -except ImportError: - GraphDot = None - savegraph = None -else: - - def savegraph(graph, *args, **kwargs): - gd = GraphDot(graph, **kwargs) - gd.savegraph(*args) - - class EdgeDef: - __slots__ = ('nodein', 'nodemid', 'nodeout', 'edges') - def __init__(self, nodeout, nodemid, nodein, edge): - self.nodein = nodein - self.nodemid = nodemid - self.nodeout = nodeout - self.edges = [edge] - - def append(self, edge): - self.edges.append(edge) - - class GraphDot: - _graph = None - _node_id_map: dict - - _show: Set[str] - def __init__( - self, - dag, - graphattr: dict={}, edgeattr: dict={}, nodeattr: dict={}, - show: Union[Sequence,str] = ['type', 'mark', 'label'], - **kwargs - ): - if show=='all' or 'all' in show: - self._show = {'type', 'mark', 'label', 'status', 'data', 'data_summary'} - else: - self._show = set(show) - - graphattr = dict(graphattr) - graphattr.setdefault("rankdir", "LR") - graphattr.setdefault("dpi", 300) - - edgeattr = dict(edgeattr) - edgeattr.setdefault("fontsize", 10) - edgeattr.setdefault("labelfontsize", 9) - edgeattr.setdefault("labeldistance", 1.2) - - nodeattr = dict(nodeattr) - - self._node_id_map = {} - self._nodes = {} - self._nodes_open_input = {} - self._nodes_open_output = {} - self._edges: Dict[str, EdgeDef] = {} - self._graph = G.AGraph(directed=True, strict=False, **kwargs) - - if graphattr: - self._graph.graph_attr.update(graphattr) - if edgeattr: - self._graph.edge_attr.update(edgeattr) - if nodeattr: - self._graph.node_attr.update(nodeattr) - - if label := kwargs.pop("label", dag.label()): - self.set_label(label) - self._transform(dag) - - def _transform(self, dag): - for nodedag in dag._nodes: - self._add_node(nodedag) - for nodedag in dag._nodes: - self._add_open_inputs(nodedag) - self._add_edges(nodedag) - self.update_style() - - def get_id(self, object, suffix: str="") -> str: - name = type(object).__name__ - omap = self._node_id_map.setdefault(name, {}) - onum = omap.setdefault(object, len(omap)) - return f"{name}_{onum}{suffix}" - - def get_label(self, node: NodeT) -> str: - text = node.label('graph') or node.name - try: - out0 = node.outputs[0] - except IndexError: - shape0 = '?' - dtype0 = '?' - else: - shape0 = out0.dd.shape - if shape0 is None: - shape0 = '?' - shape0="x".join(str(s) for s in shape0) - - dtype0 = out0.dd.dtype - if dtype0 is None: - dtype0 = '?' - else: - dtype0 = dtype0.char - - nout_pos = len(node.outputs) - nout_nonpos = node.outputs.len_all()-nout_pos - if nout_nonpos==0: - if nout_pos>1: - nout = f'鈫抺nout_pos}' - else: - nout = '' - else: - nout=f'鈫抺nout_pos}+{nout_nonpos}' - - nin_pos = len(node.inputs) - nin_nonpos = node.inputs.len_all() - nin_pos - if nin_nonpos==0: - if nin_pos>1: - nin = f'{nin_pos}鈫�' - else: - nin = '' - else: - nin=f'{nin_pos}+{nin_nonpos}鈫�' - - nlegs = f' {nin}{nout}'.replace('鈫掆啋', '鈫�') - - left, right = [], [] - info_type = f"[{shape0}]{dtype0}{nlegs}" - if 'type' in self._show: - left.append(info_type) - if 'mark' in self._show and node.mark is not None: - left.append(node.mark) - if 'label' in self._show: - right.append(text) - if 'status' in self._show: - status = [] - if node.types_tainted: status.append('types_tainted') - if node.tainted: status.append('tainted') - if node.frozen: status.append('frozen') - if node.frozen_tainted: status.append('frozen_tainted') - if node.invalid: status.append('invalid') - if not node.closed: status.append('open') - if status: - right.append(status) - - show_data = 'data' in self._show - show_data_summary = 'data_summary' in self._show - if show_data or show_data_summary: - data = None - tainted = out0.tainted and 'tainted' or 'updated' - try: - data = out0.data - except Exception: - right.append('cought exception') - data = out0._data - - if show_data: - right.append(str(data).replace('\n', '\\l')+'\\l') - if show_data_summary: - sm = data.sum() - sm2 = square(data).sum() - mn = data.min() - mx = data.max() - right.append((f'危={sm:.2g}', f'危虏={sm2:.2g}', f'min={mn:.2g}', f'max={mx:.2g}', f'{tainted}')) - - if node.exception is not None: - right.append(node.exception) - - return self._combine_labels((left, right)) - - def _combine_labels(self, labels: Union[Sequence,str]) -> str: - if isinstance(labels, str): - return labels - - slabels = [self._combine_labels(l) for l in labels] - return f"{{{'|'.join(slabels)}}}" - - def _add_node(self, nodedag): - styledict = { - "shape": "Mrecord", - "label": self.get_label(nodedag) - } - target = self.get_id(nodedag) - self._graph.add_node(target, **styledict) - nodedot = self._graph.get_node(target) - self._nodes[nodedag] = nodedot - - def _add_open_inputs(self, nodedag): - for input in nodedag.inputs: - if not input.connected(): - self._add_open_input(input, nodedag) - - def _add_open_input(self, input, nodedag): - styledict = {} - source = self.get_id(input, "_in") - target = self.get_id(nodedag) - - self._graph.add_node(source, label="", shape="none", **styledict) - self._graph.add_edge(source, target, **styledict) - - nodein = self._graph.get_node(source) - edge = self._graph.get_edge(source, target) - nodeout = self._graph.get_node(target) - - self._nodes_open_input[input] = nodein - self._edges[input] = EdgeDef(nodein, None, nodeout, edge) - - def _add_edges(self, nodedag): - for output in nodedag.outputs: - if output.connected(): - if len(output.child_inputs)>1: - self._add_edges_multi(nodedag, output) - else: - self._add_edge(nodedag, output, output.child_inputs[0]) - else: - self._add_open_output(nodedag, output) - - def _add_edges_multi(self, nodedag, output): - vnode = self.get_id(output, "_mid") - self._graph.add_node(vnode, label="", shape="none", width=0, height=0, penwidth=0, weight=10) - firstinput = output.child_inputs[0] - self._add_edge(nodedag, output, firstinput, vtarget=vnode) - for input in output.child_inputs: - self._add_edge(nodedag, output, input, vsource=vnode) - - def _add_open_output(self, nodedag, output): - styledict = {} - source = self.get_id(nodedag) - target = self.get_id(output, "_out") - self._get_index(output, styledict, 'taillabel') - - self._graph.add_node(target, label="", shape="none", **styledict) - self._graph.add_edge( - source, target, arrowhead="empty", **styledict - ) - nodein = self._graph.get_node(source) - edge = self._graph.get_edge(source, target) - nodeout = self._graph.get_node(target) - - self._nodes_open_output[output] = nodeout - self._edges[output] = EdgeDef(nodein, None, nodeout, edge) - - def _get_index(self, leg, styledict: dict, target: str): - if isinstance(leg, Input): - container = leg.node.inputs - else: - container = leg.node.outputs - if container.len_all()<2: - return - - try: - idx = container.index(leg) - except ValueError: - pass - else: - styledict[target] = str(idx) - - def _add_edge(self, nodedag, output, input, *, vsource: Optional[str]=None, vtarget: Optional[str]=None) -> None: - styledict = {} - - if vsource is not None: - source = vsource - styledict['arrowtail'] = 'none' - else: - source = self.get_id(nodedag) - self._get_index(output, styledict, 'taillabel') - - if vtarget is not None: - target = vtarget - styledict['arrowhead'] = 'none' - else: - target = self.get_id(input.node) - self._get_index(input, styledict, 'headlabel') - - self._graph.add_edge(source, target, **styledict) - - nodein = self._graph.get_node(source) - edge = self._graph.get_edge(source, target) - nodeout = self._graph.get_node(target) - - edgedef = self._edges.get(input, None) - if edgedef is None: - self._edges[input] = EdgeDef(nodein, None, nodeout, edge) - else: - edgedef.append(edge) - - def _set_style_node(self, node, attr): - if node is None: - attr["color"] = "gray" - else: - if node.invalid: - attr["color"] = "black" - elif node.being_evaluated: - attr["color"] = "gold" - elif node.tainted: - attr["color"] = "red" - elif node.frozen_tainted: - attr["color"] = "blue" - elif node.frozen: - attr["color"] = "cyan" - elif node.immediate: - attr["color"] = "green" - else: - attr["color"] = "forestgreen" - - if node.exception is not None: - attr["color"] = "magenta" - - def _set_style_edge(self, obj, attrin, attr, attrout): - if isinstance(obj, Input): - if obj.connected(): - node = obj.parent_output.node - else: - node = None - self._set_style_node(node, attrin) - else: - node = obj.node - self._set_style_node(node, attrout) - - self._set_style_node(node, attr) - - if isinstance(obj, Input): - allocated_on_input = obj.owns_buffer - try: - allocated_on_output = obj.parent_output.owns_buffer - except AttributeError: - allocated_on_output = True - elif isinstance(obj, Output): - allocated_on_input = False - allocated_on_output = obj.owns_buffer - attr.update({ - "dir": "both", - "arrowsize": 0.5 - }) - attr["arrowhead"] = attr["arrowhead"] or allocated_on_input and 'dotopen' or 'odotopen' - attr["arrowtail"] = attr["arrowtail"] or allocated_on_output and 'dot' or 'odot' - - if node: - if node.frozen: - attrin["style"] = "dashed" - attr["style"] = "dashed" - # attr['arrowhead']='tee' - else: - attr["style"] = "" - - def update_style(self): - for nodedag, nodedot in self._nodes.items(): - self._set_style_node(nodedag, nodedot.attr) - - for object, edgedef in self._edges.items(): - for edge in edgedef.edges: - self._set_style_edge( - object, edgedef.nodein.attr, edge.attr, edgedef.nodeout.attr - ) - - def set_label(self, label): - self._graph.graph_attr["label"] = label - - def savegraph(self, fname, verbose=True): - if verbose: - printl("Write output file:", fname) - - if fname.endswith(".dot"): - self._graph.write(fname) - else: - self._graph.layout(prog="dot") - self._graph.draw(fname) diff --git a/subtrees/dagflow/dagflow/input.py b/subtrees/dagflow/dagflow/input.py deleted file mode 100644 index 29a27e285970e2338f010fa86e4d17f28f8efcdf..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/input.py +++ /dev/null @@ -1,299 +0,0 @@ -from typing import Iterator, Optional, Tuple, Union -from numpy import zeros -from numpy.typing import DTypeLike, NDArray - -from dagflow.datadescriptor import DataDescriptor - -from .edges import EdgeContainer -from .exception import ( - ClosedGraphError, - ReconnectionError, - AllocationError, - InitializationError, -) -from .output import Output -from .shift import lshift -from .iter import StopNesting -from .types import EdgesLike, InputT, NodeT, ShapeLike - - -class Input: - _own_data: Optional[NDArray] = None - _own_dd: DataDescriptor - - _node: Optional[NodeT] - _name: Optional[str] - - _parent_output: Optional[Output] - _child_output: Optional[Output] - - _allocatable: bool = False - _owns_buffer: bool = False - - _debug: bool = False - - def __init__( - self, - name: Optional[str] = None, - node: Optional[NodeT] = None, - *, - child_output: Optional[Output] = None, - parent_output: Optional[Output] = None, - debug: Optional[bool] = None, - allocatable: bool = False, - data: Optional[NDArray] = None, - dtype: DTypeLike = None, - shape: Optional[ShapeLike] = None, - axes_edges: Optional[Tuple[EdgesLike]] = None, - axes_nodes: Optional[Tuple[EdgesLike]] = None, - ): - if data is not None and ( - allocatable or dtype is not None or shape is not None - ): - raise InitializationError(input=input, node=node) - - self._name = name - self._node = node - self._child_output = child_output - self._parent_output = parent_output - self._allocatable = allocatable - if debug is not None: - self._debug = debug - elif node: - self._debug = node.debug - else: - self._debug = False - - self._own_dd = DataDescriptor(dtype, shape, axes_edges, axes_nodes) - - if data is not None: - self.set_own_data(data, owns_buffer=True) - - def __str__(self) -> str: - return ( - f"鈫掆棆 {self._name}" - if self._owns_buffer is None - else f"鈫掆棌 {self._name}" - ) - - def __repr__(self) -> str: - return self.__str__() - - @property - def own_data(self) -> Optional[NDArray]: - return self._own_data - - @property - def own_dd(self) -> DataDescriptor: - return self._own_dd - - @property - def owns_buffer(self) -> bool: - return self._owns_buffer - - def set_own_data( - self, - data, - *, - owns_buffer: bool, - axes_edges: EdgesLike = None, - axes_nodes: EdgesLike = None, - ): - if self.closed: - raise ClosedGraphError( - "Unable to set input data.", node=self._node, input=self - ) - if self.own_data is not None: - raise AllocationError( - "Input already has data.", node=self._node, input=self - ) - - self._own_data = data - self._owns_buffer = owns_buffer - self.own_dd.dtype = data.dtype - self.own_dd.shape = data.shape - self.own_dd.axes_edges = axes_edges - self.own_dd.axes_nodes = axes_nodes - - @property - def closed(self): - return self._node.closed if self.node else False - - def set_child_output( - self, child_output: Output, force: bool = False - ) -> None: - if not self.closed: - return self._set_child_output(child_output, force) - raise ClosedGraphError(input=self, node=self.node, output=child_output) - - def _set_child_output( - self, child_output: Output, force: bool = False - ) -> None: - if self.child_output and not force: - raise ReconnectionError(output=self.child_output, node=self.node) - self._child_output = child_output - child_output.parent_input = self - - def set_parent_output( - self, parent_output: Output, force: bool = False - ) -> None: - if not self.closed: - return self._set_parent_output(parent_output, force) - raise ClosedGraphError( - input=self, node=self.node, output=parent_output - ) - - def _set_parent_output( - self, parent_output: Output, force: bool = False - ) -> None: - if self.connected() and not force: - raise ReconnectionError(output=self._parent_output, node=self.node) - self._parent_output = parent_output - - @property - def name(self) -> str: - return self._name - - @name.setter - def name(self, name) -> None: - self._name = name - - @property - def node(self) -> NodeT: - return self._node - - @property - def parent_node(self) -> NodeT: - return self._parent_output.node - - @property - def logger(self): - return self._node.logger - - @property - def child_output(self) -> InputT: - return self._child_output - - @property - def invalid(self) -> bool: - """Checks validity of the parent output data""" - return self._parent_output.invalid - - @property - def has_data(self) -> bool: - return self._own_data is not None - - @property - def allocatable(self) -> bool: - return self._allocatable - - @property - def debug(self) -> bool: - return self._debug - - @invalid.setter - def invalid(self, invalid) -> None: - """Sets the validity of the current node""" - self._node.invalid = invalid - - @property - def parent_output(self) -> Output: - return self._parent_output - - @property - def data(self): - # NOTE: if the node is being evaluated, we must touch the node - # (trigger deep evaluation), else we get the data directly - if self.node.being_evaluated: - return self._parent_output.data - return self._parent_output.get_data_unsafe() - - def get_data_unsafe(self): - return self._parent_output.get_data_unsafe() - - @property - def dd(self): - return self._parent_output.dd - - @property - def tainted(self) -> bool: - return self._parent_output.tainted - - def touch(self): - return self._parent_output.touch() - - def taint(self, **kwargs) -> None: - self._node.taint(caller=self, **kwargs) - - def taint_type(self, *args, **kwargs) -> None: - self._node.taint_type(*args, **kwargs) - - def connected(self) -> bool: - return bool(self._parent_output) - - def deep_iter_inputs(self, disconnected_only=False): - if disconnected_only and self.connected(): - return iter(tuple()) - raise StopNesting(self) - - def deep_iter_child_outputs(self): - if self._child_output: - raise StopNesting(self._child_output) - return iter(tuple()) - - def __lshift__(self, other): - """ - self << other - """ - return lshift(self, other) - - def __rrshift__(self, other): - """ - other >> self - """ - return lshift(self, other) - - def allocate(self, **kwargs) -> bool: - if not self._allocatable or self.has_data: - return True - - if self.own_dd.shape is None or self.own_dd.dtype is None: - raise AllocationError( - "No shape/type information provided for the Input", - node=self._node, - output=self, - ) - try: - self._own_data = zeros(self.own_dd.shape, self.own_dd.dtype, **kwargs) - except Exception as exc: - raise AllocationError( - f"Input: {exc.args[0]}", node=self._node, input=self - ) from exc - - return True - - -class Inputs(EdgeContainer): - _dtype = Input - - def __init__(self, iterable=None): - super().__init__(iterable) - - def __str__(self): - return f"鈫抂{tuple(obj.name for obj in self)}]鈼�" - - def deep_iter_inputs( - self, disconnected_only: bool = False - ) -> Iterator[Input]: - for input in self: - if disconnected_only and input.connected(): - continue - yield input - - def deep_iter_child_outputs(self) -> Iterator[Union[Input, Output]]: - for child_output in self: - yield child_output.child_output - - def touch(self) -> None: - for input in self: - input.touch() diff --git a/subtrees/dagflow/dagflow/input_extra.py b/subtrees/dagflow/dagflow/input_extra.py deleted file mode 100644 index cb71207f024ed96ef50cec8121cb739173f9188e..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/input_extra.py +++ /dev/null @@ -1,163 +0,0 @@ -from typing import Optional, Union - -class SimpleFormatter(): - _base: str - _numfmt: str - def __init__(self, base: str, numfmt: str = '_{:02d}'): - self._base = base - self._numfmt = numfmt - - @staticmethod - def from_string(string: str): - if '{' in string: - return string - - return SimpleFormatter(string) - - def format(self, num: int) -> str: - if num>0: - return self._base+self._numfmt.format(num) - - return self._base - - -class MissingInputHandler: - """ - Handler to implement behaviour when output - is connected to the missing input with >>/<< - """ - - _node = None - - def __init__(self, node=None): - self.node = node - - @property - def node(self): - return self._node - - @node.setter - def node(self, node): - self._node = node - - def __call__(self, idx=None, scope=None): - pass - - -class MissingInputFail(MissingInputHandler): - """Default missing input handler: issues and exception""" - - def __init__(self, node=None): - super().__init__(node) - - def __call__(self, idx=None, scope=None): - raise RuntimeError( - "Unable to iterate inputs further. " - "No additional inputs may be created" - ) - - -class MissingInputAdd(MissingInputHandler): - """Adds an input for each output in >> operator""" - - input_fmt: Union[str,SimpleFormatter] = SimpleFormatter("input", "_{:02d}") - input_kws: dict - output_fmt: Union[str,SimpleFormatter] = SimpleFormatter("output", "_{:02d}") - output_kws: dict - - def __init__( - self, - node=None, - *, - input_fmt: Optional[Union[str,SimpleFormatter]] = None, - input_kws: Optional[dict] = None, - output_fmt: Optional[Union[str,SimpleFormatter]] = None, - output_kws: Optional[dict] = None, - ): - if input_kws is None: - input_kws = {} - if output_kws is None: - output_kws = {} - super().__init__(node) - self.input_kws = input_kws - self.output_kws = output_kws - if input_fmt is not None: - self.input_fmt = SimpleFormatter.from_string(input_fmt) - if output_fmt is not None: - self.output_fmt = SimpleFormatter.from_string(output_fmt) - - def __call__(self, idx=None, scope=None, **kwargs): - kwargs_combined = dict(self.input_kws, **kwargs) - return self.node._add_input( - self.input_fmt.format( - idx if idx is not None else len(self.node.inputs) - ), - **kwargs_combined, - ) - - -class MissingInputAddPair(MissingInputAdd): - """ - Adds an input for each output in >> operator. - Adds an output for each new input - """ - - def __init__(self, node=None, **kwargs): - super().__init__(node, **kwargs) - - def __call__(self, idx=None, scope=None): - idx_out = len(self.node.outputs) - out = self.node._add_output( - self.output_fmt.format(idx_out), **self.output_kws - ) - return super().__call__(idx, child_output=out, scope=scope) - - -class MissingInputAddOne(MissingInputAdd): - """ - Adds an input for each output in >> operator. - Adds only one output if needed - """ - - add_child_output = False - - def __init__(self, node=None, *, add_child_output: bool = False, **kwargs): - super().__init__(node, **kwargs) - self.add_child_output = add_child_output - - def __call__(self, idx=None, scope=None): - if (idx_out := len(self.node.outputs)) == 0: - out = self.node._add_output( - self.output_fmt.format(idx_out), **self.output_kws - ) - else: - out = self.node.outputs[-1] - if self.add_child_output: - return super().__call__(idx, child_output=out, scope=scope) - return super().__call__(idx, scope=scope) - - -class MissingInputAddEach(MissingInputAdd): - """ - Adds an output for each block (for each >> operation) - """ - - add_child_output = False - scope = 0 - - def __init__(self, node=None, *, add_child_output=False, **kwargs): - super().__init__(node, **kwargs) - self.add_child_output = add_child_output - - def __call__(self, idx=None, scope=None): - if scope == self.scope != 0: - out = self.node.outputs[-1] - else: - out = self.node._add_output( - self.output_fmt.format(len(self.node.outputs)), - **self.output_kws, - ) - self.scope = scope - if self.add_child_output: - return super().__call__(idx, child_output=out, scope=scope) - return super().__call__(idx, scope=scope) diff --git a/subtrees/dagflow/dagflow/iter.py b/subtrees/dagflow/dagflow/iter.py deleted file mode 100644 index 09eaef8d6ee13f497a4b1fe1a4d75fb32e0a9f1e..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/iter.py +++ /dev/null @@ -1,14 +0,0 @@ -from collections.abc import Iterable -from itertools import islice - -class StopNesting(Exception): - def __init__(self, object): - self.object = object - -def IsIterable(obj): - return isinstance(obj, Iterable) and not isinstance(obj, str) - -def nth(iterable, n): - "Returns the nth item or a default value" - return next(islice(iterable, n, None)) if n > -1 else tuple(iterable)[n] - diff --git a/subtrees/dagflow/dagflow/iterators.py b/subtrees/dagflow/dagflow/iterators.py deleted file mode 100644 index 7a4b59c0d75f851d037269125447e23ebd1806ce..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/iterators.py +++ /dev/null @@ -1,42 +0,0 @@ - -from .iter import IsIterable, StopNesting - - -def get_proper_iterator(obj, methodname, onerror, **kwargs): - if methodname: - if method := getattr(obj, methodname, None): - return method(**kwargs) - if IsIterable(obj): - return obj - raise RuntimeError( - f"Do not know how to get an iterator for '{onerror}'! " - f"{obj=}, {type(obj)=}" - ) - - -def deep_iterate(obj, methodname, onerror, **kwargs): - try: - iterable = get_proper_iterator(obj, methodname, onerror, **kwargs) - if isinstance(iterable, dict): - raise StopNesting(iterable) - for element in iterable: - yield from deep_iterate(element, methodname, onerror, **kwargs) - except StopNesting as sn: - yield sn.object - - -def iter_inputs(inputs, disconnected_only=False): - return deep_iterate( - inputs, - "deep_iter_inputs", - "inputs", - disconnected_only=disconnected_only, - ) - - -def iter_outputs(outputs): - return deep_iterate(outputs, "deep_iter_outputs", "outputs") - - -def iter_child_outputs(inputs): - return deep_iterate(inputs, "deep_iter_child_outputs", "child_outputs") diff --git a/subtrees/dagflow/dagflow/legs.py b/subtrees/dagflow/dagflow/legs.py deleted file mode 100644 index 91c1e204e6d3000dd840af983e8d865ee78f99b0..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/legs.py +++ /dev/null @@ -1,107 +0,0 @@ - -from . import input_extra -from .input import Inputs -from .output import Outputs -from .shift import lshift, rshift -from .iter import StopNesting - -class Legs: - inputs: Inputs - outputs: Outputs - def __init__(self, inputs=None, outputs=None, missing_input_handler=None): - self._missing_input_handler = missing_input_handler - self.inputs = Inputs(inputs) - self.outputs = Outputs(outputs) - - @property - def _missing_input_handler(self): - return self.__missing_input_handler - - @_missing_input_handler.setter - def _missing_input_handler(self, handler): - if handler: - if isinstance(handler, str): - sethandler = getattr(input_extra, handler)(self) - elif isinstance(handler, type): - sethandler = handler(self) - else: - sethandler = handler - sethandler.node = self - elif hasattr(self, 'missing_input_handler'): - sethandler = self.missing_input_handler - else: - sethandler = input_extra.MissingInputFail(self) - self.__missing_input_handler = sethandler - - def __getitem__(self, key): - if isinstance(key, (int, slice, str)): - return self.outputs[key] - if (y := len(key)) != 2: - raise ValueError(f"Legs key should be of length 2, but given {y}!") - ikey, okey = key - if ikey and okey: - if isinstance(ikey, (int, str)): - ikey = (ikey,) - if isinstance(okey, (int, str)): - okey = (okey,) - return Legs( - self.inputs[ikey], - self.outputs[okey], - missing_input_handler=self.__missing_input_handler, - ) - if ikey: - return self.inputs[ikey] - if okey: - return self.outputs[okey] - raise ValueError("Empty keys specified") - - def get(self, key, default = None): - try: - return self.__getitem__(key) - except Exception: - return default - - def __str__(self) -> str: - return f"鈫抂{len(self.inputs)}],[{len(self.outputs)}]鈫�" - - def __repr__(self) -> str: - return self.__str__() - - def deep_iter_outputs(self): - return iter(self.outputs) - - def deep_iter_inputs(self, disconnected_only=False): - return iter(self.inputs) - - def deep_iter_child_outputs(self): - raise StopNesting(self) - - def print(self): - for i, input in enumerate(self.inputs): - print(i, input) - for i, output in enumerate(self.outputs): - print(i, output) - - def __rshift__(self, other): - """ - self >> other - """ - return rshift(self, other) - - def __rlshift__(self, other): - """ - other << self - """ - return rshift(self, other) - - def __lshift__(self, other): - """ - self << other - """ - return lshift(self, other) - - def __rrshift__(self, other): - """ - other >> self - """ - return lshift(self, other) diff --git a/subtrees/dagflow/dagflow/lib/Array.py b/subtrees/dagflow/dagflow/lib/Array.py deleted file mode 100644 index da6cdeaa30807834472a4ef1456d28ebde7bb575..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Array.py +++ /dev/null @@ -1,65 +0,0 @@ -from numpy import array - -from ..nodes import FunctionNode -from ..output import Output -from ..exception import InitializationError - -from numpy.typing import ArrayLike, NDArray -from typing import Optional - -class Array(FunctionNode): - """Creates a node with a single data output with predefined array""" - - _mode: str - _data: NDArray - _output = Output - def __init__(self, name, arr, *, - mode: str="store", - outname="array", - mark: Optional[str]=None, - **kwargs - ): - super().__init__(name, **kwargs) - self._mode = mode - if mark is not None: - self._mark = mark - self._data = array(arr, copy=True) - - if mode=='store': - self._output = self._add_output(outname, data=self._data) - elif mode=='store_weak': - self._output = self._add_output(outname, data=self._data, owns_buffer=False) - elif mode=='fill': - self._output = self._add_output(outname, dtype=self._data.dtype, shape=self._data.shape) - else: - raise InitializationError(f'Array: invalid mode "{mode}"', node=self) - - self._functions.update({ - "store": self._fcn_store, - "store_weak": self._fcn_store, - "fill": self._fcn_fill - }) - self.fcn = self._functions[self._mode] - - if mode=='store': - self.close() - - def _fcn_store(self, *args): - return self._data - - def _fcn_fill(self, *args): - data = self._output._data - data[:] = self._data - return data - - def _typefunc(self) -> None: - pass - - def _post_allocate(self) -> None: - if self._mode=='fill': - return - - self._data = self._output._data - - def set(self, data: ArrayLike, check_taint: bool=False) -> bool: - return self._output.set(data, check_taint) diff --git a/subtrees/dagflow/dagflow/lib/Cholesky.py b/subtrees/dagflow/dagflow/lib/Cholesky.py deleted file mode 100644 index a4c3c12bd4121b529a3a7d4963c54501d7633596..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Cholesky.py +++ /dev/null @@ -1,60 +0,0 @@ -from ..input_extra import MissingInputAddPair -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - copy_input_to_output, - check_input_square_or_diag -) -from scipy.linalg import cholesky -from numpy import sqrt - -class Cholesky(FunctionNode): - """Compute the Cholesky decomposition of a matrix V=LL虄岬€ - 1d input is considered to be a diagonal of square matrix""" - _mark: str = 'V鈫扡' - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddPair(input_fmt='matrix', output_fmt='L') - ) - super().__init__(*args, **kwargs) - - self._functions.update({ - "square": self._fcn_square, - "diagonal": self._fcn_diagonal - }) - - def _fcn_square(self, _, inputs, outputs): - """Compute Cholesky decomposition using `scipy.linalg.cholesky` - NOTE: inplace computation (`overwrite_a=True`) works only for - the F-based arrays. As soon as by default C-arrays are used, - transposition produces an F-array (view). Transposition with - `lower=False` produces a lower matrix in the end. - """ - inputs.touch() - - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - output[:] = input - cholesky(output.T, overwrite_a=True, lower=False) # produces L (!) inplace - # output[:]=cholesky(input, lower=True) - - def _fcn_diagonal(self, _, inputs, outputs): - """Compute "Cholesky" decomposition using of a diagonal of a square matrix. - Elementwise sqrt is used. - """ - inputs.touch() - - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - sqrt(input, out=output) - - def _typefunc(self) -> None: - check_has_inputs(self) - ndim = check_input_square_or_diag(self, slice(None)) - copy_input_to_output(self, slice(None), slice(None)) - - if ndim==2: - self.fcn = self._functions["square"] - self._mark = 'V鈫扡' - else: - self.fcn = self._functions["diagonal"] - self._mark = 'sqrt(V岬�)' - diff --git a/subtrees/dagflow/dagflow/lib/Concatenation.py b/subtrees/dagflow/dagflow/lib/Concatenation.py deleted file mode 100644 index ca9215db22da0e04bad8af40d43066715754c9c6..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Concatenation.py +++ /dev/null @@ -1,28 +0,0 @@ -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - combine_inputs_shape_to_output, - eval_output_dtype, -) - - -class Concatenation(FunctionNode): - """Creates a node with a single data output from all the inputs data""" - - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - combine_inputs_shape_to_output(self, slice(None), "result") - eval_output_dtype(self, slice(None), "result") - - def _fcn(self, _, inputs, outputs): - res = outputs["result"].data - res[:] = (inp.data for inp in inputs) - return res diff --git a/subtrees/dagflow/dagflow/lib/CovmatrixFromCormatrix.py b/subtrees/dagflow/dagflow/lib/CovmatrixFromCormatrix.py deleted file mode 100644 index 61d2834db8af4103e53313866485d490beed813f..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/CovmatrixFromCormatrix.py +++ /dev/null @@ -1,39 +0,0 @@ -from ..nodes import FunctionNode -from ..typefunctions import ( - check_input_square, - copy_input_to_output, - check_input_dimension, - check_inputs_multiplicable_mat -) - -from numpy import multiply - -class CovmatrixFromCormatrix(FunctionNode): - """Compute covariance matrix from correlation matrix: - V鈧栤倶=C鈧栤倶蟽鈧栂冣倶 - """ - - _mode: str - _mark: str = 'C鈫扸' - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._add_pair("matrix", "matrix", output_kws={'positional': True}) - self._add_input("sigma", positional=False) - - def _fcn(self, _, inputs, outputs): - inputs.touch() - C = inputs["matrix"].data - sigma = inputs["sigma"].data - - V = outputs["matrix"].data - - multiply(C, sigma[None,:], out=V) - multiply(V, sigma[:,None], out=V) - - def _typefunc(self) -> None: - check_input_square(self, 'matrix') - check_input_dimension(self, 'sigma', 1) - check_inputs_multiplicable_mat(self, 'matrix', 'sigma') - copy_input_to_output(self, slice(None), slice(None)) - diff --git a/subtrees/dagflow/dagflow/lib/Division.py b/subtrees/dagflow/dagflow/lib/Division.py deleted file mode 100644 index b93aef1359294e9fd692a2a0fea2d05df1364384..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Division.py +++ /dev/null @@ -1,32 +0,0 @@ -from numpy import copyto - -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - copy_input_shape_to_output, -) - -class Division(FunctionNode): - """Division of all the inputs together""" - - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - out = outputs[0].data - copyto(out, inputs[0].data.copy()) - if len(inputs) > 1: - for input in inputs[1:]: - out /= input.data - return out - - def _typefunc(self): - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - copy_input_shape_to_output(self, 0, "result") - eval_output_dtype(self, slice(None), "result") diff --git a/subtrees/dagflow/dagflow/lib/ElSumSq.py b/subtrees/dagflow/dagflow/lib/ElSumSq.py deleted file mode 100644 index ef46e7f896bcf559781d809985fac5fe5d1b8749..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/ElSumSq.py +++ /dev/null @@ -1,43 +0,0 @@ -from numpy import ndarray -from numpy.typing import NDArray - -from numba import njit -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - check_inputs_same_dtype, - AllPositionals -) - -@njit(cache=True) -def _sumsq(data: NDArray, out: NDArray): - sm = 0.0 - for v in data: - sm+=v*v - out[0]+=sm - -class ElSumSq(FunctionNode): - """Sum of the squared of all the inputs""" - - _buffer: ndarray - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - out = outputs["result"].data - out[0] = 0.0 - for input in inputs: - _sumsq(input.data, out) - return out - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - check_inputs_same_dtype(self) - eval_output_dtype(self, AllPositionals, "result") - self.outputs[0].dd.shape=(1,) diff --git a/subtrees/dagflow/dagflow/lib/Integrator.py b/subtrees/dagflow/dagflow/lib/Integrator.py deleted file mode 100644 index aa0597af9b9e68ae379457cc3026adfa9e9eb1bb..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Integrator.py +++ /dev/null @@ -1,172 +0,0 @@ -from typing import Literal - -from numba import njit -from numpy import floating, integer, issubdtype, multiply, zeros -from numpy.typing import NDArray - -from ..exception import InitializationError, TypeFunctionError -from ..input_extra import MissingInputAddEach -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - check_input_dimension, - check_input_dtype, - check_input_shape, -) - - -@njit(cache=True) -def _integrate1d(data: NDArray, weighted: NDArray, ordersX: NDArray): - """ - Summing up `weighted` within `ordersX` and puts the result into `data`. - The 1-dimensional version of integration. - """ - iprev = 0 - for i, order in enumerate(ordersX): - inext = iprev + order - data[i] = weighted[iprev:inext].sum() - iprev = inext - - -@njit(cache=True) -def _integrate2d( - data: NDArray, weighted: NDArray, ordersX: NDArray, ordersY: NDArray -): - """ - Summing up `weighted` within `ordersX` and `ordersY` and then - puts the result into `data`. The 2-dimensional version of integration. - """ - iprev = 0 - for i, orderx in enumerate(ordersX): - inext = iprev + orderx - jprev = 0 - for j, ordery in enumerate(ordersY): - jnext = jprev + ordery - data[i, j] = weighted[iprev:inext, jprev:jnext].sum() - jprev = jnext - iprev = inext - - -class Integrator(FunctionNode): - """ - The `Integrator` node performs integration (summation) - of every input within the `weight`, `ordersX` and `ordersY` (for `2d` mode). - - The `Integrator` has two modes: `1d` and `2d`. - The `mode` must be set in the constructor, while `precision=dtype` - of integration is chosen *automaticly* in the type function. - - For `2d` integration the `ordersY` input must be connected. - - Note that the `Integrator` preallocates temporary buffer. - For the integration algorithm the `Numba`_ package is used. - - .. _Numba: https://numba.pydata.org - """ - - def __init__(self, *args, mode: Literal["1d", "2d"], **kwargs): - kwargs.setdefault("missing_input_handler", MissingInputAddEach()) - super().__init__(*args, **kwargs) - if mode not in {"1d", "2d"}: - raise InitializationError( - f"Argument `mode` must be '1d' or '2d', but given '{mode}'!", - node=self, - ) - self._mode = mode - if self._mode == "2d": - self._add_input("ordersY", positional=False) - self._add_input("weights", positional=False) - self._add_input("ordersX", positional=False) - self._functions.update({"1d": self._fcn_1d, "2d": self._fcn_2d}) - - @property - def mode(self) -> str: - return self._mode - - def _typefunc(self) -> None: - """ - The function to determine the dtype and shape. - Checks inputs dimension and, selects an integration algorithm, - determines dtype and shape for outputs - """ - check_has_inputs(self) - check_has_inputs(self, ("ordersX", "weights")) - input0 = self.inputs[0] - ndim = len(input0.dd.shape) - if ndim != int(self.mode[:1]): - raise TypeFunctionError( - f"The Integrator works only with {self.mode} inputs, but one has ndim={ndim}!", - node=self, - ) - check_input_dimension(self, (slice(None), "weights"), ndim) - check_input_dimension(self, "ordersX", 1) - check_input_shape(self, (slice(None), "weights"), input0.dd.shape) - ordersX = self.inputs["ordersX"] - if not issubdtype(ordersX.dd.dtype, integer): - raise TypeFunctionError( - "The `ordersX` must be array of integers, but given '{ordersX.dd.dtype}'!", - node=self, - input=ordersX, - ) - dtype = input0.dd.dtype - if not issubdtype(dtype, floating): - raise TypeFunctionError( - "The Integrator works only within `float` or `double` " - f"precision, but given '{dtype}'!", - node=self, - ) - check_input_dtype(self, (slice(None), "weights"), dtype) - if sum(ordersX.data) != input0.dd.shape[0]: - raise TypeFunctionError( - "ordersX must be consistent with inputs shape, " - f"but given {ordersX.data=} and {input0.dd.shape=}!", - node=self, - input=ordersX, - ) - if self.mode == "2d": - check_has_inputs(self, "ordersY") - check_input_dimension(self, "ordersY", 1) - ordersY = self.inputs["ordersY"] - if not issubdtype(ordersY.dd.dtype, integer): - raise TypeFunctionError( - "The `ordersY` must be array of integers, but given '{ordersY.dd.dtype}'!", - node=self, - input=ordersY, - ) - if sum(ordersY.data) != input0.dd.shape[1]: - raise TypeFunctionError( - "ordersY must be consistent with inputs shape, " - f"but given {ordersY.data=} and {input0.dd.shape=}!", - node=self, - input=ordersX, - ) - self.fcn = self._functions[self.mode] - for output in self.outputs: - output.dd.dtype = dtype - output.dd.shape = input0.dd.shape - - def _post_allocate(self): - """Allocates the `buffer` within `weights`""" - weights = self.inputs["weights"] - self.__buffer = zeros(shape=weights.dd.shape, dtype=weights.dd.dtype) - - def _fcn_1d(self, _, inputs, outputs): - """1d version of integration function""" - weights = inputs["weights"].data - ordersX = inputs["ordersX"].data - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - multiply(input, weights, out=self.__buffer) - _integrate1d(output, self.__buffer, ordersX) - if self.debug: - return [outputs.iter_data()] - - def _fcn_2d(self, _, inputs, outputs): - """2d version of integration function""" - weights = inputs["weights"].data - ordersX = inputs["ordersX"].data - ordersY = inputs["ordersY"].data - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - multiply(input, weights, out=self.__buffer) - _integrate2d(output, self.__buffer, ordersX, ordersY) - if self.debug: - return [outputs.iter_data()] diff --git a/subtrees/dagflow/dagflow/lib/NormalizeCorrelatedVars.py b/subtrees/dagflow/dagflow/lib/NormalizeCorrelatedVars.py deleted file mode 100644 index 05c9126195d88421502391acb83780586eaa77ae..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/NormalizeCorrelatedVars.py +++ /dev/null @@ -1,92 +0,0 @@ -from ..input_extra import MissingInputAddPair -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - check_input_square_or_diag, - copy_input_to_output, - check_input_dimension, - check_inputs_equivalence, - check_inputs_multiplicable_mat -) -from ..exception import InitializationError - -from scipy.linalg import solve_triangular -from numpy import matmul, subtract, divide, multiply, add - -class NormalizeCorrelatedVars(FunctionNode): - """Normalize correlated variables or correlate normal variables with linear expression - - If x is a vector of values, 渭 are the central values and L is a cholesky decomposition - of the covariance matrix V=LL岬€ then - z = L鈦宦�(x - 渭) - x = Lz + 渭 - """ - - _mode: str - def __init__(self, *args, mode='forward', **kwargs): - if mode=='forward': - self._mark = 'c鈫抲' - elif mode=='backward': - self._mark = 'u鈫抍' - else: - raise InitializationError(f'Invalid NormalizeCorrelatedVars mode={mode}. Expect "forward" or "backward"',node=self) - - self._mode = mode - - super().__init__(*args, missing_input_handler=MissingInputAddPair(), **kwargs) - - self._add_input("matrix", positional=False) - self._add_input("central", positional=False) - - self._functions.update({ - "forward_2d": self._fcn_forward_2d, - "backward_2d": self._fcn_backward_2d, - "forward_1d": self._fcn_forward_1d, - "backward_1d": self._fcn_backward_1d - }) - - def _fcn_forward_2d(self, _, inputs, outputs): - inputs.touch() - L = inputs["matrix"].data - central = inputs["central"].data - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - subtract(input, central, out=output) - solve_triangular(L, output, lower=True, overwrite_b=True, check_finite=False) - - def _fcn_backward_2d(self, _, inputs, outputs): - inputs.touch() - L = inputs["matrix"].data - central = inputs["central"].data - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - matmul(L, input, out=output) - add(output, central, out=output) - - def _fcn_forward_1d(self, _, inputs, outputs): - inputs.touch() - Ldiag = inputs["matrix"].data - central = inputs["central"].data - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - subtract(input, central, out=output) - divide(output, Ldiag, out=output) - - def _fcn_backward_1d(self, _, inputs, outputs): - inputs.touch() - Ldiag = inputs["matrix"].data - central = inputs["central"].data - for input, output in zip(inputs.iter_data(), outputs.iter_data()): - multiply(Ldiag, input, out=output) - add(output, central, out=output) - - def _typefunc(self) -> None: - check_has_inputs(self) - ndim = check_input_square_or_diag(self, 'matrix') - check_input_dimension(self, 'central', 1) - check_inputs_equivalence(self, ('central', slice(None))) - check_inputs_multiplicable_mat(self, 'matrix', slice(None)) - copy_input_to_output(self, slice(None), slice(None)) - - key = f"{self._mode}_{ndim}d" - try: - self.fcn = self._functions[key] - except KeyError: - raise InitializationError(f'Invalid mode "{key}". Expect: {self._functions.keys()}') diff --git a/subtrees/dagflow/dagflow/lib/NormalizeCorrelatedVars2.py b/subtrees/dagflow/dagflow/lib/NormalizeCorrelatedVars2.py deleted file mode 100644 index 2d6cb853c751935170f1e15be7bb739517fb558b..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/NormalizeCorrelatedVars2.py +++ /dev/null @@ -1,140 +0,0 @@ -from ..nodes import FunctionNode -from ..node import Input, Output -from ..typefunctions import ( - check_has_inputs, - check_input_square_or_diag, - copy_input_to_output, - check_input_dimension, - check_inputs_equivalence, - check_inputs_multiplicable_mat -) - -from scipy.linalg import solve_triangular -from numpy import matmul, subtract, divide, multiply, add, zeros, copyto - -class NormalizeCorrelatedVars2(FunctionNode): - """Normalize correlated variables or correlate normal variables with linear expression - - If x is a vector of values, 渭 are the central values and L is a cholesky decomposition - of the covariance matrix V=LL岬€ then - z = L鈦宦�(x - 渭) - x = Lz + 渭 - """ - - _mark: str = 'c鈫攗' - - _input_value: Input - _input_normvalue: Input - _output_value: Output - _output_normvalue: Output - - _ndim: str - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._add_input("matrix", positional=False) - self._add_input("central", positional=False) - - self._input_value, self._output_value = self._add_pair( - "value", "value", - input_kws={'allocatable': True}, - output_kws={'forbid_reallocation': True, 'allocatable': False}, - ) - self._input_normvalue, self._output_normvalue = self._add_pair( - "normvalue", "normvalue", - input_kws={'allocatable': True}, - output_kws={'forbid_reallocation': True, 'allocatable': False}, - ) - - self._functions.update({ - "forward_2d": self._fcn_forward_2d, - "forward_1d": self._fcn_forward_1d, - "backward_2d": self._fcn_backward_2d, - "backward_1d": self._fcn_backward_1d, - }) - - def _fcn_forward_2d(self, _, inputs, outputs): - inputs.touch() - L = inputs["matrix"].data - central = inputs["central"].data - - input_value = inputs["value"].data - output_value = outputs["value"].data - output_normvalue = outputs["normvalue"].data - - subtract(input_value, central, out=output_normvalue) - solve_triangular(L, output_normvalue, lower=True, overwrite_b=True, check_finite=False) - copyto(output_value, input_value) - - def _fcn_backward_2d(self, _, inputs, outputs): - inputs.touch() - L = inputs["matrix"].data - central = inputs["central"].data - - input_normvalue = inputs["normvalue"].data - output_normvalue = outputs["normvalue"].data - output_value = outputs["value"].data - - matmul(L, input_normvalue, out=output_value) - add(output_value, central, out=output_value) - copyto(output_normvalue, input_normvalue) - - def _fcn_forward_1d(self, _, inputs, outputs): - inputs.touch() - Ldiag = inputs["matrix"].data - central = inputs["central"].data - - input_value = inputs["value"].data - output_value = outputs["value"].data - output_normvalue = outputs["normvalue"].data - - subtract(input_value, central, out=output_normvalue) - divide(output_normvalue, Ldiag, out=output_normvalue) - copyto(output_value, input_value) - - def _fcn_backward_1d(self, _, inputs, outputs): - inputs.touch() - Ldiag = inputs["matrix"].data - central = inputs["central"].data - - input_normvalue = inputs["normvalue"].data - output_normvalue = outputs["normvalue"].data - output_value = outputs["value"].data - - multiply(Ldiag, input_normvalue, out=output_value) - add(output_value, central, out=output_value) - copyto(output_normvalue, input_normvalue) - - def _on_taint(self, caller: Input) -> None: - """Choose the function to call based on the modified input: - - if normvalue is modified, the value should be updated - - if value is modified, the normvalue should be updated - - if sigma or central is modified, the normvalue should be updated - - TODO: - - implement partial taintflag propagation - - value should not be tainted on sigma/central modificantion - """ - if caller is self._input_normvalue: - self.fcn = self._functions[f"backward_{self._ndim}"] - else: - self.fcn = self._functions[f"forward_{self._ndim}"] - - def _typefunc(self) -> None: - check_has_inputs(self) - ndim = check_input_square_or_diag(self, 'matrix') - check_input_dimension(self, 'central', 1) - check_inputs_equivalence(self, ('central', slice(None))) - check_inputs_multiplicable_mat(self, 'matrix', slice(None)) - copy_input_to_output(self, slice(None), slice(None)) - - self._ndim=f"{ndim}d" - self.fcn = self._functions[f"forward_{self._ndim}"] - - self._valuedata = zeros(shape=self._input_value.dd.shape, dtype=self._input_value.dd.dtype) - self._normvaluedata = zeros(shape=self._input_normvalue.dd.shape, dtype=self._input_normvalue.dd.dtype) - self._input_value.set_own_data(self._valuedata, owns_buffer=False) - self._input_normvalue.set_own_data(self._normvaluedata, owns_buffer=False) - self._output_value._set_data(self._valuedata, owns_buffer=False, forbid_reallocation=True) - self._output_normvalue._set_data(self._normvaluedata, owns_buffer=False, forbid_reallocation=True) diff --git a/subtrees/dagflow/dagflow/lib/Product.py b/subtrees/dagflow/dagflow/lib/Product.py deleted file mode 100644 index 967ed1641a4444d71fa37671e01a3bb1f1ece264..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Product.py +++ /dev/null @@ -1,35 +0,0 @@ -from numpy import copyto - -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - copy_input_shape_to_output, - check_inputs_equivalence, - AllPositionals -) - -class Product(FunctionNode): - """Product of all the inputs together""" - - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - out = outputs["result"].data - copyto(out, inputs[0].data) - if len(inputs) > 1: - for input in inputs[1:]: - out *= input.data - return out - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - copy_input_shape_to_output(self, 0, "result") - check_inputs_equivalence(self) - eval_output_dtype(self, AllPositionals, "result") diff --git a/subtrees/dagflow/dagflow/lib/Sum.py b/subtrees/dagflow/dagflow/lib/Sum.py deleted file mode 100644 index 4735728a501310c800de304e8952e01c84e03c8f..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/Sum.py +++ /dev/null @@ -1,35 +0,0 @@ -from numpy import copyto, add - -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - copy_input_shape_to_output, - check_inputs_equivalence, - AllPositionals -) - -class Sum(FunctionNode): - """Sum of all the inputs together""" - - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - out = outputs["result"].data - copyto(out, inputs[0].data) - if len(inputs) > 1: - for input in inputs[1:]: - add(out, input.data, out=out) - return out - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - copy_input_shape_to_output(self, 0, "result") - check_inputs_equivalence(self) - eval_output_dtype(self, AllPositionals, "result") diff --git a/subtrees/dagflow/dagflow/lib/SumMatOrDiag.py b/subtrees/dagflow/dagflow/lib/SumMatOrDiag.py deleted file mode 100644 index bd3ed648c87fb69615c96ff1391b3936e6449bee..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/SumMatOrDiag.py +++ /dev/null @@ -1,81 +0,0 @@ -from numpy import copyto, add -from numpy.typing import NDArray -from numba import njit - -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - copy_input_shape_to_output, - check_inputs_square_or_diag, - check_inputs_same_dtype, - AllPositionals -) - -@njit(cache=True) -def _settodiag1(inarray: NDArray, outmatrix: NDArray): - for i in range(inarray.size): - outmatrix[i, i] = inarray[i] - -@njit(cache=True) -def _addtodiag(inarray: NDArray, outmatrix: NDArray): - for i in range(inarray.size): - outmatrix[i, i] += inarray[i] - -class SumMatOrDiag(FunctionNode): - """Sum of all the inputs together. Inputs are square matrices or diagonals of square matrices""" - - _ndim: int = 0 - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - self._functions.update({ - "2d": self._fcn2d, - "1d": self._fcn1d, - }) - - def _fcn2d(self, _, inputs, outputs): - out = outputs["result"].data - inp = inputs[0].data - if len(inp.shape)==1: - _settodiag1(inp, out) - else: - out[:] = inp - if len(inputs) > 1: - for input in inputs[1:]: - if len(input.dd.shape)==1: - _addtodiag(input.data, out) - else: - add(input.data, out, out=out) - return out - - def _fcn1d(self, _, inputs, outputs): - out = outputs["result"].data - copyto(out, inputs[0].data) - if len(inputs) > 1: - for input in inputs[1:]: - add(out, input.data, out=out) - return out - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - copy_input_shape_to_output(self, 0, "result") - self._ndim = check_inputs_square_or_diag(self) - check_inputs_same_dtype(self) - eval_output_dtype(self, AllPositionals, "result") - - size = self.inputs[0].dd.shape[0] - output = self.outputs[0] - if self._ndim==2: - output.dd.shape = size, size - elif self._ndim==1: - output.dd.shape = size, - else: - assert False - - self.fcn = self._functions[f"{self._ndim}d"] diff --git a/subtrees/dagflow/dagflow/lib/SumSq.py b/subtrees/dagflow/dagflow/lib/SumSq.py deleted file mode 100644 index 8b9ba01e6e5fc9fdea9accbc69548ccc6cdb0a08..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/SumSq.py +++ /dev/null @@ -1,40 +0,0 @@ -from numpy import add, square, ndarray, empty_like - -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - copy_input_shape_to_output, - check_inputs_equivalence, - AllPositionals -) - -class SumSq(FunctionNode): - """Sum of the squared of all the inputs""" - - _buffer: ndarray - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - out = outputs["result"].data - square(inputs[0].data, out=out) - if len(inputs) > 1: - for input in inputs[1:]: - square(input.data, out=self._buffer) - add(self._buffer, out, out=out) - return out - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - copy_input_shape_to_output(self, 0, "result") - check_inputs_equivalence(self) - eval_output_dtype(self, AllPositionals, "result") - - def _post_allocate(self) -> None: - self._buffer = empty_like(self.inputs[0].get_data_unsafe()) diff --git a/subtrees/dagflow/dagflow/lib/View.py b/subtrees/dagflow/dagflow/lib/View.py deleted file mode 100644 index fbb8425a976aca7cd971fec210fa2f3bdd474227..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/View.py +++ /dev/null @@ -1,32 +0,0 @@ -from ..nodes import FunctionNode -from ..typefunctions import ( - copy_input_dtype_to_output, - copy_input_shape_to_output, -) - -class View(FunctionNode): - """Creates a node with a single data output which is a view on the input""" - - def __init__(self, name, outname="view", **kwargs): - super().__init__(name, **kwargs) - output = self._add_output( - outname, allocatable=False, forbid_reallocation=True - ) - self._add_input("input", child_output=output) - - def _fcn(self, _, inputs, outputs): - return self.inputs[0].data - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - copy_input_dtype_to_output(self, 0, 0) - copy_input_shape_to_output(self, 0, 0) - - def _post_allocate(self) -> None: - input = self.inputs[0] - output = self.outputs[0] - output._set_data( - input.parent_output._data, - owns_buffer=False, - forbid_reallocation=True, - ) diff --git a/subtrees/dagflow/dagflow/lib/ViewConcat.py b/subtrees/dagflow/dagflow/lib/ViewConcat.py deleted file mode 100644 index 9ec8326916ca4bcb9ca278d61b14054c0a18add6..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/ViewConcat.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import List, Optional - -from numpy import zeros - -from ..nodes import FunctionNode -from ..output import Output -from ..typefunctions import check_input_dimension, check_input_dtype - - -class ViewConcat(FunctionNode): - """Creates a node with a single data output which is a concatenated memory of the inputs""" - - _output: Output - _offsets: List[int] - - def __init__(self, name, outname="concat", **kwargs): - super().__init__(name, **kwargs) - self._output = self._add_output( - outname, allocatable=False, forbid_reallocation=True - ) - self._offsets = [] - - def missing_input_handler( - self, idx: Optional[int] = None, scope: Optional[int] = None - ): - icount = len(self.inputs) - idx = idx if idx is not None else icount - iname = "input_{:02d}".format(idx) - - kwargs = {"child_output": self._output} - return self._add_input(iname, allocatable=True, **kwargs) - - def _fcn(self, _, inputs, outputs): - self.inputs.touch() - return self._output._data - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - size = 0 - self._offsets = [] - cdtype = self.inputs[0].dd.dtype - check_input_dtype(self, slice(None), cdtype) - check_input_dimension(self, slice(None), 1) - for input in self.inputs: - self._offsets.append(size) - size += input.dd.shape[0] - - output = self.outputs[0] - output.dd.dtype = cdtype - output.dd.shape = (size,) - data = zeros(shape=size, dtype=cdtype) - output._set_data(data, owns_buffer=True) - - for offset, input in zip(self._offsets, self.inputs): - size = input.dd.shape[0] - idata = data[offset : offset + size] - input.set_own_data(idata, owns_buffer=False) diff --git a/subtrees/dagflow/dagflow/lib/WeightedSum.py b/subtrees/dagflow/dagflow/lib/WeightedSum.py deleted file mode 100644 index dcf929a11c289f5e083965cffd79b28e975ba932..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/WeightedSum.py +++ /dev/null @@ -1,73 +0,0 @@ -from numpy import copyto - -from ..exception import TypeFunctionError -from ..input_extra import MissingInputAddOne -from ..nodes import FunctionNode -from ..typefunctions import ( - check_has_inputs, - eval_output_dtype, - copy_input_shape_to_output, -) - - -class WeightedSum(FunctionNode): - """Weighted sum of all the inputs together""" - - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - self._add_input("weight", positional=False) - self._functions.update( - {"number": self._fcn_number, "iterable": self._fcn_iterable} - ) - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - check_has_inputs(self) - weight = self.inputs["weight"] - shape = weight.dd.shape[0] - leninp = len(self.inputs) - if shape == 0: - raise TypeFunctionError( - "Cannot use WeightedSum with empty 'weight'!" - ) - elif shape == 1: - self.fcn = self._functions["number"] - elif shape == leninp: - self.fcn = self._functions["iterable"] - else: - raise TypeFunctionError( - f"The number of weights (={shape}) must coincide " - f"with the number of inputs (={leninp})!" - ) - copy_input_shape_to_output(self, 0, "result") - eval_output_dtype(self, slice(None), "result") - - def _fcn_number(self, _, inputs, outputs): - """ - The function for one weight for all inputs: - `len(weight) == 1` - """ - out = outputs[0].data - weight = self.inputs["weight"].data - copyto(out, inputs[0].data.copy()) - if len(inputs) > 1: - for input in inputs[1:]: - out += input.data - out *= weight - return out - - def _fcn_iterable(self, _, inputs, outputs): - """ - The function for one weight for every input: - `len(weight) == len(inputs)` - """ - out = outputs[0].data - weights = self.inputs["weight"].data - copyto(out, inputs[0].data * weights[0]) - if len(inputs) > 1: - for input, weight in zip(inputs[1:], weights[1:]): - out += input.data * weight - return out diff --git a/subtrees/dagflow/dagflow/lib/__init__.py b/subtrees/dagflow/dagflow/lib/__init__.py deleted file mode 100644 index dd731c0c80051ce8b37281492f01cfcacbd61dcb..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/lib/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .Array import Array -from .Sum import Sum -from .Product import Product -from .Division import Division -from .Concatenation import Concatenation -from .WeightedSum import WeightedSum diff --git a/subtrees/dagflow/dagflow/logger.py b/subtrees/dagflow/dagflow/logger.py deleted file mode 100644 index 8d9e91a0186590e9c340ce1c1713eed2fbf004de..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/logger.py +++ /dev/null @@ -1,42 +0,0 @@ -from logging import ( - DEBUG, - INFO, - FileHandler, - Formatter, - Logger, - StreamHandler, - getLogger, -) -from typing import Optional - -# To avoid a creation of duplicates save an instance -_loggers = {} - - -def get_logger( - filename: Optional[str] = None, debug: bool = False, **kwargs -) -> Logger: - name = kwargs.pop("name", "PyGNA") - if logger := _loggers.get(name): - return logger - logger = getLogger("PyGNA") - formatstr = ( - fmtstr - if (fmtstr := kwargs.pop("formatstr", False)) - else "%(asctime)s - %(levelname)s - %(message)s" - ) - level = DEBUG if debug else INFO - logger.setLevel(level) - formatter = Formatter(formatstr) - if filename: - fh = FileHandler(filename) - fh.setLevel(level) - fh.setFormatter(formatter) - logger.addHandler(fh) - if kwargs.pop("console", True): - ch = StreamHandler() - ch.setLevel(level) - ch.setFormatter(formatter) - logger.addHandler(ch) - _loggers[name] = logger - return logger diff --git a/subtrees/dagflow/dagflow/membernode.py b/subtrees/dagflow/dagflow/membernode.py deleted file mode 100644 index a0705fe3dd43f5ab352fb5acf54ce704dfed2c7b..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/membernode.py +++ /dev/null @@ -1,106 +0,0 @@ -from .graph import Graph -from .node import Node - -from typing import Optional - -class MemberNodesHolder: - _graph: Optional[Graph] = None - - def __init__(self, graph: Graph=None): - self.graph = graph - for key in dir(self): - val = getattr(self, key) - if isinstance(val, Node): - val.obj = self - val.graph = self._graph - - @property - def graph(self): - return self._graph - - @graph.setter - def graph(self, graph, **kwargs): - if self._graph: - raise ValueError("Graph is already set") - if graph is True: - self._graph = Graph() - elif isinstance(graph, str): - self._graph = Graph(label=graph) - elif isinstance(graph, dict): - self._graph = Graph(**kwargs) - elif graph: - self._graph = graph - - -class MemberNode(Node): - """Function signature: fcn(master, node, inputs, outputs)""" - - _obj = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _eval(self): - self._being_evaluated = True - ret = self._fcn(self._obj, self, self.inputs, self.outputs) - self._being_evaluated = False - return ret - - @property - def obj(self): - return self._obj - - @obj.setter - def obj(self, obj): - self._obj = obj - - def _stash_fcn(self): - prev_fcn = self._fcn - self._fcn_chain.append(prev_fcn) - return lambda node, inputs, outputs: prev_fcn( - node._obj, node, inputs, outputs - ) - - def _make_wrap(self, prev_fcn, wrap_fcn): - def wrapped_fcn(master, node, inputs, outputs): - wrap_fcn(prev_fcn, node, inputs, outputs) - - return wrapped_fcn - - -class StaticMemberNode(Node): - """Function signature: fcn(self)""" - - _obj = None - _touch_inputs = True - - def __init__(self, *args, **kwargs): - self._touch_inputs = kwargs.pop("touch_inputs", True) - super().__init__(*args, **kwargs) - - def _eval(self): - self._being_evaluated = True - if self._touch_inputs: - self.inputs.touch() - ret = self._fcn(self._obj) - self._being_evaluated = False - return ret - - @property - def obj(self): - return self._obj - - @obj.setter - def obj(self, obj): - self._obj = obj - - def _stash_fcn(self): - prev_fcn = self._fcn - self._fcn_chain.append(prev_fcn) - return lambda node, inputs, outputs: prev_fcn(node._obj) - - def _make_wrap(self, prev_fcn, wrap_fcn): - def wrapped_fcn(master): - wrap_fcn(prev_fcn, self, self.inputs, self.outputs) - - return wrapped_fcn diff --git a/subtrees/dagflow/dagflow/node.py b/subtrees/dagflow/dagflow/node.py deleted file mode 100644 index 322491035a297f91768b25d10088cec999e93c54..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/node.py +++ /dev/null @@ -1,512 +0,0 @@ -from .exception import ( - AllocationError, - CriticalError, - ClosedGraphError, - ClosingError, - OpeningError, - DagflowError, - ReconnectionError, - UnclosedGraphError, - InitializationError, -) -from .input import Input -from .legs import Legs -from .logger import Logger, get_logger -from .output import Output -from .iter import IsIterable -from .types import GraphT -from typing import Optional, List, Dict, Union, Callable, Any, Tuple - -class Node(Legs): - _name: str - _mark: Optional[str] = None - _label: Dict[str, str] - _graph: Optional[GraphT] = None - _fcn: Optional[Callable] = None - _fcn_chain = None - _exception: Optional[str] = None - - # Taintflag and status - _tainted: bool = True - _frozen: bool = False - _frozen_tainted: bool = False - _invalid: bool = False - _closed: bool = False - _allocated: bool = False - _being_evaluated: bool = False - - _types_tainted: bool = True - - # Options - _debug: bool = False - _auto_freeze: bool = False - _immediate: bool = False - # _always_tainted: bool = False - - def __init__( - self, name, - *, - label: Union[str, dict, None]=None, - graph: Optional[GraphT] = None, - fcn: Optional[Callable] = None, - typefunc: Optional[Callable] = None, - debug: Optional[bool] = None, - logger: Optional[Any] = None, - missing_input_handler: Optional[Callable] = None, - immediate: bool = False, - auto_freeze: bool = False, - frozen: bool = False, - **kwargs - ): - super().__init__(missing_input_handler=missing_input_handler) - self._name = name - if fcn is not None: - self._fcn = fcn - if typefunc is not None: - self._typefunc = typefunc - elif typefunc is False: - self._typefunc = lambda: None - - self._fcn_chain = [] - if graph is None: - from .graph import Graph - self.graph = Graph.current() - else: - self.graph = graph - - if debug is None and self.graph is not None: - self._debug = self.graph.debug - else: - self._debug = bool(debug) - - if isinstance(label, str): - self._label = {'text': label} - elif isinstance(label, dict): - self._label = label - else: - self._label = {'text': name} - - if logger is not None: - self._logger = logger - elif self.graph is not None: - self._logger = self.graph.logger - else: - self._logger = get_logger() - - self._immediate = immediate - self._auto_freeze = auto_freeze - self._frozen = frozen - - if kwargs: - raise InitializationError(f"Unparsed arguments: {kwargs}!") - - def __str__(self): - return f"{{{self.name}}} {super().__str__()}" - - # - # Properties - # - @property - def name(self): - return self._name - - @name.setter - def name(self, name): - self._name = name - - @property - def mark(self): - return self._mark - - @property - def exception(self): - return self._exception - - @property - def logger(self) -> Logger: - return self._logger - - @property - def tainted(self) -> bool: - return self._tainted - - @property - def types_tainted(self) -> bool: - return self._types_tainted - - @property - def frozen_tainted(self) -> bool: - return self._frozen_tainted - - @property - def frozen(self) -> bool: - return self._frozen - - @property - def auto_freeze(self) -> bool: - return self._auto_freeze - - # @property - # def always_tainted(self) -> bool: - # return self._always_tainted - - @property - def closed(self) -> bool: - return self._closed - - @property - def debug(self) -> bool: - return self._debug - - @property - def being_evaluated(self) -> bool: - return self._being_evaluated - - @property - def allocated(self) -> bool: - return self._allocated - - @property - def immediate(self) -> bool: - return self._immediate - - @property - def invalid(self) -> bool: - return self._invalid - - @invalid.setter - def invalid(self, invalid) -> None: - if invalid: - self.invalidate_self() - elif any(input.invalid for input in self.inputs.iter_all()): - return - else: - self.invalidate_self(False) - for output in self.outputs: - output.invalid = invalid - - def invalidate_self(self, invalid=True) -> None: - self._invalid = bool(invalid) - self._frozen_tainted = False - self._frozen = False - self._tainted = True - - def invalidate_children(self) -> None: - for output in self.outputs: - output.invalid = True - - def invalidate_parents(self) -> None: - for input in self.inputs.iter_all(): - node = input.parent_node - node.invalidate_self() - node.invalidate_parents() - - @property - def graph(self): - return self._graph - - @graph.setter - def graph(self, graph): - if graph is None: - return - if self._graph is not None: - raise DagflowError("Graph is already defined") - self._graph = graph - self._graph.register_node(self) - - # - # Methods - # - def __call__(self, name, child_output: Optional[Output]=None): - self.logger.debug(f"Node '{self.name}': Get input '{name}'") - inp = self.inputs.get(name, None) - if inp is None: - if self.closed: - raise ClosedGraphError(node=self) - return self._add_input(name, child_output=child_output) - elif inp.connected and (output := inp.parent_output): - raise ReconnectionError(input=inp, node=self, output=output) - return inp - - def label(self, source): - # if self._label: - # kwargs.setdefault("name", self._name) - # return self._label.format(*args, **kwargs) - label = self._label.get(source, None) - if label is None: - return self._label['text'] - - return label - - def add_input(self, name, **kwargs) -> Union[Input, Tuple[Input]]: - if not self.closed: - return self._add_input(name, **kwargs) - raise ClosedGraphError(node=self) - - def _add_input(self, name, **kwargs) -> Union[Input, Tuple[Input]]: - if IsIterable(name): - return tuple(self._add_input(n, **kwargs) for n in name) - self.logger.debug(f"Node '{self.name}': Add input '{name}'") - if name in self.inputs: - raise ReconnectionError(input=name, node=self) - positional = kwargs.pop("positional", True) - keyword = kwargs.pop("keyword", True) - inp = Input(name, self, **kwargs) - self.inputs.add(inp, positional=positional, keyword=keyword) - - if self._graph: - self._graph._add_input(inp) - return inp - - def add_output(self, name, **kwargs) -> Union[Output, Tuple[Output]]: - if not self.closed: - return self._add_output(name, **kwargs) - raise ClosedGraphError(node=self) - - def _add_output(self, name, *, keyword: bool=True, positional: bool=True, **kwargs) -> Union[Output, Tuple[Output]]: - if IsIterable(name): - return tuple( - self._add_output(n, **kwargs) for n in name - ) - self.logger.debug(f"Node '{self.name}': Add output '{name}'") - if isinstance(name, Output): - if name.name in self.outputs or name.node: - raise ReconnectionError(output=name, node=self) - name._node = self - return self.__add_output( - name, - positional=positional, - keyword=keyword - ) - if name in self.outputs: - raise ReconnectionError(output=name, node=self) - - return self.__add_output( - Output(name, self, **kwargs), - positional=positional, - keyword=keyword - ) - - def __add_output(self, out, positional: bool = True, keyword: bool = True) -> Union[Output, Tuple[Output]]: - self.outputs.add(out, positional=positional, keyword=keyword) - if self._graph: - self._graph._add_output(out) - return out - - def add_pair(self, iname: str, oname: str, **kwargs) -> Tuple[Input, Output]: - if not self.closed: - return self._add_pair(iname, oname, **kwargs) - raise ClosedGraphError(node=self) - - def _add_pair(self, iname: str, oname: str, input_kws: Optional[dict]=None, output_kws: Optional[dict]=None) -> Tuple[Input, Output]: - input_kws = input_kws or {} - output_kws = output_kws or {} - output = self._add_output(oname, **output_kws) - input = self._add_input(iname, child_output=output, **input_kws) - return input, output - - def _wrap_fcn(self, wrap_fcn, *other_fcns): - prev_fcn = self._stash_fcn() - self._fcn = self._make_wrap(prev_fcn, wrap_fcn) - if other_fcns: - self._wrap_fcn(*other_fcns) - - def _unwrap_fcn(self): - if not self._fcn_chain: - raise DagflowError("Unable to unwrap bare function") - self._fcn = self._fcn_chain.pop() - - def _stash_fcn(self): - raise DagflowError( - "Unimplemented method: use FunctionNode, StaticNode or MemberNode" - ) - - def _make_wrap(self, prev_fcn, wrap_fcn): - raise DagflowError( - "Unimplemented method: use FunctionNode, StaticNode or MemberNode" - ) - - def touch(self, force=False): - if self._frozen: - return - if not self._tainted and not force: - return - self.logger.debug(f"Node '{self.name}': Touch") - ret = self.eval() - self._tainted = False # self._always_tainted - if self._auto_freeze: - self._frozen = True - return ret - - def _eval(self): - raise CriticalError( - "Unimplemented method: use FunctionNode, StaticNode or MemberNode" - ) - - def eval(self): - if not self._closed: - raise UnclosedGraphError("Cannot evaluate the node!", node=self) - self._being_evaluated = True - try: - ret = self._eval() - self.logger.debug(f"Node '{self.name}': Evaluated return={ret}") - except Exception as exc: - raise exc - self._being_evaluated = False - return ret - - def freeze(self): - if self._frozen: - return - self.logger.debug(f"Node '{self.name}': Freeze") - if self._tainted: - raise CriticalError("Unable to freeze tainted node!", node=self) - self._frozen = True - self._frozen_tainted = False - - def unfreeze(self, force: bool = False): - if not self._frozen and not force: - return - self.logger.debug(f"Node '{self.name}': Unfreeze") - self._frozen = False - if self._frozen_tainted: - self._frozen_tainted = False - self.taint(force=True) - - def taint(self, *, caller: Optional[Input] = None, force: bool = False): - self.logger.debug(f"Node '{self.name}': Taint...") - if self._tainted and not force: - return - if self._frozen: - self._frozen_tainted = True - return - self._tainted = True - self._on_taint(caller) - ret = self.touch() if self._immediate else None - self.taint_children(force=force) - return ret - - def taint_children(self, **kwargs): - for output in self.outputs: - output.taint_children(**kwargs) - - def taint_type(self, force: bool = False): - self.logger.debug(f"Node '{self.name}': Taint types...") - if self._closed: - raise ClosedGraphError("Unable to taint type", node=self) - if self._type_tainted and not force: - return - self._type_tainted = True - self._tainted = True - self._frozen = False - for output in self.outputs: - output.taint_children_type(force) - - def print(self): - print( - f"Node {self._name}: 鈫抂{len(self.inputs)}],[{len(self.outputs)}]鈫�" - ) - for i, input in enumerate(self.inputs): - print(" ", i, input) - for i, output in enumerate(self.outputs): - print(" ", i, output) - - def _typefunc(self) -> bool: - """A output takes this function to determine the dtype and shape""" - raise DagflowError( - "Unimplemented method: the method must be overridden!" - ) - - def _fcn(self, _, inputs, outputs): - pass - - def _on_taint(self, caller: Input): - """A node method to be called on taint""" - pass - - def _post_allocate(self): - pass - - def update_types(self, recursive: bool = True) -> bool: - if not self._types_tainted: - return True - if recursive: - self.logger.debug(f"Node '{self.name}': Trigger recursive update types...") - for input in self.inputs.iter_all(): - input.parent_node.update_types(recursive) - self.logger.debug(f"Node '{self.name}': Update types...") - self._typefunc() - self._types_tainted = False - - def allocate(self, recursive: bool = True): - if self._allocated: - return True - if recursive: - self.logger.debug(f"Node '{self.name}': Trigger recursive memory allocation...") - if not all( - input.parent_node.allocate(recursive) for input in self.inputs.iter_all() - ): - return False - self.logger.debug(f"Node '{self.name}': Allocate memory on inputs") - if not self.inputs.allocate(): - raise AllocationError( - "Cannot allocate memory for inputs!", node=self - ) - self.logger.debug(f"Node '{self.name}': Allocate memory on outputs") - if not self.outputs.allocate(): - raise AllocationError( - "Cannot allocate memory for outputs!", node=self - ) - self.logger.debug(f"Node '{self.name}': Post allocate") - self._post_allocate() - self._allocated = True - return True - - def close(self, recursive: bool = True, together: List['Node'] = []) -> bool: - # Caution: `together` list should not be written in! - - if self._closed: - return True - if self.invalid: - raise ClosingError("Cannot close an invalid node!", node=self) - self.logger.debug(f"Node '{self.name}': Trigger recursive close") - for node in [self]+together: - node.update_types(recursive=recursive) - for node in [self]+together: - node.allocate(recursive=recursive) - if recursive and not all( - input.parent_node.close(recursive) for input in self.inputs.iter_all() - ): - return False - for node in together: - if not node.close(recursive=recursive): - return False - self.logger.debug(f"Node '{self.name}': Close") - self._closed = self._allocated - if not self._closed: - raise ClosingError(node=self) - return self._closed - - def open(self, force: bool = False) -> bool: - if not self._closed and not force: - return True - self.logger.debug(f"Node '{self.name}': Open") - if not all( - input.node.open(force) - for output in self.outputs - for input in output.child_inputs - ): - raise OpeningError(node=self) - self.unfreeze() - self.taint() - self._closed = False - return not self._closed - - # - # Accessors - # - def get_data(self, key=0): - return self.outputs[key].data - - def get_input_data(self, key): - return self.inputs[key].data() diff --git a/subtrees/dagflow/dagflow/node_group.py b/subtrees/dagflow/dagflow/node_group.py deleted file mode 100644 index 1993bf983865db6a59d50f8ed3ce9530ce950f7e..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/node_group.py +++ /dev/null @@ -1,44 +0,0 @@ -from .shift import lshift - - -class NodeGroup: - _nodes: list = None - - def __init__(self, *args): - self._nodes = list(args) - - def register_node(self, node): - self._nodes.append(node) - - def _wrap_fcns(self, *args): - for node in self._nodes: - node._wrap_fcn(*args) - - def _unwrap_fcns(self): - for node in self._nodes: - node._unwrap_fcn() - - def print(self): - print(f"Group of {len(self._nodes)} nodes:") - for node in self._nodes: - node.print() - - def __lshift__(self, other): - """ - self << other - """ - return lshift(self, other) - - def __rrshift__(self, other): - """ - other >> self - """ - return lshift(self, other) - - def __iter__(self): - """ - iterate inputs - - To be used with >>/<< operators which take only disconnected inputs - """ - return iter(self._nodes) diff --git a/subtrees/dagflow/dagflow/nodes.py b/subtrees/dagflow/dagflow/nodes.py deleted file mode 100644 index 668976652d021486cdbcc2fd5c6cb3cff0e6fcf7..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/nodes.py +++ /dev/null @@ -1,89 +0,0 @@ -from dagflow.exception import CriticalError -from .node import Node - - -class FunctionNode(Node): - """Function signature: fcn(node, inputs, outputs) - - Note: _fcn should be a static function with signature (node, inputs, outputs) - - - Function defined as instance property will become a static method: - class Node(...): - def __init__(self): - self._fcn = ... - node = Node() - node.fcn() # will have NO self provided as first argument - - - Fucntion defined in a nested class with staticmethod: - class Other(Node - @staticmethod - def _fcn(): - ... - - node = Node() - node.fcn() # will have NO self provided as first argument - - - [deprecated] Function defined as class property will become a bound method: - class Node(...): - _fcn = ... - node = Node() - node.fcn() # will have self provided as first argument - - - [deprecated] Function defined via staticmethod decorator as class property will become a static method: - class Node(...): - _fcn = staticmethod(...) - node = Node() - node.fcn() # will have NO self provided as first argument - """ - - fcn = None - - def __init__(self, name, **kwargs): - super().__init__(name, **kwargs) - if self.fcn is None: - self._functions = {"default": self._fcn} - self.fcn = self._functions["default"] - else: - self._functions = {"default": self.fcn} - - def _stash_fcn(self): - self._fcn_chain.append(self.fcn) - return self.fcn - - def _make_wrap(self, prev_fcn, wrap_fcn): - def wrapped_fcn(node, inputs, outputs): - wrap_fcn(prev_fcn, node, inputs, outputs) - - return wrapped_fcn - - def _eval(self): - return self.fcn(self, self.inputs, self.outputs) - - -class StaticNode(Node): - """Function signature: fcn()""" - - _touch_inputs = True - - def __init__(self, *args, **kwargs): - self._touch_inputs = kwargs.pop("touch_inputs", True) - super().__init__(*args, **kwargs) - - def _eval(self): - self._being_evaluated = True - if self._touch_inputs: - self.inputs.touch() - ret = self._fcn() - self._being_evaluated = False - return ret - - def _stash_fcn(self): - prev_fcn = self._fcn - self._fcn_chain.append(prev_fcn) - return lambda node, inputs, outputs: prev_fcn() - - def _make_wrap(self, prev_fcn, wrap_fcn): - def wrapped_fcn(): - wrap_fcn(prev_fcn, self, self.inputs, self.outputs) - - return wrapped_fcn diff --git a/subtrees/dagflow/dagflow/output.py b/subtrees/dagflow/dagflow/output.py deleted file mode 100644 index 8147fa035c554b5dda8dcf17f91a781eefbe49bb..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/output.py +++ /dev/null @@ -1,363 +0,0 @@ -from itertools import cycle -from typing import List, Optional, Tuple - -from numpy import zeros -from numpy.typing import ArrayLike, DTypeLike, NDArray - -from .edges import EdgeContainer -from .exception import ( - ClosedGraphError, - CriticalError, - InitializationError, - AllocationError, - ConnectionError, - UnclosedGraphError, -) -from .shift import lshift, rshift -from .iter import StopNesting -from .types import EdgesLike, InputT, NodeT, ShapeLike -from .datadescriptor import DataDescriptor - - -class Output: - _data: Optional[NDArray] = None - _dd: DataDescriptor - - _node: Optional[NodeT] - _name: Optional[str] - - _child_inputs: List[InputT] - _parent_input: Optional[InputT] = None - _allocating_input: Optional[InputT] = None - - _allocatable: bool = True - _owns_buffer: bool = False - _forbid_reallocation: bool = False - - _debug: bool = False - - def __init__( - self, - name: Optional[str], - node: Optional[NodeT], - *, - debug: Optional[bool] = None, - allocatable: Optional[bool] = None, - data: Optional[NDArray] = None, - owns_buffer: Optional[bool] = None, - dtype: DTypeLike = None, - shape: Optional[ShapeLike] = None, - axes_edges: Optional[Tuple[EdgesLike]] = None, - axes_nodes: Optional[Tuple[EdgesLike]] = None, - forbid_reallocation: bool = False, - ): - self._name = name - self._node = node - self._child_inputs = [] - self._debug = ( - debug if debug is not None else node.debug if node else False - ) - self._forbid_reallocation = forbid_reallocation - - self._dd = DataDescriptor(dtype, shape, axes_edges, axes_nodes) - - if data is None: - self._allocatable = True if allocatable is None else allocatable - else: - if owns_buffer is None: - owns_buffer = True - self._allocatable = not owns_buffer - self._set_data(data, owns_buffer=owns_buffer) - - if allocatable or dtype is not None or shape is not None: - raise InitializationError(output=self, node=node) - - def __str__(self): - return f"鈼忊啋 {self._name}" if self.owns_buffer else f"鈼嬧啋 {self._name}" - - def __repr__(self): - return self.__str__() - - @property - def name(self): - return self._name - - @name.setter - def name(self, name): - self._name = name - - @property - def allocatable(self): - return self._allocatable - - @property - def has_data(self) -> bool: - return self._data is not None - - @property - def node(self): - return self._node - - @property - def child_inputs(self): - return self._child_inputs - - @property - def parent_input(self): - return self._parent_input - - @parent_input.setter - def parent_input(self, input): - self._parent_input = input - - @property - def logger(self): - return self._node.logger - - @property - def invalid(self): - """Checks the validity of the current node""" - return self._node.invalid - - @invalid.setter - def invalid(self, invalid): - """Sets the validity of the following nodes""" - for input in self.child_inputs: - input.invalid = invalid - - @property - def data(self): - if self.node.being_evaluated: - return self._data - if not self.closed: - raise UnclosedGraphError( - "Unable to get the output data from unclosed graph!", - node=self._node, - output=self, - ) - try: - self.touch() - return self.get_data_unsafe() - except Exception as exc: - raise CriticalError( - "An exception occured during touching of the parent node!", - node=self._node, - output=self, - ) from exc - - def _set_data( - self, - data, - *, - owns_buffer: bool, - override: bool = False, - forbid_reallocation: Optional[bool] = None, - ): - if self.closed: - raise ClosedGraphError( - "Unable to set output data.", node=self._node, output=self - ) - if self._data is not None and not override: - # TODO: this will fail during reallocation - raise AllocationError( - "Output already has data.", node=self._node, output=self - ) - if owns_buffer: - forbid_reallocation = True - elif forbid_reallocation is None: - forbid_reallocation = owns_buffer - - forbid_reallocation |= self._forbid_reallocation - if forbid_reallocation and self._allocating_input: - raise AllocationError( - "Output is connected to allocating input, but reallocation is forbidden", - node=self._node, - output=self, - ) - - self._data = data - self.dd.dtype = data.dtype - self.dd.shape = data.shape - self._owns_buffer = owns_buffer - self._forbid_reallocation = forbid_reallocation - - @property - def dd(self) -> Optional[DataDescriptor]: - return self._dd - - @property - def owns_buffer(self): - return self._owns_buffer - - @property - def forbid_reallocation(self): - return self._forbid_reallocation - - @property - def closed(self): - return self.node.closed if self.node else False - - @property - def tainted(self) -> bool: - return self._node.tainted - - @property - def debug(self) -> bool: - return self._debug - - def get_data_unsafe(self): - return self._data - - def connect_to(self, input) -> InputT: - if not self.closed and input.closed: - raise ConnectionError( - "Cannot connect an output to a closed input!", - node=self.node, - output=self, - input=input, - ) - if self.closed and input.allocatable: - raise ConnectionError( - "Cannot connect a closed output to an allocatable input!", - node=self.node, - output=self, - input=input, - ) - return self._connect_to(input) - - def _connect_to(self, input) -> InputT: - if input.allocatable: - if self._allocating_input: - raise ConnectionError( - "Output has multiple allocatable/allocated child inputs", - node=self._node, - output=self, - ) - if self._forbid_reallocation: - raise ConnectionError( - "Output forbids reallocation and may not connect to allocating inputs", - node=self._node, - output=self, - ) - self._allocating_input = input - self._child_inputs.append(input) - input._set_parent_output(self) - return input - - def __rshift__(self, other): - return rshift(self, other) - - def __rlshift__(self, other): - return lshift(self, other) - - def taint_children(self, **kwargs) -> None: - for input in self._child_inputs: - input.taint(**kwargs) - - def taint_children_type(self, **kwargs) -> None: - for input in self._child_inputs: - input.taint_type(**kwargs) - - def touch(self): - return self._node.touch() - - def connected(self): - return bool(self._child_inputs) - - def deep_iter_outputs(self, disconnected_only=False): - if disconnected_only and self.connected(): - return iter(tuple()) - raise StopNesting(self) - - def deep_iter_child_outputs(self): - raise StopNesting(self) - - def repeat(self): - return RepeatedOutput(self) - - def allocate(self, **kwargs): - if not self._allocatable: - return True - - if self._allocating_input: - input = self._allocating_input - input.allocate(recursive=False) - if input.has_data: - idata = input._own_data - if idata.shape != self.dd.shape or idata.dtype != self.dd.dtype: - raise AllocationError( - "Input's data shape/type is inconsistent", - node=self._node, - output=self, - input=input, - ) - - if self._data is not idata: - if self._data is not None: - idata[:] = self._data - self._set_data(idata, owns_buffer=False, override=True) - return True - - if self.has_data: - return True - - if self.dd.shape is None or self.dd.dtype is None: - raise AllocationError( - "No shape/type information provided for the Output", - node=self._node, - output=self, - ) - try: - data = zeros(self.dd.shape, self.dd.dtype, **kwargs) - self._set_data(data, owns_buffer=True) - except Exception as exc: - raise AllocationError( - f"Output: {exc.args[0]}", node=self._node, output=self - ) from exc - - return True - - def set( - self, data: ArrayLike, check_taint: bool = False, force: bool = False - ) -> bool: - if self.node._frozen and not force: - return False - - tainted = True - if check_taint: - tainted = (self._data != data).any() - - if tainted: - self._data[:] = data - self.taint_children() - self.node.invalidate_parents() - self.node._tainted = False - - return tainted - - -class RepeatedOutput: - def __init__(self, output): - self._output = output - - def __iter__(self): - return cycle((self._output,)) - - def __rshift__(self, other): - return rshift(self, other) - - def __rlshift__(self, other): - return lshift(self, other) - - -class Outputs(EdgeContainer): - _dtype = Output - - def __init__(self, iterable=None) -> None: - super().__init__(iterable) - - def __str__(self) -> str: - return f"鈼媅{tuple(obj.name for obj in self)}]鈫�" - - def __repr__(self) -> str: - return self.__str__() diff --git a/subtrees/dagflow/dagflow/printl.py b/subtrees/dagflow/dagflow/printl.py deleted file mode 100644 index 459eb59e9c4b045facb4735b9f2eb451681292a3..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/printl.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- - - -printlevel = 0 -singlemargin = " " -marginflag = False -prefix_function = lambda: "" - - -def set_prefix_function(f): - global prefix_function - prefix_function = f - - -class next_level: - def __enter__(self): - global printlevel - printlevel += 1 - - def __exit__(self, *args, **kwargs): - global printlevel - printlevel -= 1 - - -def current_level(): - return printlevel - - -def print_margin(kwargs): - global marginflag - prefix = kwargs.pop("prefix", prefix_function()) - postfix = kwargs.pop("postfix", None) - prefixopts = kwargs.pop("prefixopts", dict(end="")) - postfixopts = kwargs.pop("postfixopts", dict(end=" ")) - if marginflag: - return - - if prefix: - print(*prefix, **prefixopts) - - print(singlemargin * printlevel, sep="", end="") - - if postfix: - print(*postfix, **postfixopts) - - marginflag = True - - -def reset_margin_flag(*args, **kwargs): - global marginflag - - for arg in args + (kwargs.pop("sep", ""), kwargs.pop("end", "\n")): - if "\n" in str(arg): - marginflag = False - return - - -def printl(*args, **kwargs): - print_margin(kwargs) - print(*args, **kwargs) - reset_margin_flag(*args, **kwargs) diff --git a/subtrees/dagflow/dagflow/shift.py b/subtrees/dagflow/dagflow/shift.py deleted file mode 100644 index 693c422063d1bc2ff4447d428316a454edf2aeba..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/shift.py +++ /dev/null @@ -1,50 +0,0 @@ -from itertools import zip_longest - -from .exception import ConnectionError -from .iterators import iter_child_outputs, iter_inputs, iter_outputs - -_rshift_scope_id = 0 - - -def rshift_scope_id(): - global _rshift_scope_id - ret = _rshift_scope_id - _rshift_scope_id += 1 - return ret - - -def rshift(outputs, inputs): - """`>>` operator""" - scope_id = rshift_scope_id() - - for output, inp in zip_longest( - iter_outputs(outputs), - iter_inputs(inputs, True), - fillvalue=None, - ): - if not output: - raise ConnectionError("Unable to connect mismatching lists!") - if isinstance(output, dict): - if inp: - raise ConnectionError( - f"Cannot perform a binding from dict={output} due to " - f"non-empty input={inp}!" - ) - for key, val in output.items(): - val >> inputs(key) - continue - if not inp: - missing_input_handler = getattr( - inputs, "_missing_input_handler", lambda *args, **kwargs: None - ) - if not (inp := missing_input_handler(scope=scope_id)): - break - output.connect_to(inp) - - child_outputs = tuple(iter_child_outputs(inputs)) - return child_outputs[0] if len(child_outputs) == 1 else child_outputs - - -def lshift(inputs, outputs): - """`<<` operator""" - return rshift(outputs, inputs) diff --git a/subtrees/dagflow/dagflow/tools/__init__.py b/subtrees/dagflow/dagflow/tools/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/subtrees/dagflow/dagflow/tools/schema.py b/subtrees/dagflow/dagflow/tools/schema.py deleted file mode 100644 index f4c452feff550561cc586d6072f2223c93d98def..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/tools/schema.py +++ /dev/null @@ -1,77 +0,0 @@ -from typing import Any, Union -from schema import Schema, Schema, SchemaError -from contextlib import suppress - -from os import access, R_OK -from typing import Callable - -def IsReadable(filename: str): - """Returns True if the file is readable""" - return access(filename, R_OK) - -def IsFilewithExt(*exts: str): - """Returns a function that retunts True if the file extension is consistent""" - def checkfilename(filename: str): - return any(filename.endswith(f'.{ext}' for ext in exts)) - return checkfilename - -def LoadFileWithExt(*, key: str=None,**kwargs: Callable): - """Returns a function that retunts True if the file extension is consistent""" - def checkfilename(filename: Union[str, dict]): - if key is not None: - filename = filename[key] - for ext, loader in kwargs.items(): - if filename.endswith(f'.{ext}'): - return loader(filename) - - return False - return checkfilename - -from yaml import load, Loader -def LoadYaml(fname: str): - with open(fname, 'r') as file: - return load(file, Loader) - -from dictwrapper.dictwrapper import DictWrapper -class NestedSchema(object): - __slots__ = ('_schema', '_processdicts') - _schema: Union[Schema,object] - _processdicts: bool - - def __init__(self, /, schema: Union[Schema,object], *, processdicts: bool=False): - self._schema = schema - self._processdicts = processdicts - - def validate(self, data: Any) -> Any: - if not isinstance(data, dict): - return self._schema.validate(data) - - if self._processdicts: - return { - key: self._process_dict((key,), subdata) for key, subdata in data.items() - } - - dtin = DictWrapper(data) - dtout = DictWrapper({}) - for key, subdata in dtin.walkitems(): - dtout[key] = self._process_element(key, subdata) - - return dtout.object - - def _process_element(self, key, subdata: Any) -> Any: - try: - return self._schema.validate(subdata, _is_event_schema=False) - except SchemaError as err: - key = ".".join(str(k) for k in key) - raise SchemaError(f'Key "{key}" has invalid value "{subdata}":\n{err.args[0]}') from err - - def _process_dict(self, key, data: Any) -> Any: - if not isinstance(data, dict): - return self._schema.validate(data) - - with suppress(SchemaError): - return self._schema.validate(data, _is_event_schema=False) - - return { - subkey: self._process_dict(key+(subkey,), subdata) for subkey, subdata in data.items() - } diff --git a/subtrees/dagflow/dagflow/typefunctions.py b/subtrees/dagflow/dagflow/typefunctions.py deleted file mode 100644 index f923f66710769f577aacd3e9461374b535b91c14..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/typefunctions.py +++ /dev/null @@ -1,301 +0,0 @@ -from collections.abc import Sequence -from typing import Union - -from numpy import result_type -from itertools import repeat - -from .exception import TypeFunctionError -from .types import NodeT - -AllPositionals = slice(None) - -try: - zip((), (), strict=True) -except TypeError: - # provide a replacement of strict zip from Python 3.1 - # to be deprecated at some point - from itertools import zip_longest - - def zip(*iterables, strict: bool = False): - sentinel = object() - for combo in zip_longest(*iterables, fillvalue=sentinel): - if strict and sentinel in combo: - raise ValueError("Iterables have different lengths") - yield combo - - -def check_has_inputs( - node: NodeT, inputkey: Union[str, int, slice, Sequence, None] = None -) -> None: - """Checking if the node has inputs""" - if inputkey is None or inputkey == AllPositionals: - try: - node.inputs[0] - except Exception as exc: - raise TypeFunctionError( - "The node must have at lease one input!", node=node - ) from exc - else: - try: - node.inputs[inputkey] - except Exception as exc: - raise TypeFunctionError( - f"The node must have the input '{inputkey}'!", node=node - ) from exc - - -def eval_output_dtype( - node: NodeT, - inputkey: Union[str, int, slice, Sequence] = AllPositionals, - outputkey: Union[str, int, slice, Sequence] = AllPositionals, -) -> None: - """Automatic calculation and setting dtype for the output""" - inputs = node.inputs.iter(inputkey) - outputs = node.outputs.iter(outputkey) - - dtype = result_type(*(inp.dd.dtype for inp in inputs)) - for output in outputs: - output.dd.dtype = dtype - - -def copy_input_to_output( - node: NodeT, - inputkey: Union[str, int, slice, Sequence] = 0, - outputkey: Union[str, int, slice, Sequence] = AllPositionals, - dtype: bool = True, - shape: bool = True, -) -> None: - """Coping input dtype and setting for the output""" - inputs = tuple(node.inputs.iter(inputkey)) - outputs = tuple(node.outputs.iter(outputkey)) - - if dtype and shape: - - def cpy(input, output): - output.dd.dtype = input.dd.dtype - output.dd.shape = input.dd.shape - - elif dtype: - - def cpy(input, output): - output.dd.dtype = input.dd.dtype - - elif shape: - - def cpy(input, output): - output.dd.shape = input.dd.shape - - else: - return - - if len(inputs) == 1: - inputs = repeat(inputs[0], len(outputs)) - - for input, output in zip(inputs, outputs, strict=True): - cpy(input, output) - - -def copy_input_dtype_to_output( - node: NodeT, - inputkey: Union[str, int, slice, Sequence] = 0, - outputkey: Union[str, int, slice, Sequence] = AllPositionals, -) -> None: - """Coping input dtype and setting for the output""" - inputs = tuple(node.inputs.iter(inputkey)) - outputs = tuple(node.outputs.iter(outputkey)) - - if len(inputs) == 1: - inputs = repeat(inputs[0], len(outputs)) - - for input, output in zip(inputs, outputs, strict=True): - output.dd.dtype = input.dd.dtype - - -def copy_input_shape_to_output( - node: NodeT, - inputkey: Union[str, int] = 0, - outputkey: Union[str, int, slice, Sequence] = AllPositionals, -) -> None: - """Coping input shape and setting for the output""" - inputs = tuple(node.inputs.iter(inputkey)) - outputs = tuple(node.outputs.iter(outputkey)) - - if len(inputs) == 1: - inputs = repeat(inputs[0], len(outputs)) - - for input, output in zip(inputs, outputs, strict=True): - output.dd.shape = input.dd.shape - - -def combine_inputs_shape_to_output( - node: NodeT, - inputkey: Union[str, int, slice, Sequence] = AllPositionals, - outputkey: Union[str, int, slice, Sequence] = AllPositionals, -) -> None: - """Combine all the inputs shape and setting for the output""" - inputs = node.inputs.iter(inputkey) - shape = tuple(inp.dd.shape for inp in inputs) - for output in node.outputs.iter(outputkey): - output.dd.shape = shape - - -def check_input_dimension( - node: NodeT, inputkey: Union[str, int, slice, Sequence], ndim: int -): - """Checking the dimension of the input""" - for input in node.inputs.iter(inputkey): - dim = len(input.dd.shape) - if ndim != dim: - raise TypeFunctionError( - f"The node supports only {ndim}d inputs. Got {dim}d!", - node=node, - input=input, - ) - - -def check_input_square( - node: NodeT, - inputkey: Union[str, int, slice, Sequence], -): - """Checking input is a square matrix""" - for input in node.inputs.iter(inputkey): - shape = input.dd.shape - dim = len(shape) - if dim != 2 or shape[0] != shape[1]: - raise TypeFunctionError( - f"The node supports only square inputs. Got {shape}!", - node=node, - input=input, - ) - - -def check_input_square_or_diag( - node: NodeT, - inputkey: Union[str, int, slice, Sequence], -) -> int: - """Check if input is a square matrix or diagonal (1d) of a square matrix. - Returns the maximal dimension.""" - dim_max = 0 - for input in node.inputs.iter(inputkey): - shape = input.dd.shape - dim = len(shape) - dim_max = max(dim, dim_max) - if (dim == 2 and shape[0] != shape[1]) and dim != 1: - raise TypeFunctionError( - f"The node supports only square inputs (or 1d as diagonal). Got {shape}!", - node=node, - input=input, - ) - return dim_max - -def check_input_shape( - node: NodeT, inputkey: Union[str, int, slice, Sequence], shape: tuple -): - """Checking the shape equivalence for inputs""" - for input in node.inputs.iter(inputkey): - sshape = input.dd.shape - if sshape != shape: - raise TypeFunctionError( - f"The node supports only inputs with shape={shape}. Got {sshape}!", - node=node, - input=input, - ) - - -def check_input_dtype( - node: NodeT, inputkey: Union[str, int, slice, Sequence], dtype -): - """Checking the dtype equivalence for inputs""" - for input in node.inputs.iter(inputkey): - dtt = input.dd.dtype - if dtt != dtype: - raise TypeFunctionError( - f"The node supports only input types {dtype}. Got {dtt}!", - node=node, - input=input, - ) - - -def check_inputs_equivalence( - node: NodeT, inputkey: Union[str, int, slice, Sequence] = AllPositionals -): - """Checking the equivalence of the dtype and shape of all the inputs""" - inputs = tuple(node.inputs.iter(inputkey)) - input0, inputs = inputs[0], inputs[1:] - - dtype, shape = input0.dd.dtype, input0.dd.shape - for input in inputs: - if input.dd.dtype != dtype or input.dd.shape != shape: - raise TypeFunctionError( - f"Input data {input.dtype} [{input.shape}] is inconsistent with {dtype} [{shape}]", - node=node, - input=input, - ) - -def check_inputs_square_or_diag( - node: NodeT, - inputkey: Union[str, int, slice, Sequence] = AllPositionals, -) -> int: - """Check if inputs are square matrices or diagonals (1d) of a square matrices of the same size. - Returns the maximal dimension.""" - inputs = tuple(node.inputs.iter(inputkey)) - - dim_max = 0 - shape0 = inputs[0].dd.shape[0] - - for input in inputs: - shape = input.dd.shape - dim = len(shape) - dim_max = max(dim, dim_max) - if shape0 != shape[0] or ((dim == 2 and shape[0] != shape[1]) and dim != 1): - raise TypeFunctionError( - f"The node supports only square inputs (or 1d as diagonal) of size {shape0}x{shape0}. Got {shape}!", - node=node, - input=input, - ) - return dim_max - - -def check_inputs_same_dtype( - node: NodeT, inputkey: Union[str, int, slice, Sequence] = AllPositionals -): - """Checking dtypes of all the inputs are same""" - inputs = tuple(node.inputs.iter(inputkey)) - input0, inputs = inputs[0], inputs[1:] - - dtype = input0.dd.dtype - for input in inputs: - if input.dd.dtype != dtype: - raise TypeFunctionError( - f"Input data {input.dd.dtype} is inconsistent with {dtype}", - node=node, - input=input, - ) - - -def check_inputs_multiplicable_mat( - node: NodeT, - inputkey1: Union[str, int, slice, Sequence], - inputkey2: Union[str, int, slice, Sequence], -): - """Checking that inputs from key1 and key2 may be multiplied (matrix)""" - inputs1 = tuple(node.inputs.iter(inputkey1)) - inputs2 = tuple(node.inputs.iter(inputkey2)) - - len1, len2 = len(inputs1), len(inputs2) - if len1 == len2: - pass - elif len1 == 1: - inputs1 = repeat(inputs1[0], len2) - elif len2 == 1: - inputs2 = repeat(inputs2[0], len1) - - for input1, input2 in zip(inputs1, inputs2, strict=True): - shape1 = input1.dd.shape - shape2 = input2.dd.shape - if shape1[-1] != shape2[0]: - raise TypeFunctionError( - f"Inputs {shape1} and {shape2} are not multiplicable", - node=node, - input=input, - ) diff --git a/subtrees/dagflow/dagflow/types.py b/subtrees/dagflow/dagflow/types.py deleted file mode 100644 index 8ee0295bd890c7554446ffc0eba3ecd2b10e2382..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/types.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Tuple, TypeVar - -GraphT = TypeVar("GraphT", bound="Graph") -NodeT = TypeVar("NodeT", bound="Node") -InputT = TypeVar("InputT", bound="Input") -InputsT = TypeVar("InputsT", bound="Inputs") -OutputT = TypeVar("OutputT", bound="Output") -OutputsT = TypeVar("OutputsT", bound="Outputs") - -ShapeLike = Tuple[int, ...] -EdgesLike = Tuple[OutputT] diff --git a/subtrees/dagflow/dagflow/variable.py b/subtrees/dagflow/dagflow/variable.py deleted file mode 100644 index d565d9f9abe9b2170ea221ce52acab2f6d5c5529..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/variable.py +++ /dev/null @@ -1,145 +0,0 @@ -from .node import Node, Output -from .exception import InitializationError -from .lib.NormalizeCorrelatedVars2 import NormalizeCorrelatedVars2 -from .lib.Cholesky import Cholesky -from .lib.Array import Array -from .lib.CovmatrixFromCormatrix import CovmatrixFromCormatrix - -from numpy import zeros_like, array -from numpy.typing import DTypeLike -from typing import Optional, Dict - -class Parameters(object): - value: Output - _value_node: Node - - def __init__(self, value: Node): - self._value_node = value - self.value = value.outputs[0] - - @staticmethod - def from_numbers(*, dtype: DTypeLike='d', **kwargs) -> 'Parameters': - sigma = kwargs['sigma'] - if sigma is not None: - return GaussianParameters.from_numbers(dtype=dtype, **kwargs) - - label: Dict[str, str] = kwargs.get('label') - if label is None: - label = {'text': 'parameter'} - else: - label = dict(label) - name: str = label.setdefault('name', 'parameter') - value = kwargs['value'] - return Parameters( - Array( - name, - array((value,), dtype=dtype), - label = label, - mode='store_weak' - ) - ) - -class GaussianParameters(Parameters): - central: Output - sigma: Output - normvalue: Output - - _central_node: Node - _sigma_node: Node - _normvalue_node: Node - - _cholesky_node: Optional[Node] = None - _covariance_node: Optional[Node] = None - _correlation_node: Optional[Node] = None - _sigma_total_node: Optional[Node] = None - - _forward_node: Node - _backward_node: Node - - def __init__(self, value: Node, central: Node, *, sigma: Node=None, covariance: Node=None, correlation: Node=None): - super().__init__(value) - self._central_node = central - - if sigma is not None and covariance is not None: - raise InitializationError('GaussianParameters: got both "sigma" and "covariance" as arguments') - if correlation is not None and sigma is None: - raise InitializationError('GaussianParameters: got "correlation", but no "sigma" as arguments') - - if correlation is not None: - self._correlation_node = correlation - self._covariance_node = CovmatrixFromCormatrix(f"V({value.name})") - self._cholesky_node = Cholesky(f"L({value.name})") - self._sigma_total_node = sigma - self._sigma_node = self._cholesky_node - - self._sigma_total_node >> self._covariance_node.inputs['sigma'] - correlation >> self._covariance_node - self._covariance_node >> self._cholesky_node - elif sigma is not None: - self._sigma_node = sigma - elif covariance is not None: - self._cholesky_node = Cholesky(f"L({value.name})") - self._sigma_node = self._cholesky_node - self._covariance_node = covariance - - covariance >> self._cholesky_node - else: - # TODO: no sigma/covariance AND central means normalized=value? - raise InitializationError('GaussianParameters: got no "sigma" and no "covariance" arguments') - - self.central = self._central_node.outputs[0] - self.sigma = self._sigma_node.outputs[0] - - self._normvalue_node = Array( - f'Normalized {value.name}', - zeros_like(self.central._data), - mark = f'norm({value.mark})', - mode='store_weak' - ) - self.normvalue = self._normvalue_node.outputs[0] - - self._norm_node = NormalizeCorrelatedVars2(f"Normalize {value.name}", immediate=True) - self.central >> self._norm_node.inputs['central'] - self.sigma >> self._norm_node.inputs['matrix'] - (self.value, self.normvalue) >> self._norm_node - - self._norm_node.close(recursive=True) - self._norm_node.touch() - - @staticmethod - def from_numbers( - value: float, - *, - central: float, - sigma: float, - label: Optional[Dict[str,str]]=None, - dtype: DTypeLike='d', - **_ - ) -> 'GaussianParameters': - if label is None: - label = {'text': 'gaussian parameter'} - else: - label = dict(label) - name = label.setdefault('name', 'parameter') - node_value = Array( - name, - array((value,), dtype=dtype), - label = label, - mode='store_weak' - ) - - node_central = Array( - f'{name}_central', - array((central,), dtype=dtype), - label = {k: f'central: {v}' for k,v in label.items()}, - mode='store_weak' - ) - - node_sigma = Array( - f'{name}_sigma', - array((sigma,), dtype=dtype), - label = {k: f'sigma: {v}' for k,v in label.items()}, - mode='store_weak' - ) - - return GaussianParameters(value=node_value, central=node_central, sigma=node_sigma) diff --git a/subtrees/dagflow/dagflow/wrappers.py b/subtrees/dagflow/dagflow/wrappers.py deleted file mode 100644 index 18dec84a76b0475df33406825e6f20827b4177a5..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dagflow/wrappers.py +++ /dev/null @@ -1,35 +0,0 @@ - -from .printl import next_level, printl - - -def printer(fcn, node, inputs, outputs): - printl(f"Evaluate {node.name}") - with next_level(): - fcn(node, inputs, outputs) - printl(f"... done with {node.name}") - - -def before_printer(fcn, node, inputs, outputs): - printl(f"Evaluate {node.name}: {node.label()}") - with next_level(): - fcn(node, inputs, outputs) - - -def after_printer(fcn, node, inputs, outputs): - with next_level(): - fcn(node, inputs, outputs) - printl(f"Evaluate {node.name}: {node.label()}") - - -def dataprinter(fcn, node, inputs, outputs): - fcn(node, inputs, outputs) - for i, output in enumerate(outputs): - printl("{: 2d} {}: {!s}".format(i, output.name, output._data)) - - -def toucher(fcn, node, inputs, outputs): - for i, input in enumerate(inputs): - printl("touch input {: 2d} {}.{}".format(i, node.name, input.name)) - with next_level(): - input.touch() - fcn(node, inputs, outputs) diff --git a/subtrees/dagflow/dictwrapper b/subtrees/dagflow/dictwrapper deleted file mode 120000 index 3ff36458e776b6f68c096e68ceac44280b392e97..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/dictwrapper +++ /dev/null @@ -1 +0,0 @@ -subtrees/dictwrapper/dictwrapper \ No newline at end of file diff --git a/subtrees/dagflow/example/example.py b/subtrees/dagflow/example/example.py deleted file mode 100755 index be2288fc6c9538e6d0c565ab25d05529d215870c..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/example/example.py +++ /dev/null @@ -1,128 +0,0 @@ -from numpy import arange, copyto, result_type - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.input_extra import MissingInputAddEach -from dagflow.lib import Array, Product, Sum, WeightedSum -from dagflow.nodes import FunctionNode - -array = arange(3, dtype="d") -debug = False - - -class ThreeInputsOneOutput(FunctionNode): - def __init__(self, *args, **kwargs): - kwargs.setdefault("missing_input_handler", MissingInputAddEach()) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - for i, output in enumerate(outputs): - out = output.data - copyto(out, inputs[3 * i].data) - for input in inputs[3 * i + 1 : (i + 1) * 3]: - out += input.data - return out - - @property - def result(self): - return [out.data for out in self.outputs] - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - for i, output in enumerate(self.outputs): - inputs = self.inputs[2*i:2*(1+i)] - output.dd.shape = inputs[0].dd.shape - output.dd.dtype = result_type(tuple(inp.dd.dtype for inp in inputs)) - self.logger.debug( - f"Node '{self.name}': dtype={tuple(out.dd.dtype for out in self.outputs)}, " - f"shape={tuple(out.dd.shape for out in self.outputs)}" - ) - - -# Check predefined Array, Sum and Product -with Graph(debug=debug) as graph: - (in1, in2, in3, in4) = ( - Array(name, array) for name in ("n1", "n2", "n3", "n4") - ) - s = Sum("sum") - m = Product("product") - - (in1, in2, in3) >> s - (in4, s) >> m - graph.close() - - print("Result:", m.outputs["result"].data) - savegraph(graph, "dagflow_example_1a.png") - -# Check random generated Array, Sum and Product -with Graph(debug=debug) as graph: - (in1, in2, in3, in4) = ( - Array(name, array) for name in ("n1", "n2", "n3", "n4") - ) - s = Sum("sum") - m = Product("product") - - (in1, in2, in3) >> s - (in4, s) >> m - graph.close() - - print("Result:", m.outputs["result"].data) - savegraph(graph, "dagflow_example_1b.png") - -# Check predefined Array, two Sum's and Product -with Graph(debug=debug) as graph: - (in1, in2, in3, in4) = ( - Array(name, array) for name in ("n1", "n2", "n3", "n4") - ) - s = Sum("sum") - s2 = Sum("sum") - m = Product("product") - - (in1, in2) >> s - (in3, in4) >> s2 - (s, s2) >> m - graph.close() - - print("Result:", m.outputs["result"].data) - savegraph(graph, "dagflow_example_2.png") - -# Check predefined Array, Sum, WeightedSum and Product -with Graph(debug=debug) as graph: - (in1, in2, in3, in4) = ( - Array(name, array) for name in ("n1", "n2", "n3", "n4") - ) - weight = Array("weight", (2, 3)) - # The same result with other weight - # weight = makeArray(5)("weight") - s = Sum("sum") - ws = WeightedSum("weightedsum") - m = Product("product") - - (in1, in2) >> s # [0,2,4] - (in3, in4) >> ws - {"weight": weight} >> ws # [0,1,2] * 2 + [0,1,2] * 3 = [0,5,10] - # NOTE: also it is possible to use the old style binding: - #weight >> ws("weight") - (s, ws) >> m # [0,2,4] * [0,5,10] = [0,10,40] - graph.close() - - print("Result:", m.outputs["result"].data) - savegraph(graph, "dagflow_example_3.png") - - -with Graph(debug=debug) as graph: - (in1, in2, in3) = (Array(name, array) for name in ("n1", "n2", "n3")) - (in4, in5, in6) = ( - Array(name, (1, 0, 0)) for name in ("n4", "n5", "n6") - ) - (in7, in8, in9) = ( - Array(name, (3, 3, 3)) for name in ("n7", "n8", "n9") - ) - s = ThreeInputsOneOutput("3to1") - (in1, in2, in3) >> s - (in4, in5, in6) >> s - (in7, in8, in9) >> s - graph.close() - - print("Result:", s.result) - savegraph(graph, "dagflow_example_4.png") diff --git a/subtrees/dagflow/example/graph_evaluation.gif b/subtrees/dagflow/example/graph_evaluation.gif deleted file mode 100644 index 36b4b7d3f7a3d4a685531c0dc3f5f5287a853992..0000000000000000000000000000000000000000 Binary files a/subtrees/dagflow/example/graph_evaluation.gif and /dev/null differ diff --git a/subtrees/dagflow/pytest.ini b/subtrees/dagflow/pytest.ini deleted file mode 100755 index 0f91ae46aaea5c6922d3a8f583bc520c4224ede9..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -testpaths=test/ -; addopts= --cov-report term --cov=./ --cov-report xml:cov.xml diff --git a/subtrees/dagflow/requirements.txt b/subtrees/dagflow/requirements.txt deleted file mode 100644 index 63a493e7e59c09a6a189eb7ffc5eecdf9dadf4f8..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -contextlib2 -coverage -numba -numpy -pygraphviz -pytest -pytest-cov -schema diff --git a/subtrees/dagflow/storage b/subtrees/dagflow/storage deleted file mode 120000 index 79ecdb34cd8f2191cb528038042137a9de8c82d4..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/storage +++ /dev/null @@ -1 +0,0 @@ -subtrees/dictwrapper/storage \ No newline at end of file diff --git a/subtrees/dagflow/subtrees/dictwrapper/.gitignore b/subtrees/dagflow/subtrees/dictwrapper/.gitignore deleted file mode 100644 index 4906f4c4df53b47c9f99b611f1d4fcf3a85e209a..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/.gitignore +++ /dev/null @@ -1,145 +0,0 @@ - -# Created by https://www.toptal.com/developers/gitignore/api/python -# Edit at https://www.toptal.com/developers/gitignore?templates=python - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/subtrees/dagflow/subtrees/dictwrapper/.gitlab-ci.yml b/subtrees/dagflow/subtrees/dictwrapper/.gitlab-ci.yml deleted file mode 100644 index 05a935a95d9c71b73f11b9832bdfcfe5c68de3ca..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/.gitlab-ci.yml +++ /dev/null @@ -1,21 +0,0 @@ -stages: - - tests - -tests: - image: git.jinr.ru:5005/gna/gna-base-docker-image:latest - stage: tests - - script: - - python3 -m pip install -r requirements.txt - - coverage run --source=. -m pytest - - coverage report - - coverage xml - coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' - artifacts: - reports: - coverage_report: - coverage_format: cobertura - path: coverage.xml - only: - - master - - merge_requests diff --git a/subtrees/dagflow/subtrees/dictwrapper/README.md b/subtrees/dagflow/subtrees/dictwrapper/README.md deleted file mode 100644 index 7516994f16ea61a017bb6fb999a98a5191079895..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Summary - -[](https://www.python.org/) -[](https://git.jinr.ru/dag-computing/dictwrapper.py/commits/master) -[](https://git.jinr.ru/dag-computing/dictwrapper.py/-/commits/master) -<!--- Uncomment here after adding docs! -[](http://dag-computing.pages.jinr.ru/dictwrapper.py) ---> - -* `DictWrapper` is a tool to work with nested dictionaries -* `Storage` is a map-like class supporing list/set as a key and does not distinguish the order of the keys diff --git a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/__init__.py b/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/__init__.py deleted file mode 100644 index 08d58d7a49f476d4fb48d92b2756353a6dc09b4d..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .dictwrapper import DictWrapper, DictWrapperAccess diff --git a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/classwrapper.py b/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/classwrapper.py deleted file mode 100644 index ec06b6468799c8bf7f6a04f775ac17ab2befff81..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/classwrapper.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import Any - -class ClassWrapper(object): - _object: Any - _types: Any - def __init__(self, obj, *, types=None): - self._object = obj - self._types = type(obj) if types is None else types - self._wrapper_class = type(self) - - @property - def object(self) -> Any: - return self._object - - def __str__(self): - return str(self._object) - - def __repr__(self): - return repr(self._object) - - def __dir__(self): - return dir(self._object) - - def __len__(self): - return len(self._object) - - def __bool__(self): - return bool(self._object) - - def __contains__(self, v): - return v in self._object - - def __eq__(self, other): - if isinstance(other, ClassWrapper): - return self._object==other._object - - return self._object==other - - def _wrap(self, obj, **kwargs): - if isinstance(obj, ClassWrapper): - return obj - - if isinstance(obj, self._types): - return self._wrapper_class(obj, **kwargs) - - return obj - diff --git a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/dictwrapper.py b/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/dictwrapper.py deleted file mode 100644 index e76223a808fff96e5369c49e849573e20ea7a08b..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/dictwrapper.py +++ /dev/null @@ -1,289 +0,0 @@ -from .classwrapper import ClassWrapper -from .visitor import MakeDictWrapperVisitor -from .dictwrapperaccess import DictWrapperAccess - -from collections.abc import Sequence, MutableMapping -from typing import Any - -class DictWrapper(ClassWrapper): - """Dictionary wrapper managing nested dictionaries - The following functionality is implemented: - - Tuple keys are treated to access nested dictionaries ('key1', 'key2', 'key3') - - Optionally sep symbol may be set to automatically split string keys into tuple keys: - 'key1.key2.key3' will be treated as a nested key if '.' is set for the sep symbol - - self._ may be used to access nested dictionaries via attributes: dw.key1.key2.key3 - """ - _sep: str = None - _parent: Any = None - _types: Any = dict - _not_recursive_to_others: bool = True - def __new__(cls, dic, *args, parent=None, sep=None, recursive_to_others=None): - if not isinstance(dic, (MutableMapping, DictWrapper)): - return dic - return ClassWrapper.__new__(cls) - - def __init__(self, dic, *, sep: str=None, parent=None, recursive_to_others: bool=False): - if isinstance(dic, DictWrapper): - if sep is None: - sep = dic._sep - recursive_to_others = not dic._not_recursive_to_others - dic = dic._object - super().__init__(dic, types=type(dic)) - - self._sep = sep - self._not_recursive_to_others = not recursive_to_others - if parent: - if sep and sep!=parent._sep: - raise ValueError(f'Inconsistent separators: {sep} (self) and {parent._sep} (parent)') - - self._parent = parent - self._sep = parent._sep - self._types = parent._types - self._not_recursive_to_others = parent._not_recursive_to_others - - @property - def _(self): - return DictWrapperAccess(self) - - def parent(self): - return self._parent - - def child(self, key): - try: - ret = self[key] - except KeyError: - ret = self[key]=self._types() - return self._wrap(ret, parent=self) - - if not isinstance(ret, self._wrapper_class): - raise KeyError('Child {!s} is not DictWrapper'.format(key)) - - return ret - - def keys(self): - return self._object.keys() - - def iterkey(self, key): - if isinstance(key, str): - if self._sep: - yield from key.split(self._sep) - else: - yield key - elif isinstance(key, Sequence): - for sk in key: - yield from self.iterkey(sk) - else: - yield key - - def splitkey(self, key): - it = self.iterkey(key) - try: - return next(it), tuple(it) - except StopIteration: - return None, None - - def get(self, key, *args, **kwargs): - if key==(): - return self - key, rest=self.splitkey(key) - - if not rest: - ret = self._object.get(key, *args, **kwargs) - return self._wrap(ret, parent=self) - - sub = self._wrap(self._object.get(key), parent=self) - if sub is None: - if args: - return args[0] - raise KeyError(f"No nested key '{key}'") - - if self._not_recursive_to_others and not isinstance(sub, DictWrapper): - raise TypeError(f"Nested value for '{key}' has wrong type") - - return sub.get(rest, *args, **kwargs) - - def __getitem__(self, key): - if key==(): - return self - key, rest=self.splitkey(key) - - sub = self._object.__getitem__(key) - sub = self._wrap(sub, parent=self) - if not rest: - return sub - - if sub is None: - raise KeyError( f"No nested key '{key}'" ) - - if self._not_recursive_to_others and not isinstance(sub, DictWrapper): - raise TypeError(f"Nested value for '{key}' has wrong type") - - return sub[rest] - - def __delitem__(self, key): - if key==(): - raise ValueError('May not delete itself') - key, rest=self.splitkey(key) - - sub = self._wrap(self._object.__getitem__(key), parent=self) - if not rest: - del self._object[key] - return - - if self._not_recursive_to_others and not isinstance(sub, DictWrapper): - raise TypeError(f"Nested value for '{key}' has wrong type") - - del sub[rest] - - def setdefault(self, key, value): - key, rest=self.splitkey(key) - - if not rest: - ret=self._object.setdefault(key, value) - return self._wrap(ret, parent=self) - - if key in self: - sub = self._wrap(self._object.get(key), parent=self) - else: - sub = self._object[key] = self._types() - sub = self._wrap(sub, parent=self) - # # cfg._set_parent( self ) - - if self._not_recursive_to_others and not isinstance(sub, DictWrapper): - raise TypeError(f"Nested value for '{key}' has wrong type") - - return sub.setdefault(rest, value) - - def set(self, key, value): - key, rest=self.splitkey(key) - - if not rest: - self._object[key] = value - return value - - if key in self: - sub = self._wrap(self._object.get(key), parent=self) - else: - sub = self._object[key] = self._types() - sub = self._wrap(sub, parent=self) - # # cfg._set_parent( self ) - - if self._not_recursive_to_others and not isinstance(sub, DictWrapper): - raise TypeError(f"Nested value for '{key}' has wrong type") - - return sub.set(rest, value) - - __setitem__= set - - def __contains__(self, key): - if key==(): - return True - key, rest=self.splitkey(key) - - if key not in self._object: - return False - - if rest: - sub = self._wrap(self._object.get(key), parent=self) - - if self._not_recursive_to_others and not isinstance(sub, DictWrapper): - raise TypeError(f"Nested value for '{key}' has wrong type") - - return rest in sub - - return True - - def keys(self): - return self._object.keys() - - def items(self): - for k, v in self._object.items(): - yield k, self._wrap(v, parent=self) - - def values(self): - for v in self._object.values(): - yield self._wrap(v, parent=self) - - def deepcopy(self): - new = DictWrapper(self._types(), parent=self._parent, sep=self._sep, recursive_to_others=not self._not_recursive_to_others) - for k, v in self.items(): - k = k, - if isinstance(v, self._wrapper_class): - new[k] = v.deepcopy()._object - else: - new[k] = v - - new._sep = self._sep - - return new - - def walkitems(self, startfromkey=(), *, appendstartkey=False, maxdepth=None): - v0 = self[startfromkey] - k0 = tuple(self.iterkey(startfromkey)) - - if maxdepth is None: - nextdepth=None - else: - nextdepth=max(maxdepth-len(k0)-1, 0) - - if maxdepth==0 or not isinstance(v0, self._wrapper_class): - if appendstartkey: - yield k0, v0 - else: - yield (), v0 - return - - if not appendstartkey: - k0 = () - - for k, v in v0.items(): - k = k0+(k,) - if isinstance(v, self._wrapper_class): - for k1, v1 in v.walkitems(maxdepth=nextdepth): - yield k+k1, v1 - elif not self._not_recursive_to_others and isinstance(v, MutableMapping): - for k1, v1 in v.items(): - if isinstance(k1, tuple): - yield k+k1, v1 - else: - yield k+(k1,), v1 - else: - yield k, v - - def walkdicts(self): - yieldself=True - for k, v in self.items(): - k = k, - if isinstance(v, self._wrapper_class): - yieldself=False - for k1, v1 in v.walkdicts(): - yield k+k1, v1 - if yieldself: - yield (), self - - def walkkeys(self, *args, **kwargs): - for k, _ in self.walkitems(*args, **kwargs): - yield k - - def walkvalues(self, *args, **kwargs): - for _, v in self.walkitems(*args, **kwargs): - yield v - - def visit(self, visitor, parentkey=()): - visitor = MakeDictWrapperVisitor(visitor) - - if not parentkey: - visitor.start(self) - - visitor.enterdict(parentkey, self) - for k, v in self.items(): - key = parentkey + (k,) - if isinstance(v, self._wrapper_class): - v.visit(visitor, parentkey=key) - else: - visitor.visit(key, v) - - visitor.exitdict(parentkey, self) - - if not parentkey: - visitor.stop(self) diff --git a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/dictwrapperaccess.py b/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/dictwrapperaccess.py deleted file mode 100644 index 76274d633787eda2ebd1602ff7625c224743f2dc..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/dictwrapperaccess.py +++ /dev/null @@ -1,25 +0,0 @@ -class DictWrapperAccess(object): - '''DictWrapper wrapper. Enables attribute based access to nested dictionaries''' - _ = None - def __init__(self, dct): - self.__dict__['_'] = dct - - def __call__(self, key): - return self._.child(key)._ - - def __getattr__(self, key): - ret = self._[key] - - if isinstance(ret, self._._wrapper_class): - return ret._ - - return ret - - def __setattr__(self, key, value): - self._[key]=value - - def __delattr__(self, key): - del self._[key] - - def __dir__(self): - return list(self._.keys()) diff --git a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/visitor.py b/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/visitor.py deleted file mode 100644 index 5600451fd9b4c02c1e919ad69d31268c9c70b70a..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/dictwrapper/visitor.py +++ /dev/null @@ -1,59 +0,0 @@ -class DictWrapperVisitor(object): - def start(self, dct): - pass - - def enterdict(self, k, v): - pass - - def visit(self, k, v): - pass - - def exitdict(self, k, v): - pass - - def stop(self, dct): - pass - -def MakeDictWrapperVisitor(fcn): - if isinstance(fcn, DictWrapperVisitor): - return fcn - - if not callable(fcn): - raise TypeError(f'Expect function, got {type(fcn).__name__}') - - ret=DictWrapperVisitor() - ret.visit = fcn - return ret - -class DictWrapperVisitorDemostrator(DictWrapperVisitor): - fmt = '{action:7s} {depth!s:>5s} {key!s:<{keylen}s} {vtype!s:<{typelen}s} {value}' - opts = dict(keylen=20, typelen=15) - def typestring(self, v): - return type(v).__name__ - - def start(self, d): - v = object.__repr__(d.object) - print('Start printing dictionary:', v) - self._print('Action', 'Key', 'Value', 'Type', depth='Depth') - - def stop(self, _): - print('Done printing dictionary') - - def enterdict(self, k, d): - d = d.object - v = object.__repr__(d) - self._print('Enter', k, v, self.typestring(d)) - - def exitdict(self, k, d): - d = d.object - v = object.__repr__(d) - self._print('Exit', k, v, self.typestring(d)) - - def visit(self, k, v): - self._print('Visit', k, v, self.typestring(v)) - - def _print(self, action, k, v, vtype, *, depth=None): - if depth is None: - depth = len(k) - print(self.fmt.format(action=action, depth=depth, key=k, vtype=vtype, value=v, **self.opts)) - diff --git a/subtrees/dagflow/subtrees/dictwrapper/requirements.txt b/subtrees/dagflow/subtrees/dictwrapper/requirements.txt deleted file mode 100644 index 755c43c771ca6569f968a4c78c66331e92ca2496..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pytest -pytest-cov -coverage diff --git a/subtrees/dagflow/subtrees/dictwrapper/storage/__init__.py b/subtrees/dagflow/subtrees/dictwrapper/storage/__init__.py deleted file mode 100644 index 2f838374ff32bc47a2f2d5228a46df22263aa9e2..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/storage/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .storage import Storage diff --git a/subtrees/dagflow/subtrees/dictwrapper/storage/storage.py b/subtrees/dagflow/subtrees/dictwrapper/storage/storage.py deleted file mode 100644 index 3d168d23430a3f5e879688a82103183924b56409..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/storage/storage.py +++ /dev/null @@ -1,83 +0,0 @@ -from __future__ import annotations - -from collections import UserDict -from collections.abc import Sequence -from typing import Any, Callable, Generator, Optional - -class Storage(UserDict): - _protect: bool = False - - def __init__(*args, protect: bool = False, **kwargs) -> None: - self = args[0] - self._protect = protect - UserDict.__init__(*args, **kwargs) - - def _process_key(self, key: Any) -> tuple: - if isinstance(key, Sequence): - return tuple(sorted(key)) - else: - return frozenset((key,)) - - def __getitem__(self, key: Any) -> Any: - key = self._process_key(key) - return super().__getitem__(key) - - def __setitem__(self, key: Any, val: Any) -> None: - key = self._process_key(key) - if self._protect and key in self: - raise AttributeError( - f"Reassigning of the existed key '{key}' is restricted, " - "due to the protection!" - ) - super().__setitem__(key, val) - - def __contains__(self, key: Any) -> bool: - key = self._process_key(key) - return super().__contains__(key) - - def values(self, *, keys: tuple = (), **kwargs) -> Generator: - for _, val in self.items(*keys, **kwargs): - yield val - - def keys(self, *args, **kwargs) -> Generator: - for key, _ in self.items(*args, **kwargs): - yield key - - def items( - self, - *args, - filterkey: Optional[Callable[[Any], bool]] = None, - filterkeyelem: Optional[Callable[[Any], bool]] = None, - ) -> tuple: - """ - Returns items from the slice by `args`. - If `args` are empty returns all items. - """ - res = super().items() - if args: - args = set(args) - res = (elem for elem in res if args.issubset(elem[0])) - if filterkey: - res = (elem for elem in res if filterkey(elem[0])) - if filterkeyelem: - res = ( - elem - for elem in res - if all(filterkeyelem(key) for key in elem[0]) - ) - - yield from res - - def slice(self, *args, **kwargs) -> Storage: - """ - Returns new `Storage` with keys containing `args`. - It is possible to filter elements by `filterkey` and `filterkeyelem`. - """ - return Storage( - self.items( - *args, - filterkey=kwargs.pop("filterkey", None), - filterkeyelem=kwargs.pop("filterkeyelem", None), - ), # type: ignore - **kwargs, - ) diff --git a/subtrees/dagflow/subtrees/dictwrapper/test/test_dictwrapper.py b/subtrees/dagflow/subtrees/dictwrapper/test/test_dictwrapper.py deleted file mode 100644 index c396e143682af2ae9a67acd3a0d8c17d67a2983c..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/test/test_dictwrapper.py +++ /dev/null @@ -1,336 +0,0 @@ -from dictwrapper.dictwrapper import DictWrapper -import pytest - -def test_dictwrapper_01(): - dw = DictWrapper({}) - - assert not dw - assert len(dw)==0 - -def test_dictwrapper_02(): - dw = DictWrapper(dict(a=1)) - - assert dw - assert len(dw)==1 - -def test_dictwrapper_03(): - d = dict(a=1, b=2, c=3) - dw = DictWrapper(d) - - assert dw.get('a')==1 - assert dw.get('b')==2 - assert dw.get('c')==3 - assert dw.get('d')==None - assert dw.get('d.e')==None - - assert tuple(dw.keys())==('a','b','c') - -@pytest.mark.parametrize('sep', [None, '.']) -def test_dictwrapper_03(sep): - dct = dict(a=1, b=2, c=3, d=dict(e=4), f=dict(g=dict(h=5))) - dct['z.z.z'] = 0 - print(dct) - dw = DictWrapper(dct, sep=sep) - - # - # Test self access - # - assert dw.get(()).object is dct - assert dw[()].object is dct - - # - # Test wrapping - # - assert isinstance(dw.get('d'), DictWrapper) - assert isinstance(dw.get(('f', 'g')), DictWrapper) - - # - # Test get tuple - # - assert dw.get(('d', 'e'))==4 - assert dw.get(('d', 'e1')) is None - assert dw.get(('f', 'g', 'h'))==5 - try: - dw.get(('z', 'z', 'z')) - assert False - except KeyError: - pass - - # - # Test getitem tuple - # - assert dw[('d', 'e')]==4 - try: - dw[('d', 'e1')] - assert False - except KeyError: - pass - assert dw[('f', 'g', 'h')]==5 - - try: - dw[('z', 'z', 'z')] - assert False - except KeyError: - pass - - # - # Test get sep - # - if sep: - assert dw.get('d.e')==4 - else: - assert dw.get('d.e') is None - - if sep: - try: - dw.get('z.z.z') - assert False - except KeyError: - pass - else: - assert dw.get('z.z.z')==0 - - # - # Test getitem sep - # - try: - assert dw['d.e']==4 - assert sep is not None - except KeyError: - pass - - try: - assert dw['f.g.h']==5 - assert dw[('f.g', 'h')]==5 - assert sep is not None - except KeyError: - pass - - if sep: - try: - dw['z.z.z'] - assert False - except KeyError: - pass - else: - assert dw['z.z.z']==0 - - # - # Test contains - # - assert 'a' in dw - assert not 'a1' in dw - assert 'd' in dw - - # - # Test contains tuple - # - assert ('d', 'e') in dw - assert not ('k', 'e') in dw - assert ('f', 'g', 'h') in dw - assert ('f.g.h' in dw) == bool(sep) - assert ('z.z.z' in dw) == bool(not sep) - - # - # Test parents - # - g = dw.get(('f', 'g')) - assert g.parent().parent() is dw - - # - # Test children - # - m=dw.child(('k', 'l', 'm')) - assert dw.get(('k', 'l', 'm')).object is m.object - - # - # Test recursive setitem - # - dw[('k', 'l', 'm', 'n')] = 5 - try: - dw.child(tuple('klmn')) - assert False - except KeyError: - pass - assert dw.get(('k', 'l', 'm', 'n')) == 5 - - dw[('o.l.m.n')] = 6 - assert dw['o.l.m.n'] == 6 - if not sep: - assert dw.object['o.l.m.n'] == 6 - - # - # Test attribute access - # - assert dw._.a==1 - assert dw._.b==2 - assert dw._.c==3 - assert dw._.d.e==4 - assert dw._.f.g.h==5 - - dw._.f.g.h=6 - assert dw._.f.g.h==6 - assert dw._._ is dw - -def test_dictwrapper_06_inheritance(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5, i=6)))]) - dct['z.z.z'] = 0 - - class DictWrapperA(DictWrapper): - def count(self): - return len(tuple(self.walkitems())) - - def depth(self): - return max([len(k) for k in self.walkkeys()]) - - dw = DictWrapperA(dct, sep='.') - assert dw.count()==7 - assert dw['d'].count()==1 - assert dw['f'].count()==2 - assert dw['f.g'].count()==2 - assert dw._.f._.count()==2 - - assert dw.depth()==3 - assert dw['d'].depth()==1 - assert dw['f'].depth()==2 - -def test_dictwrapper_07_delete(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5)))]) - dct['z.z.z'] = 0 - dw = DictWrapper(dct) - - assert 'a' in dw - del dw['a'] - assert 'a' not in dw - - assert ('d', 'e') in dw - del dw[('d', 'e')] - assert ('d', 'e') not in dw - - assert ('f', 'g', 'h') in dw - del dw._.f.g.h - assert ('f', 'g', 'h') not in dw - assert ('f', 'g') in dw - -def test_dictwrapper_08_create(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5)))]) - dct['z.z.z'] = 0 - dw = DictWrapper(dct, sep='.') - - dw._('i.k').l=3 - assert dw._.i.k.l==3 - - child = dw.child('child') - assert dw['child'].object=={} - -def test_dictwrapper_09_dictcopy(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5)))]) - dct['z'] = {} - dw = DictWrapper(dct, sep='.') - - dw1 = dw.deepcopy() - for i, (k, v) in enumerate(dw1.walkdicts()): - # print(i, k) - assert k in dw - assert v._object==dw[k]._object - assert v._object is not dw[k]._object - assert type(v._object) is type(dw[k]._object) - assert i==2 - -def test_dictwrapper_09_walkitems(): - dct = { - 'a': 1, - 'b': 2, - 'c': 3, - 'c1': { - 'i': { - 'j': { - 'k': { - 'l': 6 - } - } - } - }, - 'd': { - 'e': 4 - }, - 'f': { - 'g': { - 'h': 5 - } - } - } - dct['z'] = {} - dw = DictWrapper(dct, sep='.') - - imaxlist=[5, 0, 6, 5, 5, 5, 5, 5, 5] - for imax, maxdepth in zip(imaxlist, [None]+list(range(len(imaxlist)))): - i=0 - print(f'{imax=}, {maxdepth=}') - maxk = -1 - for i, (k, v) in enumerate(dw.walkitems(maxdepth=maxdepth)): - print(i, k, v) - assert maxdepth is None or len(k)<=maxdepth - maxk=max(maxk, len(k)) - print(f'{maxk=}') - print() - assert i==imax - -def test_dictwrapper_09_walk(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5)))]) - dw = DictWrapper(dct) - - keys0 = [ ('a',), ('b', ), ('c',), ('d', 'e'), ('f', 'g', 'h') ] - keys = [k for k, v in dw.walkitems()] - assert keys==keys0 - - assert [(k,v) for k, v in dw.walkitems('a', appendstartkey=True)] == [(('a',), 1)] - assert [(k,v) for k, v in dw.walkitems('a', appendstartkey=False)] == [((), 1)] - assert [(k,v) for k, v in dw.walkitems('d', appendstartkey=True)] == [(('d','e'), 4)] - assert [(k,v) for k, v in dw.walkitems('d', appendstartkey=False)] == [(('e',), 4)] - assert [(k,v) for k, v in dw.walkitems(('f','g'), appendstartkey=True)] == [(('f','g', 'h'), 5)] - assert [(k,v) for k, v in dw.walkitems(('f','g'), appendstartkey=False)] == [(('h',), 5)] - -def test_dictwrapper_10_iterkey(): - d = dict(a=1, b=2, c=3) - dw = DictWrapper(d) - - assert ['a']==list(dw.iterkey('a')) - assert ['a.b']==list(dw.iterkey('a.b')) - assert ['a', 'b']==list(dw.iterkey(('a', 'b'))) - assert [1]==list(dw.iterkey(1)) - assert [1.0]==list(dw.iterkey(1.0)) - -def test_dictwrapper_11_iterkey(): - d = dict(a=1, b=2, c=3) - dw = DictWrapper(d, sep='.') - - assert ['a']==list(dw.iterkey('a')) - assert ['a', 'b']==list(dw.iterkey('a.b')) - assert ['a', 'b']==list(dw.iterkey(('a', 'b'))) - assert [1]==list(dw.iterkey(1)) - assert [1.0]==list(dw.iterkey(1.0)) - -def test_dictwrapper_setdefault_01(): - d = dict(a=dict(b=dict(key='value'))) - dw = DictWrapper(d) - - newdict = dict(newkey='newvalue') - - sd1 = dw.setdefault(('a','b'), newdict) - assert isinstance(sd1, DictWrapper) - assert sd1._object==d['a']['b'] - - sd2 = dw.setdefault(('a','c'), newdict) - assert isinstance(sd2, DictWrapper) - assert sd2._object==newdict - -def test_dictwrapper_eq_01(): - d = dict(a=dict(b=dict(key='value'))) - dw = DictWrapper(d) - - assert dw['a']==d['a'] - assert d['a']==dw['a'] - assert dw['a']!=d - assert dw['a']==dw['a'] - assert dw['a'] is not dw['a'] diff --git a/subtrees/dagflow/subtrees/dictwrapper/test/test_dictwrapper_storage.py b/subtrees/dagflow/subtrees/dictwrapper/test/test_dictwrapper_storage.py deleted file mode 100644 index 13a8c461c8eb8bd5fae31fae6745cd8fc92a1841..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/test/test_dictwrapper_storage.py +++ /dev/null @@ -1,99 +0,0 @@ -from dictwrapper.dictwrapper import DictWrapper -from storage.storage import Storage -from pytest import raises - -def test_dictwrapper_storage(): - storage = Storage({ - ('a1', 'b1', 'c1'): 'v1', - ('a2', 'b2', 'c2'): 'v2', - }) - dct = {'root': { - 'subfolder1': { - 'key1': 'value1', - 'key2': 'value2' - }, - 'subfolder2': { - 'key1': 'value1', - 'key2': 'value2', - 'st': storage - }, - 'key0': 'value0' - }} - dw = DictWrapper(dct, recursive_to_others=True) - dws = DictWrapper(dct, sep='.', recursive_to_others=True) - dwerror = DictWrapper(dct, recursive_to_others=False) - - objects = (dw, dws, dwerror) - objectsok = (dw, dws) - - assert storage['a1', 'b1', 'c1']=='v1' - assert storage['b1', 'a1', 'c1']=='v1' - assert storage['c1', 'a1', 'b1']=='v1' - - for obj in objects: - st1 = obj['root', 'subfolder2', 'st'] - assert st1 is storage - - for obj in objectsok: - assert obj['root', 'subfolder2', 'st', 'a1', 'b1', 'c1']=='v1' - assert obj['root', 'subfolder2', 'st', 'b1', 'a1', 'c1']=='v1' - assert obj['root', 'subfolder2', 'st', 'c1', 'a1', 'b1']=='v1' - assert obj['root', 'subfolder2', 'st', 'a2', 'b2', 'c2']=='v2' - assert obj['root', 'subfolder2', 'st', 'b2', 'a2', 'c2']=='v2' - assert obj['root', 'subfolder2', 'st', 'c2', 'a2', 'b2']=='v2' - - assert ('root', 'subfolder2', 'st', 'a1', 'b1', 'c1') in obj - assert ('root', 'subfolder2', 'st', 'b1', 'a1', 'c1') in obj - assert ('root', 'subfolder2', 'st', 'c1', 'a1', 'b1') in obj - assert ('root', 'subfolder2', 'st', 'a2', 'b2', 'c2') in obj - assert ('root', 'subfolder2', 'st', 'b2', 'a2', 'c2') in obj - assert ('root', 'subfolder2', 'st', 'c2', 'a2', 'b2') in obj - - assert dws['root.subfolder2.st.a1.b1.c1']=='v1' - assert dws['root.subfolder2.st.b1.a1.c1']=='v1' - assert dws['root.subfolder2.st.c1.a1.b1']=='v1' - assert dws['root.subfolder2.st.a2.b2.c2']=='v2' - assert dws['root.subfolder2.st.b2.a2.c2']=='v2' - assert dws['root.subfolder2.st.c2.a2.b2']=='v2' - - assert 'root.subfolder2.st.a1.b1.c1' in dws - assert 'root.subfolder2.st.b1.a1.c1' in dws - assert 'root.subfolder2.st.c1.a1.b1' in dws - assert 'root.subfolder2.st.a2.b2.c2' in dws - assert 'root.subfolder2.st.b2.a2.c2' in dws - assert 'root.subfolder2.st.c2.a2.b2' in dws - - assert 'root.subfolder3.st.c2.a2.b2' not in dws - assert 'root.subfolder2.st.c3.a2.b2' not in dws - - with raises(KeyError): - dws['root.subfolder2.st.a1.b2.c1'] - - with raises(KeyError): - dws['root.subfolder1.st.a1.b1.c1'] - - with raises(TypeError): - dwerror['root', 'subfolder2', 'st', 'a1', 'b1', 'c1'] - - with raises(TypeError): - dwerror.get(('root', 'subfolder2', 'st', 'a1', 'b1', 'c1')) - - with raises(TypeError): - dwerror.get(('root', 'subfolder2', 'st', 'a1', 'b1', 'c1'), 'default') - - with raises(TypeError): - del dwerror['root', 'subfolder2', 'st', 'a1', 'b1', 'c1'] - - with raises(TypeError): - dwerror.setdefault(('root', 'subfolder2', 'st', 'a1', 'b1', 'c1'), 'default') - - with raises(TypeError): - dwerror.set(('root', 'subfolder2', 'st', 'a1', 'b1', 'c1'), 'default') - - with raises(TypeError): - ('root', 'subfolder2', 'st', 'a1', 'b1', 'c1') in dwerror - - # Walks - for k, v in dw.walkitems(): - print(k, v) - diff --git a/subtrees/dagflow/subtrees/dictwrapper/test/test_storage.py b/subtrees/dagflow/subtrees/dictwrapper/test/test_storage.py deleted file mode 100644 index c27818dd2f96272f2754dde50ac15e1a72381b8e..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/test/test_storage.py +++ /dev/null @@ -1,62 +0,0 @@ -from itertools import permutations - -from storage.storage import Storage -from pytest import raises - - -def test_getset(): - storage = Storage() - safestorage = Storage(protect=True) - val = "val" - val2 = ["val", "lav"] - storage["a", "b", "c"] = val - safestorage["a", "b", "c"] = val - for key in permutations(("a", "b", "c")): - assert storage[tuple(key)] == val - assert safestorage[tuple(key)] == val - storage["c", "b", "a"] = val2 - for key in permutations(("a", "b", "c")): - assert storage[tuple(key)] == val2 - with raises(AttributeError): - safestorage[tuple(key)] = val2 - safestorage._protect = False - for key in permutations(("a", "b", "c")): - safestorage[tuple(key)] = val - - -def test_slice_filter(): - storage = Storage() - storage["a", "b"] = 1 - storage["a", "b", "c"] = 2 - storage["a", "c", "d", "b"] = 3 - assert all( - len(tuple(x)) == 3 - for x in (storage.items(), storage.items("a"), storage.items("a", "b")) - ) - assert len(tuple(storage.items("a", "b", "c"))) == 2 - assert len(tuple(storage.items("a", "b", "d", "c"))) == 1 - assert isinstance(storage.slice("a"), Storage) - assert all( - x == storage - for x in ( - storage.slice("a"), - storage.slice("a", "b"), - storage.slice( - filterkey=lambda key: all(elem in "abcd" for elem in key) - ), - storage.slice(filterkeyelem=lambda key: key in "abcd") - ) - ) - assert storage.slice("a", "b", "c") == { - ("a", "b", "c"): 2, - ("a", "b", "c", "d"): 3, - } - assert storage.slice("a", "b", "c", "d") == { - ("a", "b", "c", "d"): 3, - } - assert storage.slice( - filterkey=lambda key: all(elem != "d" for elem in key) - ) == { - ("a", "b", "c"): 2, - ("a", "b"): 1, - } diff --git a/subtrees/dagflow/subtrees/dictwrapper/test/test_visitor.py b/subtrees/dagflow/subtrees/dictwrapper/test/test_visitor.py deleted file mode 100644 index 49bf0ee13106bcb8ac01966fd789a7da97c0f1ee..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/subtrees/dictwrapper/test/test_visitor.py +++ /dev/null @@ -1,32 +0,0 @@ -from dictwrapper.dictwrapper import DictWrapper -from dictwrapper.visitor import DictWrapperVisitorDemostrator - -def test_dictwrapper_04_visitor(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5)))]) - dct['z.z.z'] = 0 - dw = DictWrapper(dct) - - keys0 = (('a',) , ('b',) , ('c',) , ('d', 'e'), ('f', 'g', 'h'), ('z.z.z', )) - values0 = (1, 2, 3, 4, 5, 0) - - keys = tuple(dw.walkkeys()) - values = tuple(dw.walkvalues()) - assert keys==keys0 - assert values==values0 - - class Visitor(object): - keys, values = (), () - def __call__(self, k, v): - self.keys+=k, - self.values+=v, - v = Visitor() - dw.visit(v) - assert v.keys==keys0 - assert v.values==values0 - -def test_dictwrapper_05_visitor(): - dct = dict([('a', 1), ('b', 2), ('c', 3), ('d', dict(e=4)), ('f', dict(g=dict(h=5)))]) - dct['z.z.z'] = 0 - dw = DictWrapper(dct) - - dw.visit(DictWrapperVisitorDemostrator()) diff --git a/subtrees/dagflow/test/core/allocation.py b/subtrees/dagflow/test/core/allocation.py deleted file mode 100644 index ccf8baf0f098447cca22abd7d663dc65f81fb95f..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/core/allocation.py +++ /dev/null @@ -1,34 +0,0 @@ -import numpy as np - -from dagflow.graph import Graph - -def test_output_allocation_1(): - data = np.arange(12, dtype='d').reshape(3,4) - with Graph(close=True) as graph: - n1 = graph.add_node("node1", typefunc=False) - n2 = graph.add_node("node2", typefunc=False) - - out1 = n1._add_output("o1", data=data, allocatable=False) - in1 = n2._add_input("i1") - - out1 >> in1 - - assert (data==out1.data).all() - -def test_output_allocation_2(): - data = np.arange(12, dtype='d').reshape(3,4) - with Graph(close=True) as graph: - n1 = graph.add_node("node1", typefunc=False) - n2 = graph.add_node("node2", typefunc=False) - - out1 = n1._add_output("o1", dtype=data.dtype, shape=data.shape) - in1 = n2._add_input("i1", data=data) - - out1 >> in1 - - assert (data==out1.data).all() - assert (data==in1.data).all() - assert (data==in1._own_data).all() - assert data.dtype==out1.data.dtype - assert data.dtype==in1.data.dtype - assert data.dtype==in1._own_data.dtype diff --git a/subtrees/dagflow/test/core/outputs.py b/subtrees/dagflow/test/core/outputs.py deleted file mode 100644 index 37263af43e94d38742de00c88f09fd8627187607..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/core/outputs.py +++ /dev/null @@ -1,84 +0,0 @@ -from dagflow.lib.Array import Array -from dagflow.lib.Sum import Sum -from dagflow.graph import Graph -from dagflow.output import SettableOutput -from dagflow.exception import CriticalError -import pytest - -def test_SettableOutput_01(): - value = 123. - array_in = (value, ) - array_alt = (value+1, ) - with Graph() as g: - va = Array('test', array_in) - s = Sum('add') - va >> s - g.close() - - va.taint() - newout = SettableOutput.take_over(va.outputs.array) - newout.set(array_alt) - - assert va.outputs.array is newout - assert s.inputs[0].parent_output is newout - assert s.outputs.result.data==array_alt - -def test_SettableOutput_02(): - """Test SettableOutput, Node.invalidate_parents()""" - value = 123. - array_in = (value, ) - array_alt = (value+1, ) - with Graph() as g: - va = Array('test', array_in) - sm1 = Sum('add 1') - sm2 = Sum('add 2') - va >> sm1 >> sm2 - g.close() - - output1 = va.outputs[0] - output2 = sm1.outputs[0] - output3 = sm2.outputs[0] - - assert va.tainted==True - assert sm1.tainted==True - assert sm2.tainted==True - assert va.invalid==False - assert sm1.invalid==False - assert sm2.invalid==False - assert output3.data==array_in - assert va.tainted==False - assert sm1.tainted==False - assert sm2.tainted==False - - newout = SettableOutput.take_over(sm1.outputs[0]) - assert va.tainted==False - assert sm1.tainted==False - assert sm2.tainted==False - assert va.invalid==False - assert sm1.invalid==False - assert sm2.invalid==False - assert output3.data==array_in - - newout.set(array_alt) - assert va.tainted==True - assert sm1.tainted==False - assert sm2.tainted==True - assert va.invalid==True - assert sm1.invalid==False - assert sm2.invalid==False - assert output2.data==array_alt - assert output3.data==array_alt - with pytest.raises(CriticalError): - output1.data==array_alt - - va.invalid = False - assert va.tainted==True - assert sm1.tainted==True - assert sm2.tainted==True - assert va.invalid==False - assert sm1.invalid==False - assert sm2.invalid==False - assert output3.data==array_in - assert output2.data==array_in - assert output1.data==array_in - diff --git a/subtrees/dagflow/test/nodes/test_Array.py b/subtrees/dagflow/test/nodes/test_Array.py deleted file mode 100644 index 46f86032c2cb92b9628687d1b4b45e57fc6781b5..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_Array.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.Sum import Sum -from dagflow.graphviz import savegraph - -from numpy import arange -import pytest - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_Array_00(dtype): - array = arange(12.0, dtype=dtype).reshape(3,4) - with Graph(close=True) as graph: - arr1 = Array('array: store', array, mode='store') - arr2 = Array('array: store (weak)', array, mode='store_weak') - arr3 = Array('array: fill', array, mode='fill') - - assert arr1.tainted==True - assert arr2.tainted==True - assert arr3.tainted==True - - out1 = arr1.outputs['array'] - out2 = arr2.outputs['array'] - out3 = arr3.outputs['array'] - - assert out1.owns_buffer == True - assert out2.owns_buffer == False - assert out3.owns_buffer == True - - assert out1.allocatable == False - assert out2.allocatable == True - assert out3.allocatable == True - - assert (out1._data==array).all() - assert (out2._data==array).all() - assert (out3._data==0.0).all() - - result1 = arr1.get_data(0) - result2 = arr2.get_data(0) - result3 = arr3.get_data(0) - - assert (result1==array).all() - assert (result2==array).all() - assert (result3==array).all() - assert arr1.tainted==False - assert arr2.tainted==False - assert arr3.tainted==False - - savegraph(graph, f"output/test_array_00_{dtype}.png") - -def test_Array_01_set(): - value = 123. - array_in = (value, ) - array_alt = (value+1, ) - va = Array('test', array_in) - sm = Sum('sum') - va >> sm - va.close() - sm.close() - - output = va.outputs[0] - output2 = sm.outputs[0] - - assert va.tainted==True - assert sm.tainted==True - assert output.data[0]==value - assert output2.data[0]==value - assert va.tainted==False - assert sm.tainted==False - - assert va.set(array_in, check_taint=True)==False - assert va.tainted==False - assert sm.tainted==False - assert (output.data==array_in).all() - assert (output2.data==array_in).all() - - assert va.set(array_in)==True - assert va.tainted==False - assert sm.tainted==True - assert (output.data==array_in).all() - assert (output2.data==array_in).all() - assert va.tainted==False - assert sm.tainted==False - - assert va.set(array_alt, check_taint=True)==True - assert va.tainted==False - assert sm.tainted==True - assert (output.data==array_alt).all() - assert (output2.data==array_alt).all() - assert va.tainted==False - assert sm.tainted==False diff --git a/subtrees/dagflow/test/nodes/test_Cholesky.py b/subtrees/dagflow/test/nodes/test_Cholesky.py deleted file mode 100644 index 328f5985e9d3a3ac72e58be9f1033d65d955031f..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_Cholesky.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python - -from dagflow.exception import TypeFunctionError -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.Cholesky import Cholesky -import numpy as np -import scipy -from pytest import raises -from dagflow.graphviz import savegraph - -import pytest - -@pytest.mark.parametrize("dtype", ('d', 'f')) -def test_Cholesky_00(dtype): - inV = np.array([[10, 2, 1], [ 2, 12, 3], [ 1, 3, 13]], dtype=dtype) - inV2 = inV@inV - inD = np.diag(inV) - inL2d1 = scipy.linalg.cholesky(inV, lower=True) - inL2d2 = scipy.linalg.cholesky(inV2, lower=True) - inL1d = np.sqrt(inD) - - with Graph(close=True) as graph: - V1 = Array('V1', inV, mode='store') - V2 = Array('V2', inV2, mode='store') - D = Array('D', (inD), mode='store') - chol2d = Cholesky('Cholesky 2d') - chol1d = Cholesky('Cholesky 1d') - (V1, V2) >> chol2d - D >> chol1d - - assert V1.tainted==True - assert V2.tainted==True - assert chol2d.tainted==True - assert chol1d.tainted==True - - result2d1 = chol2d.get_data(0) - result2d2 = chol2d.get_data(1) - result1d = chol1d.get_data(0) - assert V1.tainted==False - assert V2.tainted==False - assert D.tainted==False - assert chol2d.tainted==False - assert chol1d.tainted==False - - assert np.allclose(inL2d1, result2d1, atol=0, rtol=0) - assert np.allclose(inL2d2, result2d2, atol=0, rtol=0) - assert np.allclose(inL1d, result1d, atol=0, rtol=0) - - savegraph(graph, f"output/test_Cholesky_00_{dtype}.png") - -def test_Cholesky_01_typefunctions(): - inV = np.array([ - [10, 2, 1], - [ 2, 12, 3], - ], dtype='d') - - with Graph() as g1: - V1 = Array('V1', inV, mode='store') - chol1 = Cholesky('Cholesky') - V1 >> chol1 - - with Graph() as g2: - V2 = Array('V2', inV[0], mode='store') - chol2 = Cholesky('Cholesky') - V2 >> chol1 - - with raises(TypeFunctionError): - g1.close() - - with raises(TypeFunctionError): - g2.close() diff --git a/subtrees/dagflow/test/nodes/test_CovmatrixFromCormatrix.py b/subtrees/dagflow/test/nodes/test_CovmatrixFromCormatrix.py deleted file mode 100644 index 67478f1745a8a24c308c68456ee929b7e9a87262..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_CovmatrixFromCormatrix.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -from numpy import arange - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.lib.Array import Array -from dagflow.lib.CovmatrixFromCormatrix import CovmatrixFromCormatrix - -from numpy import array, allclose, tril -import pytest - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_CovmatrixFromCormatrix_00(dtype): - inSigma = arange(1.0, 4.0, dtype=dtype) - inC = array([ - [1.0, 0.5, 0.0], - [0.5, 1.0, 0.9], - [0.0, 0.9, 1.0], - ], - dtype=dtype) - with Graph(close=True) as graph: - matrix = Array('matrix', inC) - sigma = Array('sigma', inSigma) - cov = CovmatrixFromCormatrix('covariance') - - sigma >> cov.inputs['sigma'] - matrix >> cov - - inV = inC * inSigma[:,None] * inSigma[None,:] - V = cov.get_data() - - assert allclose(inV, V, atol=0, rtol=0) - assert allclose(tril(V), tril(V.T), atol=0, rtol=0) - - savegraph(graph, f"output/test_CovmatrixFromCormatrix_00_{dtype}.png", show=['all']) - diff --git a/subtrees/dagflow/test/nodes/test_ElSumSq.py b/subtrees/dagflow/test/nodes/test_ElSumSq.py deleted file mode 100644 index a3e0f3c30906226a9d6f94078ffeed0a300f5fe8..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_ElSumSq.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.ElSumSq import ElSumSq -from dagflow.graphviz import savegraph - -from numpy import arange, sum -import pytest - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_ElSumSq_01(dtype): - arrays_in = tuple(arange(12, dtype=dtype)*i for i in (1, 2, 3)) - arrays2_in = tuple(a**2 for a in arrays_in) - - with Graph(close=True) as graph: - arrays = tuple(Array('test', array_in) for array_in in arrays_in) - sm = ElSumSq('sumsq') - arrays >> sm - - output = sm.outputs[0] - - res = sum(arrays2_in) - assert sm.tainted==True - assert all(output.data==res) - assert sm.tainted==False - - arrays2_in = (arrays2_in[1],) + arrays2_in[1:] - res = sum(arrays2_in) - assert arrays[0].set(arrays[1].get_data()) - assert sm.tainted==True - assert all(output.data==res) - assert sm.tainted==False - - savegraph(graph, f"output/test_SumSq_00_{dtype}.png", show='all') diff --git a/subtrees/dagflow/test/nodes/test_Integrator.py b/subtrees/dagflow/test/nodes/test_Integrator.py deleted file mode 100644 index 1463982a4278ad6febae798d86bb106b759079a5..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_Integrator.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python -from dagflow.exception import TypeFunctionError -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.Integrator import Integrator - -from pytest import raises - - -def test_Integrator_00(debug_graph): - with Graph(debug=debug_graph, close=True): - arr1 = Array("array", [1.0, 2.0, 3.0]) - arr2 = Array("array", [3.0, 2.0, 1.0]) - weights = Array("weights", [2.0, 2.0, 2.0]) - ordersX = Array("ordersX", [1, 1, 1]) - integrator = Integrator("integrator", mode="1d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - assert (integrator.outputs[0].data == [2, 4, 6]).all() - assert (integrator.outputs[1].data == [6, 4, 2]).all() - - -def test_Integrator_01(debug_graph): - with Graph(debug=debug_graph, close=True): - arr1 = Array("array", [1.0, 2.0, 3.0]) - arr2 = Array("array", [3.0, 2.0, 1.0]) - weights = Array("weights", [2.0, 2.0, 2.0]) - ordersX = Array("ordersX", [2, 0, 1]) - integrator = Integrator("integrator", mode="1d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - assert (integrator.outputs[0].data == [6, 0, 6]).all() - assert (integrator.outputs[1].data == [10, 0, 2]).all() - - -def test_Integrator_02(debug_graph): - arr123 = [1.0, 2.0, 3.0] - with Graph(debug=debug_graph, close=True): - arr1 = Array("array", [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) - arr2 = Array("array", [arr123, arr123]) - weights = Array("weights", [[1.0, 1.0, 1.0], arr123]) - ordersX = Array("ordersX", [1, 1]) - ordersY = Array("ordersY", [1, 1, 1]) - integrator = Integrator("integrator", mode="2d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - ordersY >> integrator("ordersY") - assert (integrator.outputs[0].data == [[1, 1, 1], [1, 2, 3]]).all() - assert (integrator.outputs[1].data == [[1, 2, 3], [1, 4, 9]]).all() - - -def test_Integrator_03(debug_graph): - arr123 = [1.0, 2.0, 3.0] - with Graph(debug=debug_graph, close=True): - arr1 = Array("array", [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) - arr2 = Array("array", [arr123, arr123]) - weights = Array("weights", [[1.0, 1.0, 1.0], arr123]) - ordersX = Array("ordersX", [1, 1]) - ordersY = Array("ordersY", [1, 2, 0]) - integrator = Integrator("integrator", mode="2d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - ordersY >> integrator("ordersY") - assert (integrator.outputs[0].data == [[1, 2, 0], [1, 5, 0]]).all() - assert (integrator.outputs[1].data == [[1, 5, 0], [1, 13, 0]]).all() - - -def test_Integrator_04(debug_graph): - arr123 = [1.0, 2.0, 3.0] - with Graph(debug=debug_graph, close=True): - arr1 = Array("array", [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) - arr2 = Array("array", [arr123, arr123]) - weights = Array("weights", [[1.0, 1.0, 1.0], arr123]) - ordersX = Array("ordersX", [0, 2]) - ordersY = Array("ordersY", [1, 1, 1]) - integrator = Integrator("integrator", mode="2d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - ordersY >> integrator("ordersY") - assert (integrator.outputs[0].data == [[0, 0, 0], [2, 3, 4]]).all() - assert (integrator.outputs[1].data == [[0, 0, 0], [2, 6, 12]]).all() - - -def test_Integrator_05(debug_graph): - unity = [1.0, 1.0, 1.0] - with Graph(debug=debug_graph, close=True): - arr1 = Array( - "array", [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] - ) - arr2 = Array("array", [unity, unity, unity]) - weights = Array("weights", [unity, unity, unity]) - ordersX = Array("ordersX", [1, 1, 1]) - ordersY = Array("ordersY", [1, 0, 2]) - integrator = Integrator("integrator", mode="2d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - ordersY >> integrator("ordersY") - assert ( - integrator.outputs[0].data == [[1, 0, 0], [0, 0, 1], [0, 0, 1]] - ).all() - assert ( - integrator.outputs[1].data == [[1, 0, 2], [1, 0, 2], [1, 0, 2]] - ).all() - - -# test wrong ordersX: sum(ordersX) != shape -def test_Integrator_06(debug_graph): - arr = [1.0, 2.0, 3.0] - with Graph(debug=debug_graph): - arr1 = Array("array", arr) - weights = Array("weights", arr) - ordersX = Array("ordersX", [1, 2, 3]) - integrator = Integrator("integrator", mode="1d") - arr1 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - with raises(TypeFunctionError): - integrator.close() - - -# test wrong ordersX: sum(ordersX[i]) != shape[i] -def test_Integrator_07(debug_graph): - arr = [1.0, 2.0, 3.0] - with Graph(debug=debug_graph): - arr1 = Array("array", [arr, arr]) - weights = Array("weights", [arr, arr]) - ordersX = Array("ordersX", [1, 3]) - ordersY = Array("ordersY", [1, 0, 0]) - integrator = Integrator("integrator", mode="2d") - arr1 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - ordersY >> integrator("ordersY") - with raises(TypeFunctionError): - integrator.close() - - -# test wrong shape -def test_Integrator_08(debug_graph): - with Graph(debug=debug_graph, close=False): - arr1 = Array("array", [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) - arr2 = Array("array", [[1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0]]) - weights = Array("weights", [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) - ordersX = Array("ordersX", [0, 2]) - ordersY = Array("ordersY", [1, 1, 1, 3]) - integrator = Integrator("integrator", mode="2d") - arr1 >> integrator - arr2 >> integrator - weights >> integrator("weights") - ordersX >> integrator("ordersX") - ordersY >> integrator("ordersY") - with raises(TypeFunctionError): - integrator.close() diff --git a/subtrees/dagflow/test/nodes/test_NormalizeCorrelatedVars.py b/subtrees/dagflow/test/nodes/test_NormalizeCorrelatedVars.py deleted file mode 100644 index 234b3e233d4d7b7d22b88d43349753bfc96d4f98..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_NormalizeCorrelatedVars.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python - -from numpy import arange -from dagflow.exception import TypeFunctionError - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.lib.Array import Array -from dagflow.lib.NormalizeCorrelatedVars import NormalizeCorrelatedVars -from dagflow.lib.Cholesky import Cholesky - -from numpy import array, arange, allclose, sqrt -from scipy.linalg import solve_triangular, cholesky - -import pytest -from pytest import raises - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_NormalizeCorrelatedVars_00(dtype): - inCentral = arange(3.0, dtype=dtype)*100.0 - inV = array([[10, 2, 1], [ 2, 12, 3], [ 1, 3, 13]], dtype=dtype) - inD = inV.diagonal() - inL = cholesky(inV, lower=True) - inLd = sqrt(inD) - inOffset = array((-10.0, 20.0, 30.0), dtype=dtype) - inVec = inCentral + inOffset - with Graph(close=True) as graph: - matrix = Array('matrix', inV) - diag = Array('diag', inD) - Lmatrix = Cholesky('cholesky 1d') - Ldiag = Cholesky('cholesky 2d') - central = Array('central', inCentral) - vec = Array('vec', inVec) - norm1d_fwd = NormalizeCorrelatedVars('norm1d fwd') - norm2d_fwd = NormalizeCorrelatedVars('norm2d fwd') - - norm1d_bwd = NormalizeCorrelatedVars('norm1d bwd', mode='backward') - norm2d_bwd = NormalizeCorrelatedVars('norm2d bwd', mode='backward') - - central >> norm1d_fwd.inputs['central'] - central >> norm2d_fwd.inputs['central'] - central >> norm1d_bwd.inputs['central'] - central >> norm2d_bwd.inputs['central'] - - matrix >> Lmatrix - diag >> Ldiag - - Lmatrix >> norm2d_fwd.inputs['matrix'] - Ldiag >> norm1d_fwd.inputs['matrix'] - Lmatrix >> norm2d_bwd.inputs['matrix'] - Ldiag >> norm1d_bwd.inputs['matrix'] - - vec >> norm1d_fwd >> norm1d_bwd - vec >> norm2d_fwd >> norm2d_bwd - - nodes = ( - matrix, diag, - Lmatrix, Ldiag, - central, vec, - norm1d_fwd, norm2d_fwd, - norm1d_bwd, norm2d_bwd, - ) - - assert all(node.tainted==True for node in nodes) - back_matrix = norm2d_bwd.get_data(0) - back_diag = norm1d_bwd.get_data(0) - - assert all(node.tainted==False for node in nodes) - - result_matrix = norm2d_fwd.get_data(0) - result_diag = norm1d_fwd.get_data(0) - - norm1 = solve_triangular(inL, inOffset, lower=True) - norm2 = inOffset/inLd - - if debug: - print('V:', inV) - print('Vdiag:', inD) - print('L:', inL) - print('Ldiag:', inLd) - print('Central:', inCentral) - print('In:', inVec) - print('Offset:', inOffset) - print('Norm 1:', norm1) - print('Norm 2:', norm2) - print('Rec 1:', back_matrix) - print('Rec 2:', back_diag) - print('Diff 1:', inVec-back_matrix) - print('Diff 2:', inVec-back_diag) - - assert allclose(norm1, result_matrix, atol=0, rtol=0) - assert allclose(norm2, result_diag, atol=0, rtol=0) - assert allclose(inVec, back_matrix, atol=1.e-14, rtol=0) - assert allclose(inVec, back_diag, atol=0, rtol=0) - - savegraph(graph, f"output/test_NormalizeCorrelatedVars_00_{dtype}.png") - -def test_NormalizeCorrelatedVars_01(dtype='d'): - inVec = arange(4.0, dtype=dtype)*100.0 - inV = array([[10, 2, 1], [ 2, 12, 3], [ 1, 3, 13]], dtype=dtype) - inD = inV.diagonal() - with Graph() as graph1: - diag = Array('diag', inD) - vec = Array('vec', inVec) - norm1d_fwd = NormalizeCorrelatedVars('norm1d fwd') - - vec >> norm1d_fwd.inputs['central'] - diag >> norm1d_fwd.inputs['matrix'] - - with Graph() as graph2: - matrix = Array('matrix', inV) - vec = Array('vec', inVec) - norm2d_fwd = NormalizeCorrelatedVars('norm2d fwd') - - vec >> norm2d_fwd.inputs['central'] - matrix >> norm2d_fwd.inputs['matrix'] - - with raises(TypeFunctionError): - graph1.close() - - with raises(TypeFunctionError): - graph2.close() - diff --git a/subtrees/dagflow/test/nodes/test_NormalizeCorrelatedVars2.py b/subtrees/dagflow/test/nodes/test_NormalizeCorrelatedVars2.py deleted file mode 100644 index e5ee5172762873563c125d991fb7d4c53eec3aed..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_NormalizeCorrelatedVars2.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env python - -from numpy import arange -from dagflow.exception import TypeFunctionError - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.lib.Array import Array -from dagflow.lib.NormalizeCorrelatedVars2 import NormalizeCorrelatedVars2 -from dagflow.lib.Cholesky import Cholesky - -from numpy import array, arange, allclose, sqrt, full_like, zeros_like, ones_like, finfo -from scipy.linalg import solve_triangular, cholesky - -import pytest -from pytest import raises - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_NormalizeCorrelatedVars2_00(dtype): - fp_tolerance = finfo(dtype).resolution*2 - - inCentral = arange(3.0, dtype=dtype)*100.0 - inV = array([[10, 2, 1], [ 2, 12, 3], [ 1, 3, 13]], dtype=dtype) - inD = inV.diagonal() - inL = cholesky(inV, lower=True) - inLd = sqrt(inD) - inOffset = array((-10.0, 20.0, 30.0), dtype=dtype) - inVec = inCentral + inOffset - inNorm = full_like(inVec, -100) - with Graph(close=True) as graph: - var_matrix = Array('var_matrix', inV) - var_diag = Array('var_diag', inD) - Lmatrix = Cholesky('cholesky 1d') - Ldiag = Cholesky('cholesky 2d') - central = Array('central', inCentral) - value1d = Array('vec 1d', inVec, mode='store_weak') - normvalue1d = Array('normvalue 1d', inNorm, mode='store_weak') - value2d = Array('vec 2d', inVec, mode='store_weak') - normvalue2d = Array('normvalue 2d', inNorm, mode='store_weak') - norm1d = NormalizeCorrelatedVars2('norm1d') - norm2d = NormalizeCorrelatedVars2('norm2d') - - central >> norm1d.inputs['central'] - central >> norm2d.inputs['central'] - - var_matrix >> Lmatrix - var_diag >> Ldiag - - Lmatrix >> norm2d.inputs['matrix'] - Ldiag >> norm1d.inputs['matrix'] - - (value1d, normvalue1d) >> norm1d - (value2d, normvalue2d) >> norm2d - - - nodes = ( - var_matrix, var_diag, - Lmatrix, Ldiag, - central, - value1d, normvalue1d, - value1d, normvalue2d, - norm1d, norm2d, - ) - - assert all(node.tainted==True for node in nodes) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - - assert all(node.tainted==False for node in nodes) - assert all(inNorm!=back_matrix) - assert all(inNorm!=back_diag) - - norm1 = solve_triangular(inL, inOffset, lower=True) - norm2 = inOffset/inLd - - if debug: - print('V:', inV) - print('Vdiag:', inD) - print('L:', inL) - print('Ldiag:', inLd) - print('Central:', inCentral) - print('In:', inVec) - print('Offset:', inOffset) - print('Norm 1:', norm1) - print('Norm 2:', norm2) - print('Rec 1:', back_matrix) - print('Rec 2:', back_diag) - print('Diff 1:', inVec-back_matrix) - print('Diff 2:', inVec-back_diag) - - assert allclose(norm1, norm_matrix, atol=0, rtol=0) - assert allclose(norm2, norm_diag, atol=0, rtol=0) - assert allclose(inVec, back_matrix, atol=0, rtol=0) - assert allclose(inVec, back_diag, atol=0, rtol=0) - - # - # Set norm value - # - inZeros = zeros_like(inVec) - normvalue1d.set(inZeros) - normvalue2d.set(inZeros) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - assert allclose(inZeros, norm_matrix, atol=0, rtol=0) - assert allclose(inZeros, norm_diag, atol=0, rtol=0) - assert allclose(inCentral, back_matrix, atol=0, rtol=0) - assert allclose(inCentral, back_diag, atol=0, rtol=0) - - # - # Set normvalue - # - inOnes = ones_like(inVec) - normvalue1d.set(inOnes) - normvalue2d.set(inOnes) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - checkDiagOnes = inCentral + inLd - checkMatrixOnes = inCentral + inL@inOnes - assert allclose(inOnes, norm_matrix, atol=0, rtol=0) - assert allclose(inOnes, norm_diag, atol=0, rtol=0) - assert allclose(checkMatrixOnes, back_matrix, atol=0, rtol=0) - assert allclose(checkDiagOnes, back_diag, atol=0, rtol=0) - - # - # Set value (with immediate flag) - # - norm2d._immediate = True - norm1d._immediate = True - value1d.set(inCentral) - value2d.set(inCentral) - norm_matrix = norm2d.outputs[1]._data - norm_diag = norm1d.outputs[1]._data - back_matrix = norm2d.outputs[0]._data - back_diag = norm1d.outputs[0]._data - assert allclose(inZeros, norm_matrix, atol=0, rtol=0) - assert allclose(inZeros, norm_diag, atol=0, rtol=0) - assert allclose(inCentral, back_matrix, atol=0, rtol=0) - assert allclose(inCentral, back_diag, atol=0, rtol=0) - - # - # Set value (with immediate flag) - # - norm2d._immediate = True - norm1d._immediate = True - normvalue1d.set(inOnes) - normvalue2d.set(inOnes) - norm_matrix = norm2d.outputs[1]._data - norm_diag = norm1d.outputs[1]._data - back_matrix = norm2d.outputs[0]._data - back_diag = norm1d.outputs[0]._data - assert allclose(inOnes, norm_matrix, atol=0, rtol=0) - assert allclose(inOnes, norm_diag, atol=0, rtol=0) - assert allclose(checkMatrixOnes, back_matrix, atol=0, rtol=0) - assert allclose(checkDiagOnes, back_diag, atol=0, rtol=0) - - # - # Set central - # - norm2d._immediate = False - norm1d._immediate = False - central.set(-inOnes) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - assert all(norm_matrix!=inOnes) - assert all(norm_matrix!=inOnes) - assert allclose(checkMatrixOnes, back_matrix, atol=0, rtol=0) - assert allclose(checkDiagOnes, back_diag, atol=0, rtol=0) - - # - # Revert central - # - central.set(inCentral) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - assert allclose(inOnes, norm_matrix, atol=fp_tolerance, rtol=0) - assert allclose(inOnes, norm_diag, atol=fp_tolerance, rtol=0) - assert allclose(checkMatrixOnes, back_matrix, atol=0, rtol=0) - assert allclose(checkDiagOnes, back_diag, atol=0, rtol=0) - - # - # Set sigma - # - var_matrix.set(inV*2) - var_diag.set(inD*2) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - assert all(norm_matrix!=inOnes) - assert all(norm_matrix!=inOnes) - assert allclose(checkMatrixOnes, back_matrix, atol=0, rtol=0) - assert allclose(checkDiagOnes, back_diag, atol=0, rtol=0) - - # - # Revert sigma - # - var_matrix.set(inV) - var_diag.set(inD) - back_matrix = norm2d.get_data(0) - back_diag = norm1d.get_data(0) - norm_matrix = norm2d.get_data(1) - norm_diag = norm1d.get_data(1) - assert allclose(inOnes, norm_matrix, atol=fp_tolerance, rtol=0) - assert allclose(inOnes, norm_diag, atol=fp_tolerance, rtol=0) - assert allclose(checkMatrixOnes, back_matrix, atol=0, rtol=0) - assert allclose(checkDiagOnes, back_diag, atol=0, rtol=0) - - savegraph(graph, f"output/test_NormalizeCorrelatedVars2_00_{dtype}.png", show=['all']) - -def test_NormalizeCorrelatedVars2_01(dtype='d'): - inVec = arange(4.0, dtype=dtype)*100.0 - inNorm = full_like(inVec, -100) - inV = array([[10, 2, 1], [ 2, 12, 3], [ 1, 3, 13]], dtype=dtype) - inD = inV.diagonal() - with Graph() as graph1: - var_diag = Array('var_diag', inD) - vec = Array('vec', inVec, mode='store_weak') - nvec = Array('vec', inNorm, mode='store_weak') - norm1d = NormalizeCorrelatedVars2('norm1d') - - vec >> norm1d.inputs['central'] - var_diag >> norm1d.inputs['matrix'] - - (vec, nvec) >> norm1d - - with Graph() as graph2: - var_matrix = Array('var_matrix', inV) - vec = Array('vec', inVec, mode='store_weak') - nvec = Array('vec', inNorm, mode='store_weak') - norm2d = NormalizeCorrelatedVars2('norm2d') - - vec >> norm2d.inputs['central'] - var_matrix >> norm2d.inputs['matrix'] - - (vec, nvec) >> norm2d - - with raises(TypeFunctionError): - graph1.close() - - with raises(TypeFunctionError): - graph2.close() - diff --git a/subtrees/dagflow/test/nodes/test_Sum.py b/subtrees/dagflow/test/nodes/test_Sum.py deleted file mode 100644 index 1ab8897d58c6beda0f9679dc2f6a9279ac022ab4..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_Sum.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.Sum import Sum -from dagflow.graphviz import savegraph - -from numpy import arange, sum -import pytest - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_Sum_01(dtype): - arrays_in = tuple(arange(12, dtype=dtype)*i for i in (1, 2, 3)) - - with Graph(close=True) as graph: - arrays = tuple(Array('test', array_in) for array_in in arrays_in) - sm = Sum('sum') - arrays >> sm - - output = sm.outputs[0] - - res = sum(arrays_in, axis=0) - - assert sm.tainted==True - assert all(output.data==res) - assert sm.tainted==False - - arrays_in = (arrays_in[1],) + arrays_in[1:] - res = sum(arrays_in, axis=0) - assert arrays[0].set(arrays[1].get_data()) - assert sm.tainted==True - assert all(output.data==res) - assert sm.tainted==False - - savegraph(graph, f"output/test_sum_00_{dtype}.png") diff --git a/subtrees/dagflow/test/nodes/test_SumMatOrDiag.py b/subtrees/dagflow/test/nodes/test_SumMatOrDiag.py deleted file mode 100644 index 5b8bacfe276436e5d3730ab936987c00117b7543..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_SumMatOrDiag.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.SumMatOrDiag import SumMatOrDiag -from dagflow.graphviz import savegraph -from dagflow.exception import TypeFunctionError - -from numpy import arange, diag, allclose -import pytest -from pytest import raises - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_SumMatOrDiag_01(dtype): - for size in (5, 4): - array1 = arange(size, dtype=dtype)+1.0 - array2 = arange(size, dtype=dtype)*3 - matrix1 = arange(size*size, dtype=dtype).reshape(size, size)+1.0 - matrix2 = arange(size*size, dtype=dtype).reshape(size, size)*2.5 - arrays_in = (array1, array2, matrix1, matrix2) - - combinations = ((0,), (2,), (0, 1), (0, 2), (2, 0), (0, 1, 2), (2, 3), (0, 1, 2, 3)) - - sms = [] - - with Graph(close=True) as graph: - arrays = tuple(Array(f'test {i}', array_in) for i, array_in in enumerate(arrays_in)) - - for cmb in combinations: - sm = SumMatOrDiag(f'sum {cmb}') - tuple(arrays[i] for i in cmb) >> sm - sms.append(sm) - - for cmb, sm in zip(combinations, sms): - res = 0.0 - all1d = True - for i in cmb: - array_in = arrays_in[i] - if len(array_in.shape)==1: - array_in = diag(array_in) - else: - all1d = False - res += array_in - - if all1d: - res = diag(res) - - assert sm.tainted==True - output = sm.outputs[0] - assert allclose(output.data, res, rtol=0, atol=0) - assert sm.tainted==False - - savegraph(graph, f"output/test_SumMatOrDiag_00_{dtype}_{size}.png", show='all') - -def test_SumMatOrDiag_02(dtype='d'): - size = 5 - in_array1 = arange(size, dtype=dtype) # 0 - in_array2 = arange(size+1, dtype=dtype) # 1 - in_matrix1 = arange(size*size, dtype=dtype).reshape(size, size) # 2 - in_matrix2 = arange(size*(size+1), dtype=dtype).reshape(size, size+1) # 3 - in_matrix3 = arange((size+1)*(size+1), dtype=dtype).reshape(size+1, size+1) # 4 - arrays_in = (in_array1, in_array2, in_matrix1, in_matrix2, in_matrix3) - - combinations = ( - (0, 1), (0, 3), (0, 4), - (3, 0), (4, 0), - (2, 3), (2, 4) - ) - with Graph(close=False): - arrays = tuple(Array(f'test {i}', array_in) for i, array_in in enumerate(arrays_in)) - - for i1, i2 in combinations: - sm = SumMatOrDiag(f'sum') - (arrays[i1], arrays[i2]) >> sm - - with raises(TypeFunctionError): - sm.close() - diff --git a/subtrees/dagflow/test/nodes/test_SumSq.py b/subtrees/dagflow/test/nodes/test_SumSq.py deleted file mode 100644 index d70a48113e2f57d0ceca9f028c669c36f1cfd8b0..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_SumSq.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.lib.Array import Array -from dagflow.lib.SumSq import SumSq -from dagflow.graphviz import savegraph - -from numpy import arange, sum -import pytest - -debug = False - -@pytest.mark.parametrize('dtype', ('d', 'f')) -def test_SumSq_01(dtype): - arrays_in = tuple(arange(12, dtype=dtype)*i for i in (1, 2, 3)) - arrays2_in = tuple(a**2 for a in arrays_in) - - with Graph(close=True) as graph: - arrays = tuple(Array('test', array_in) for array_in in arrays_in) - sm = SumSq('sumsq') - arrays >> sm - - output = sm.outputs[0] - - res = sum(arrays2_in, axis=0) - - assert sm.tainted==True - assert all(output.data==res) - assert sm.tainted==False - - arrays2_in = (arrays2_in[1],) + arrays2_in[1:] - res = sum(arrays2_in, axis=0) - assert arrays[0].set(arrays[1].get_data()) - assert sm.tainted==True - assert all(output.data==res) - assert sm.tainted==False - - savegraph(graph, f"output/test_SumSq_00_{dtype}.png") diff --git a/subtrees/dagflow/test/nodes/test_View.py b/subtrees/dagflow/test/nodes/test_View.py deleted file mode 100644 index 6150da820ac9fe1271e50bab0b91c47c6fca3b10..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_View.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - - -from numpy import arange - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.lib.View import View -from dagflow.lib.Array import Array - -debug = False - -def test_View_00(): - """Create four nodes: sum up three of them, multiply the result by the fourth - Use graph context to create the graph. - Use one-line code for connecting the nodes - """ - array = arange(5.0) - with Graph(close=True) as graph: - initial = Array('array', array) - view = View("view") - view2 = View("view2") - - initial >> view >> view2 - - assert initial.tainted==True - assert view.tainted==True - assert view2.tainted==True - - result = view.get_data() - result2 = view2.get_data() - assert (result==array).all() - assert (result2==array).all() - assert view.tainted==False - assert view2.tainted==False - assert initial.tainted==False - - d1=initial.outputs[0]._data - d2=view.outputs[0]._data - d3=view2.outputs[0]._data - assert (d1==d2).all() - assert (d1==d3).all() - d1[:]=-1 - assert (d2==-1).all() - assert (d3==-1).all() - - initial.taint() - assert initial.tainted==True - assert view.tainted==True - assert view2.tainted==True - - view2.touch() - savegraph(graph, "output/test_View_00.png") diff --git a/subtrees/dagflow/test/nodes/test_ViewConcat.py b/subtrees/dagflow/test/nodes/test_ViewConcat.py deleted file mode 100644 index f9703cb99dae4f80ee9169687e4d722ba3de7472..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/nodes/test_ViewConcat.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python - -from pytest import raises -import numpy as np - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.lib.ViewConcat import ViewConcat -from dagflow.lib.View import View -from dagflow.lib.NormalizeCorrelatedVars2 import NormalizeCorrelatedVars2 -from dagflow.lib.Array import Array -from dagflow.exception import ConnectionError - -import pytest - -debug = False - -@pytest.mark.parametrize('closemode', ['graph', 'recursive']) -def test_ViewConcat_00(closemode): - """Create four nodes: sum up three of them, multiply the result by the fourth - Use graph context to create the graph. - Use one-line code for connecting the nodes - """ - closegraph = closemode=='graph' - - array1 = np.arange(5.0) - array2 = np.ones(shape=10, dtype='d') - array3 = np.zeros(shape=12, dtype='d')-1 - array = np.concatenate((array1, array2, array3)) - arrays = (array1, array2, array3) - n1, n2, _ = (a.size for a in arrays) - with Graph(debug=debug, close=closegraph) as graph: - inputs = [Array('array', array, mode='fill') for array in arrays] - concat = ViewConcat("concat") - view = View("view") - - inputs >> concat >> view - - if not closegraph: - view.close() - - graph.print() - - assert all(initial.tainted==True for initial in inputs) - assert concat.tainted==True - assert view.tainted==True - - result = concat.get_data() - result_view = view.get_data() - assert (result==array).all() - assert (result_view==array).all() - assert concat.tainted==False - assert view.tainted==False - assert all(i.tainted==False for i in inputs) - - data1, data2, data3 = (i.get_data(0) for i in inputs) - datac = concat.get_data(0) - datav = view.get_data(0) - assert all(data1==datac[:data1.size]) - assert all(data2==datac[n1:n1+data2.size]) - assert all(data3==datac[n1+n2:n1+n2+data3.size]) - - data1[2]=-1 - data2[:]=-1 - data3[::2]=-2 - assert all(data1==datac[:data1.size]) - assert all(data2==datac[n1:n1+data2.size]) - assert all(data3==datac[n1+n2:n1+n2+data3.size]) - assert all(data1==datav[:data1.size]) - assert all(data2==datav[n1:n1+data2.size]) - assert all(data3==datav[n1+n2:n1+n2+data3.size]) - - inputs[1].taint() - assert concat.tainted==True - assert view.tainted==True - - view.touch() - savegraph(graph, "output/test_ViewConcat_00.png") - -def test_ViewConcat_01(): - with Graph() as graph: - concat = ViewConcat("concat") - concat2 = ViewConcat("concat 2") - view = View('view') - normnode = NormalizeCorrelatedVars2('normvars') - - with raises(ConnectionError): - view >> concat - - with raises(ConnectionError): - normnode.outputs[0] >> concat - - with raises(ConnectionError): - concat >> normnode.inputs[0] - - with raises(ConnectionError): - concat >> concat2 - - savegraph(graph, "output/test_ViewConcat_01.png") diff --git a/subtrees/dagflow/test/test_class.py b/subtrees/dagflow/test/test_class.py deleted file mode 100755 index 041591dba0fe942c362eaf1ec40e0d91ab105936..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_class.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - - -from numpy import arange - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.lib.Array import Array -from dagflow.lib.Product import Product -from dagflow.lib.Sum import Sum -from dagflow.printl import current_level, printl, set_prefix_function -from dagflow.wrappers import * - -set_prefix_function(lambda: "{:<2d} ".format(current_level())) -debug = False - - -def test_00(): - """Create four nodes: sum up three of them, multiply the result by the fourth - Use graph context to create the graph. - Use one-line code for connecting the nodes - """ - array = arange(5) - names = "n1", "n2", "n3", "n4" - with Graph(debug=debug) as graph: - initials = [Array(name, array) for name in names] - s = Sum("add") - m = Product("mul") - - (initials[3], (initials[:3] >> s)) >> m - - graph._wrap_fcns(dataprinter, printer) - graph.close() - - s.print() - m.print() - - result = m.outputs["result"].data - printl(result) - - savegraph(graph, "output/class_00.pdf") diff --git a/subtrees/dagflow/test/test_close_open.py b/subtrees/dagflow/test/test_close_open.py deleted file mode 100644 index dcb936ff95f05659d6ee8370e06426fdc11d78f0..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_close_open.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.lib import Array, Product, Sum, WeightedSum -from numpy import arange, array - - -def test_00(debug_graph): - with Graph(debug=debug_graph, close=True): - arr = Array("arr", arange(3, dtype="d")) # [0, 1, 2] - ws = WeightedSum("weightedsum") - (arr, arr) >> ws - Array("weight", (2, 3)) >> ws("weight") - assert ws.closed - assert (ws.outputs["result"].data == [0, 5, 10]).all() - assert arr.open() - assert not ws.inputs["weight"].closed - assert not arr.closed - - -def test_01(debug_graph): - with Graph(debug=debug_graph, close=True): - arr1 = Array("arr1", arange(3, dtype="d")) # [0, 1, 2] - arr2 = Array("arr2", array((3, 2, 1), dtype="d")) - sum = Sum("sum") - (arr1, arr2) >> sum - assert sum.closed - assert (sum.outputs["result"].data == [3, 3, 3]).all() - assert sum.open() - assert all((not sum.closed, arr1.closed, arr2.closed)) - assert arr1.open() - assert all((not sum.closed, not arr1.closed, arr2.closed)) - assert arr2.open() - assert all((not sum.closed, not arr1.closed, not arr2.closed)) - - -def test_02(debug_graph): - with Graph(debug=debug_graph, close=True): - arr1 = Array("arr1", arange(3, dtype="d")) # [0, 1, 2] - arr2 = Array("arr2", array((3, 2, 1), dtype="d")) - arr3 = Array("unity", array((1, 1, 1), dtype="d")) - sum1 = Sum("sum1") - sum2 = Sum("sum2") - prod = Product("product") - (arr1, arr2, arr3) >> sum1 # [4, 4, 4] - (arr3, sum1) >> prod # [4, 4, 4] - (arr1, prod) >> sum2 # [4, 5, 6] - assert sum2.closed - assert (sum2.outputs["result"].data == [4, 5, 6]).all() - assert arr1.open() - assert arr2.closed - assert arr3.closed - assert not arr1.closed - assert not prod.closed - assert not sum1.closed - - -def test_03(debug_graph): - with Graph(debug=debug_graph, close=False): - arr1 = Array("arr1", arange(3, dtype="d")) # [0, 1, 2] - arr2 = Array("arr2", array((3, 2, 1), dtype="d")) - arr3 = Array("unity", array((1, 1, 1), dtype="d")) - sum1 = Sum("sum1") - sum2 = Sum("sum2") - prod = Product("product") - (arr1, arr2, arr3) >> sum1 # [4, 4, 4] - (arr3, sum1) >> prod # [4, 4, 4] - (arr1, prod) >> sum2 # [4, 5, 6] - - with Graph(debug=debug_graph, close=True): - arr4 = Array("arr1", arange(3, dtype="d")) # [0, 1, 2] - sum3 = Sum("sum3") - (sum2, arr4) >> sum3 # [4, 7, 8] - assert arr1.closed - assert arr2.closed - assert arr3.closed - assert arr4.closed - assert sum2.closed - assert sum3.closed - assert (sum3.outputs["result"].data == [4, 6, 8]).all() - assert arr1.open() - assert arr2.closed - assert arr3.closed - assert arr4.closed - assert not arr1.closed - assert not prod.closed - assert not sum1.closed - assert not sum2.closed diff --git a/subtrees/dagflow/test/test_connection.py b/subtrees/dagflow/test/test_connection.py deleted file mode 100755 index d23dfb49b91776e67f9c4fafa859727cf79ac39c..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_connection.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python - -from dagflow.exception import ClosedGraphError, UnclosedGraphError -from dagflow.graph import Graph -from dagflow.input import Input -from dagflow.nodes import FunctionNode -from dagflow.output import Output -from dagflow.wrappers import * -from pytest import raises - -nodeargs = {"typefunc": lambda: True} - - -def test_01(): - i = Input("input", None) - o = Output("output", None) - - o >> i - - -def test_02(): - n1 = FunctionNode("node1") - n2 = FunctionNode("node2") - - n1.add_output("o1") - n1.add_output("o2") - - n2.add_input("i1") - n2.add_input("i2") - n2.add_output("o1") - - n1 >> n2 - - -def test_03(): - n1 = FunctionNode("node1") - n2 = FunctionNode("node2") - - out = n1.add_output("o1") - - n2.add_input("i1") - n2.add_output("o1") - - out >> n2 - - -def test_04(): - n1 = FunctionNode("node1") - n2 = FunctionNode("node2") - - out = n1.add_output("o1") - - n2.add_pair("i1", "o1") - - final = out >> n2 - - -def test_05(): - n1 = FunctionNode("node1", **nodeargs) - n2 = FunctionNode("node2", **nodeargs) - - out1 = n1.add_output("o1", allocatable=False) - out2 = n1.add_output("o2", allocatable=False) - - _, final = n2.add_pair("i1", "o1", output_kws={"allocatable": False}) - n2.add_input("i2") - - (out1, out2) >> n2 - - n2.close() - assert n2.closed - assert n1.closed - with raises(ClosedGraphError): - n2.add_input("i3") - with raises(ClosedGraphError): - n1.add_output("o3") - final.data - - -def test_06(): - n1 = FunctionNode("node1", **nodeargs) - n2 = FunctionNode("node2", **nodeargs) - - out1 = n1._add_output("o1", allocatable=False) - out2 = n1._add_output("o2", allocatable=False) - - _, final = n2._add_pair("i1", "o1", output_kws={"allocatable": False}) - n2._add_input("i2") - - (out1, out2) >> n2 - - n1.close(recursive=False) - assert n1.closed - assert not n2.closed - n2.close(recursive=False) - assert n2.closed - with raises(ClosedGraphError): - n2.add_input("i3") - with raises(ClosedGraphError): - n1.add_output("o3") - final.data - - -def test_07(): - g = Graph() - n1 = g.add_node("node1", **nodeargs) - n2 = g.add_node("node2", **nodeargs) - g._wrap_fcns(toucher, printer) - - out1 = n1._add_output("o1", allocatable=False) - out2 = n1._add_output("o2", allocatable=False) - - _, final = n2._add_pair("i1", "o1", output_kws={"allocatable": False}) - n2._add_input("i2") - - (out1, out2) >> n2 - - with raises(UnclosedGraphError): - final.data - g.close() - with raises(ClosedGraphError): - n2.add_input("i3") - with raises(ClosedGraphError): - n1.add_output("o3") - final.data - - -def test_08(): - g = Graph() - n1 = g.add_node("node1", **nodeargs) - n2 = g.add_node("node2", **nodeargs) - n3 = g.add_node("node3", **nodeargs) - g._wrap_fcns(toucher, printer) - - out1 = n1._add_output("o1", allocatable=False) - out2 = n1._add_output("o2", allocatable=False) - - _, out3 = n2._add_pair("i1", "o1", output_kws={"allocatable": False}) - n2._add_input("i2") - - _, final = n3._add_pair("i1", "o1", output_kws={"allocatable": False}) - - (out1, out2) >> n2 - out3 >> n3 - - with raises(UnclosedGraphError): - final.data - g.close() - with raises(ClosedGraphError): - n2.add_input("i3") - with raises(ClosedGraphError): - n1.add_output("o3") - with raises(ClosedGraphError): - n3.add_pair("i3", "o3") - final.data - - print() - final.data - - print("Taint n2") - n2.taint() - final.data - - print("Taint n3") - n3.taint() - final.data diff --git a/subtrees/dagflow/test/test_containers.py b/subtrees/dagflow/test/test_containers.py deleted file mode 100755 index 1e19a94a0bafd98c6d983f251266c49c22475585..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_containers.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python - -import contextlib - -from dagflow.input import Input, Inputs -from dagflow.legs import Legs -from dagflow.output import Output - - -def test_01(): - inputs = Inputs() - - input1 = Input("i1", None) - input2 = Input("i2", None) - input3 = Input("i3", None) - - inputs.add( (input1, input2) ) - inputs.add( input3 ) - - print(inputs) - - print(inputs[0]) - print(inputs[1]) - print(inputs[2]) - - try: - print(inputs[3]) - except IndexError: - pass - else: - raise RuntimeError("fail") - - print(inputs["i1"]) - print(inputs["i2"]) - print(inputs[("i1", "i3")]) - - print(inputs["i1"]) - print(inputs["i2"]) - print(inputs["i3"]) - with contextlib.suppress(KeyError): - print(inputs["i4"]) - - -def test_02(): - inputs = Inputs() - print(inputs) - - output1 = Output("o1", None) - - try: - inputs.add( output1 ) - except Exception: - pass - else: - raise RuntimeError("fail") - - -def test_03(): - print("test3") - input1 = Input("i1", None) - input2 = Input("i2", None) - input3 = Input("i3", None) - - output1 = Output("o1", None) - output2 = Output("o2", None) - - legs = Legs((input1, input2, input3), (output1, output2)) - print(legs) - legs.print() - print() - - legs1 = legs[None, "o1"] - print(legs1) - # legs1.print() - print() - - legs2 = legs[:, "o1"] - print(legs2) - legs2.print() - print() - - legs3 = legs[("i1", "i3"), "o1"] - print(legs3) - legs3.print() - print() diff --git a/subtrees/dagflow/test/test_function_switch.py b/subtrees/dagflow/test/test_function_switch.py deleted file mode 100644 index 2d700799e736fbcfeb99c77bec9a172375902dfa..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_function_switch.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python - -from dagflow.graph import Graph -from dagflow.input_extra import MissingInputAddOne -from dagflow.lib import Array -from dagflow.nodes import FunctionNode -from numpy import arange, array, copyto, result_type - - -class SumIntProductFloatElseNothing(FunctionNode): - def __init__(self, name, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(name, **kwargs) - self._functions.update( - {"int": self._fcn_int, "float": self._fcn_float} - ) - - def _fcn(self, _, inputs, outputs): - return outputs[0].data - - def _fcn_int(self, _, inputs, outputs): - out = outputs[0].data - copyto(out, inputs[0].data.copy()) - if len(inputs) > 1: - for input in inputs[1:]: - out += input.data - return out - - def _fcn_float(self, _, inputs, outputs): - out = outputs[0].data - copyto(out, inputs[0].data.copy()) - if len(inputs) > 1: - for input in inputs[1:]: - out *= input.data - return out - - def _typefunc(self) -> bool: - if self.inputs[0].dd.dtype == "i": - self.fcn = self._functions.get("int") - elif self.inputs[0].dd.dtype == "d": - self.fcn = self._functions.get("float") - self.outputs["result"].dd.shape = self.inputs[0].dd.shape - self.outputs["result"].dd.dtype = result_type( - *tuple(inp.dd.dtype for inp in self.inputs) - ) - self.logger.debug( - f"Node '{self.name}': dtype={self.outputs['result'].dd.dtype}, " - f"shape={self.outputs['result'].dd.shape}, function={self.fcn.__name__}" - ) - return True - - -def test_00(debug_graph): - with Graph(debug=debug_graph, close=True): - arr = Array("arr", array(("1", "2", "3"))) - node = SumIntProductFloatElseNothing("node") - (arr, arr) >> node - assert (node.outputs["result"].data == ["", "", ""]).all() - - -def test_01(debug_graph): - with Graph(debug=debug_graph, close=True): - arr = Array("arr", arange(3, dtype="i")) # [0, 1, 2] - node = SumIntProductFloatElseNothing("node") - (arr, arr) >> node - assert (node.outputs["result"].data == [0, 2, 4]).all() - - -def test_02(debug_graph): - with Graph(debug=debug_graph, close=True): - arr = Array("arr", arange(3, dtype="d")) # [0, 1, 2] - node = SumIntProductFloatElseNothing("node") - (arr, arr) >> node - assert (node.outputs["result"].data == [0, 1, 4]).all() diff --git a/subtrees/dagflow/test/test_graph.py b/subtrees/dagflow/test/test_graph.py deleted file mode 100755 index eb070b2fde9fb230c6e78a37cd35fd20468ac5ed..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_graph.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -from dagflow.graph import Graph -from dagflow.graphviz import GraphDot -from dagflow.printl import current_level, set_prefix_function -from dagflow.wrappers import * - -set_prefix_function( - lambda: "{:<2d} ".format(current_level()), -) -nodeargs = dict(typefunc=lambda: True) - - -def test_01(): - """Simple test of the graph plotter""" - g = Graph() - n1 = g.add_node("node1", **nodeargs) - n2 = g.add_node("node2", **nodeargs) - n3 = g.add_node("node3", **nodeargs) - g._wrap_fcns(toucher, printer) - - out1 = n1._add_output("o1", allocatable=False) - out2 = n1._add_output("o2", allocatable=False) - - _, out3 = n2._add_pair("i1", "o1", output_kws={"allocatable": False}) - n2._add_input("i2") - n3._add_pair("i1", "o1", output_kws={"allocatable": False}) - - print(f"{out1=}, {out2=}") - (out1, out2) >> n2 - out3 >> n3 - g.close() - - d = GraphDot(g) - d.savegraph("output/test1_00.png") - - -def test_02(): - """Simple test of the graph plotter""" - g = Graph() - n1 = g.add_node("node1", **nodeargs) - n2 = g.add_node("node2", **nodeargs) - n3 = g.add_node("node3", **nodeargs) - g._wrap_fcns(toucher, printer) - - out1 = n1._add_output("o1", allocatable=False) - out2 = n1._add_output("o2", allocatable=False) - - _, out3 = n2._add_pair("i1", "o1", output_kws={"allocatable": False}) - n2._add_input("i2") - - _, final = n3._add_pair("i1", "o1", output_kws={"allocatable": False}) - - (out1, out2) >> n2 - out3 >> n3 - g.close() - - d = GraphDot(g) - d.savegraph("output/test2_00.png") - - final.data - d = GraphDot(g) - d.savegraph("output/test2_01.png") - - -def test_02a(): - """Simple test of the graph plotter""" - g = Graph() - n1 = g.add_node("node1", **nodeargs) - n2 = g.add_node("node2", **nodeargs) - n3 = g.add_node("node3", **nodeargs) - n4 = g.add_node("node4", **nodeargs) - g._wrap_fcns(toucher, printer) - - out1 = n1._add_output("o1", allocatable=False) - - in2, out2 = n2._add_pair("i1", "o1", output_kws={"allocatable": False}) - in3, out3 = n3._add_pair("i1", "o1", output_kws={"allocatable": False}) - in4, out4 = n4._add_pair("i1", "o1", output_kws={"allocatable": False}) - - out1.repeat() >> (in2, in3, in4) - g.close() - - d = GraphDot(g) - d.savegraph("output/test2a_00.png") - - print(out4.data) - d = GraphDot(g) - d.savegraph("output/test2a_01.png") diff --git a/subtrees/dagflow/test/test_graph_big.py b/subtrees/dagflow/test/test_graph_big.py deleted file mode 100755 index 71a71ef43a02d28270d00a0e80a06908bd0af77c..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_graph_big.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python -from dagflow.graph import Graph -from dagflow.graphviz import GraphDot -from dagflow.printl import current_level, set_prefix_function -from dagflow.wrappers import * - -set_prefix_function(lambda: "{:<2d} ".format(current_level())) - -counter = 0 -nodeargs = dict(typefunc=lambda: True) - - -def test_graph_big_01(): - """Create a graph of nodes and test evaluation features""" - g = Graph() - label = None - - def plot(suffix=""): - global counter - d = GraphDot(g) - newlabel = label and label + suffix or suffix - if newlabel is not None: - d.set_label(newlabel) - d.savegraph("output/test_graph_big_{:03d}.png".format(counter)) - counter += 1 - - def plotter(fcn, node, inputs, outputs): - plot(f"[start evaluating {node.name}]") - fcn(node, inputs, outputs) - plot(f"[done evaluating {node.name}]") - - A1 = g.add_node("A1", **nodeargs) - A2 = g.add_node("A2", auto_freeze=True, label="{name}|frozen", **nodeargs) - A3 = g.add_node("A3", immediate=True, label="{name}|immediate", **nodeargs) - B = g.add_node("B", **nodeargs) - C1 = g.add_node("C1", **nodeargs) - C2 = g.add_node("C2", **nodeargs) - D = g.add_node("D", **nodeargs) - E = g.add_node("E", **nodeargs) - F = g.add_node("F", **nodeargs) - H = g.add_node("H", **nodeargs) - P = g.add_node("P", immediate=True, label="{name}|immediate", **nodeargs) - - g._wrap_fcns(toucher, printer, plotter) - - A1._add_output("o1", allocatable=False) - A2._add_output("o1", allocatable=False) - P._add_output("o1", allocatable=False) - A3._add_pair("i1", "o1", output_kws={"allocatable": False}) - B._add_pair( - ("i1", "i2", "i3", "i4"), - ("o1", "o2"), - output_kws={"allocatable": False}, - ) - C1._add_output("o1", allocatable=False) - C2._add_output("o1", allocatable=False) - D._add_pair("i1", "o1", output_kws={"allocatable": False}) - D._add_pair("i2", "o2", output_kws={"allocatable": False}) - H._add_pair("i1", "o1", output_kws={"allocatable": False}) - _, other = F._add_pair("i1", "o1", output_kws={"allocatable": False}) - _, final = E._add_pair("i1", "o1", output_kws={"allocatable": False}) - - (A1, A2, (P >> A3), D[:1]) >> B >> (E, H) - ((C1, C2) >> D[:, 1]) >> F - - g.print() - g.close() - - label = "Initial graph state." - plot() - - label = "Read E..." - plot() - plot() - plot() - final.data - label = "Done reading E." - plot() - - label = "Taint D." - plot() - plot() - plot() - D.taint() - plot() - label = "Read F..." - other.data - label = "Done reading F." - plot() - - label = "Read E..." - plot() - plot() - plot() - final.data - label = "Done reading E." - plot() - - label = "Taint A2." - plot() - plot() - plot() - A2.taint() - plot() - label = "Read E..." - plot() - final.data - label = "Done reading E." - plot() - - label = "Unfreeze A2 (tainted)." - plot() - plot() - plot() - A2.unfreeze() - plot() - label = "Read E..." - plot() - final.data - label = "Done reading E." - plot() - - label = "Unfreeze A2 (not tainted)." - plot() - plot() - plot() - A2.unfreeze() - plot() - label = "Read E..." - plot() - final.data - label = "Done reading E." - plot() - - label = "Taint P" - plot() - plot() - plot() - P.taint() - plot() - label = "Read E..." - plot() - final.data - label = "Done reading E." - plot() - - label = "Invalidate P" - plot() - plot() - plot() - P.invalid = True - plot() - - label = "Validate P" - plot() - plot() - plot() - P.invalid = False - plot() - label = "Read E..." - plot() - final.data - label = "Done reading E." - plot() diff --git a/subtrees/dagflow/test/test_hooks.py b/subtrees/dagflow/test/test_hooks.py deleted file mode 100755 index 5678da9ecfb42add4173f9db008b85f064b5c378..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_hooks.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python - -from numpy import arange, copyto, result_type -from pytest import raises - -from dagflow.exception import ( - CriticalError, - ReconnectionError, - UnclosedGraphError, -) -from dagflow.graph import Graph -from dagflow.input_extra import MissingInputAddOne -from dagflow.lib.Array import Array -from dagflow.lib.WeightedSum import WeightedSum -from dagflow.nodes import FunctionNode - - -class ThreeInputsSum(FunctionNode): - def __init__(self, *args, **kwargs): - kwargs.setdefault( - "missing_input_handler", MissingInputAddOne(output_fmt="result") - ) - super().__init__(*args, **kwargs) - - def _fcn(self, _, inputs, outputs): - out = outputs["result"].data - copyto(out, inputs[0].data.copy()) - for input in inputs[1:3]: - out += input.data - return out - - def _typefunc(self) -> None: - """A output takes this function to determine the dtype and shape""" - if (y := len(self.inputs)) != 3: - raise CriticalError( - f"The node must have only 3 inputs, but given {y}: {self.inputs}!" - ) - self.outputs["result"].dd.shape = self.inputs[0].dd.shape - self.outputs["result"].dd.dtype = result_type( - *tuple(inp.dd.dtype for inp in self.inputs) - ) - self.logger.debug( - f"Node '{self.name}': dtype={self.outputs['result'].dd.dtype}, " - f"shape={self.outputs['result'].dd.shape}" - ) - - -def test_00(debug_graph): - with Graph(debug=debug_graph, close=True): - arr = Array("arr", arange(3, dtype="i")) # [0, 1, 2] - node = ThreeInputsSum("threesum") - for _ in range(3): - # Error while evaluating before len(inputs) == 3 - with raises(UnclosedGraphError): - node.eval() - arr >> node - assert (node.outputs["result"].data == [0, 3, 6]).all() - - -def test_01(debug_graph): - with Graph(debug=debug_graph, close=True): - arr = Array("arr", arange(3, dtype="i")) # [0, 1, 2] - ws = WeightedSum("weightedsum") - (arr, arr) >> ws - # Error while eval before setting the weight input - with raises(UnclosedGraphError): - ws.eval() - # multiply the first input by 2 and the second one by 3 - Array("weight", (2, 3)) >> ws("weight") - with raises(ReconnectionError): - Array("weight", (2, 3)) >> ws("weight") - assert (ws.outputs["result"].data == [0, 5, 10]).all() diff --git a/subtrees/dagflow/test/test_input_handler.py b/subtrees/dagflow/test/test_input_handler.py deleted file mode 100755 index d8a763aa96e2e858fcb7ddea637861dc027c8b31..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/test_input_handler.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python -"""Test missing input handlers""" - -from contextlib import suppress - -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.input_extra import * -from dagflow.wrappers import * - -nodeargs = dict(typefunc=lambda: True) - - -def test_00(): - """Test default handler: fail on connect""" - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", missing_input_handler=MissingInputFail, **nodeargs - ) - graph.close() - - with suppress(Exception): - (in1, in2, in3) >> s - savegraph( - graph, "output/missing_input_handler_00.pdf", label="Fail on connect" - ) - - -def test_01(): - """Test InputAdd handler: add new input on each new connect""" - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", - missing_input_handler=MissingInputAdd( - output_kws={"allocatable": False} - ), - **nodeargs - ) - - (in1, in2, in3) >> s - in4 >> s - - print() - print("test 01") - s.print() - graph.close() - - savegraph( - graph, "output/missing_input_handler_01.pdf", label="Add only inputs" - ) - - -def test_02(): - """ - Test InputAddPair handler: add new input on each new connect - and connect them as inputs to another input - """ - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", - missing_input_handler=MissingInputAddPair( - output_kws={"allocatable": False} - ), - **nodeargs - ) - - (in1, in2, in3) >> s - in4 >> s - - print() - print("test 02") - s.print() - - for input, output in zip(s.inputs, s.outputs): - assert input.child_output is output - graph.close() - - savegraph( - graph, - "output/missing_input_handler_02.pdf", - label="Add inputs and an output for each input", - ) - - -def test_03(): - """ - Test InputAddOne handler: add new input on each new connect and - add an output if needed - """ - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", - missing_input_handler=MissingInputAddOne( - output_kws={"allocatable": False} - ), - **nodeargs - ) - - (in1, in2, in3) >> s - in4 >> s - - print() - print("test 03") - s.print() - graph.close() - - savegraph( - graph, - "output/missing_input_handler_03.pdf", - label="Add only inputs and only one output", - ) - - -def test_04(): - """ - Test InputAddOne handler: add new input on each new connect and - add an output if needed. - This version also sets the input for each input - """ - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", - missing_input_handler=MissingInputAddOne( - add_child_output=True, output_kws={"allocatable": False} - ), - **nodeargs - ) - - (in1, in2, in3) >> s - in4 >> s - - print() - print("test 04") - s.print() - - output = s.outputs[0] - for input in s.inputs: - assert input.child_output is output - graph.close() - - savegraph( - graph, - "output/missing_input_handler_04.pdf", - label="Add inputs and only one output", - ) - - -def test_05(): - """ - Test InputAddEach handler: add new input on each new connect and - add an output for each >> group - """ - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", - missing_input_handler=MissingInputAddEach( - add_child_output=False, output_kws={"allocatable": False} - ), - **nodeargs - ) - - (in1, in2, in3) >> s - in4 >> s - - print() - print("test 05") - s.print() - graph.close() - - savegraph( - graph, - "output/missing_input_handler_05.pdf", - label="Add inputs and an output for each block", - ) - - -def test_06(): - """ - Test InputAddEach handler: add new input on each new connect and - add an output for each >> group. - This version also sets the child_output for each input - """ - graph = Graph() - - in1 = graph.add_node("n1", **nodeargs) - in2 = graph.add_node("n2", **nodeargs) - in3 = graph.add_node("n3", **nodeargs) - in4 = graph.add_node("n4", **nodeargs) - for node in (in1, in2, in3, in4): - node.add_output("o1", allocatable=False) - - s = graph.add_node( - "add", - missing_input_handler=MissingInputAddEach( - add_child_output=True, output_kws={"allocatable": False} - ), - **nodeargs - ) - - (in1, in2, in3) >> s - in4 >> s - - print() - print("test 06") - s.print() - - o1, o2 = s.outputs - for input in s.inputs[:3]: - assert input.child_output is o1 - for input in s.inputs[3:]: - assert input.child_output is o2 - graph.close() - - savegraph( - graph, - "output/missing_input_handler_06.pdf", - label="Add inputs and an output for each block", - ) diff --git a/subtrees/dagflow/test/variables/test_load_variables.py b/subtrees/dagflow/test/variables/test_load_variables.py deleted file mode 100644 index fb84ef918336076614c8e1ea96af75fc581184f9..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/variables/test_load_variables.py +++ /dev/null @@ -1,104 +0,0 @@ -from dagflow.graphviz import savegraph -from dagflow.graph import Graph -from dagflow.bundles.load_variables import load_variables - -cfg1 = { - 'variables': { - 'var1': 1.0, - 'var2': 1.0, - 'sub1': { - 'var3': 2.0 - } - }, - 'format': 'value', - 'labels': { - 'var1': { - 'text': 'text label 1', - 'latex': r'\LaTeX label 1', - 'name': 'v1-1' - }, - 'var2': 'simple label 2', - }, - } - -cfg2 = { - 'variables': { - 'var1': (1.0, 1.0, 0.1), - 'var2': (1.0, 2.0, 0.1), - 'sub1': { - 'var3': (2.0, 1.0, 0.1) - } - }, - 'format': ('value', 'central', 'sigma_absolute'), - 'labels': { - 'var1': { - 'text': 'text label 1', - 'latex': r'\LaTeX label 1', - 'name': 'v1-2' - }, - 'var2': 'simple label 2' - }, - } - -cfg3 = { - 'variables': { - 'var1': (1.0, 1.0, 0.1), - 'var2': (1.0, 2.0, 0.1), - 'sub1': { - 'var3': (2.0, 3.0, 0.1) - } - }, - 'labels': { - 'var1': { - 'text': 'text label 1', - 'latex': r'\LaTeX label 1', - 'name': 'v1-3' - }, - 'var2': 'simple label 2' - }, - 'format': ('value', 'central', 'sigma_relative') - } - -cfg4 = { - 'variables': { - 'var1': (1.0, 1.0, 10), - 'var2': (1.0, 2.0, 10), - 'sub1': { - 'var3': (2.0, 3.0, 10) - } - }, - 'labels': { - 'var1': { - 'text': 'text label 1', - 'latex': r'\LaTeX label 1', - }, - 'var2': 'simple label 2' - }, - 'format': ('value', 'central', 'sigma_percent') - } - -cfg5 = { - 'variables': { - 'var1': (1.0, 10), - 'var2': (2.0, 10), - 'sub1': { - 'var3': (3.0, 10) - } - }, - 'labels': { - 'var1': { - 'text': 'text label 1', - 'latex': r'\LaTeX label 1', - }, - 'var2': 'simple label 2' - }, - 'format': ('central', 'sigma_percent') - } - -def test_load_variables_v01(): - cfgs = (cfg1, cfg2, cfg3, cfg4, cfg5) - with Graph(close=True) as g: - for cfg in cfgs: - load_variables(cfg) - - savegraph(g, 'output/test_load_variables.pdf', show='all') diff --git a/subtrees/dagflow/test/variables/test_variables.py b/subtrees/dagflow/test/variables/test_variables.py deleted file mode 100644 index 149ac3c566d33f1d2c96dc5bd6c86181f822035f..0000000000000000000000000000000000000000 --- a/subtrees/dagflow/test/variables/test_variables.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -from dagflow.lib import Array -from dagflow.variable import GaussianParameters -from dagflow.graph import Graph -from dagflow.graphviz import savegraph -from dagflow.exception import CriticalError - -from numpy import square, allclose -import pytest - -@pytest.mark.parametrize('mode', ('single', 'uncorr', 'cov', 'cov1d')) -def test_variables_00_variable(mode) -> None: - value_in = [1.1, 1.8, 5.0] - central_in = [1.0, 2.0, 3.0] - sigma_in = [1.0, 0.5, 2.0] - corrs_in = [-0.1, 0.5, -0.9] # 01, 02, 12 - variance_in = square(sigma_in) - zeros_in = [0.0, 0.0, 0.0] - - if mode=='single': - value_in = value_in[:1] - central_in = central_in[:1] - sigma_in = sigma_in[:1] - zeros_in = zeros_in[:1] - - with Graph(debug=False, close=False) as graph: - value = Array("variable", value_in, mode='store_weak', mark='v') - central = Array("central", central_in, mark='v鈧€') - - if mode in ('single', 'uncorr', 'cor'): - sigma = Array("sigma", sigma_in, mark='蟽') - - if mode in ('single', 'uncorr'): - gp = GaussianParameters(value, central, sigma=sigma) - elif mode=='cov': - covariance = Array("covariance", [ - [variance_in[0], corrs_in[0]*sigma_in[0]*sigma_in[1], corrs_in[1]*sigma_in[0]*sigma_in[2]], - [corrs_in[0]*sigma_in[0]*sigma_in[1], variance_in[1], corrs_in[2]*sigma_in[1]*sigma_in[2]], - [corrs_in[1]*sigma_in[0]*sigma_in[2], corrs_in[2]*sigma_in[1]*sigma_in[2], variance_in[2]] - ], - mark='V') - gp = GaussianParameters(value, central, covariance=covariance) - elif mode=='cov1d': - covariance = Array("covariance", variance_in, mark='diag(V)') - gp = GaussianParameters(value, central, covariance=covariance) - elif mode=='cor': - correlation = Array("correlation", [ - [1.0, corrs_in[0], corrs_in[1]], - [corrs_in[0], 1.0, corrs_in[2]], - [corrs_in[1], corrs_in[2], 1.0], - ], mark='C') - gp = GaussianParameters(value, central, sigma=sigma, correlation=correlation) - else: - raise RuntimeError(f"Invalid mode {mode}") - - try: - graph.close() - except CriticalError as error: - savegraph(graph, f"output/test_variables_00_{mode}.png") - raise error - - value_out0 = gp.value.data.copy() - normvalue_out0 = gp.normvalue.data - assert allclose(value_in, value_out0, atol=0, rtol=0) - assert all(normvalue_out0!=0) - - gp.normvalue.set(zeros_in) - value_out1 = gp.value.data - normvalue_out1 = gp.normvalue.data - assert allclose(central_in, value_out1, atol=0, rtol=0) - assert allclose(normvalue_out1, 0.0, atol=0, rtol=0) - - gp.value.set(value_out0) - value_out2 = gp.value.data - normvalue_out2 = gp.normvalue.data - assert allclose(value_in, value_out2, atol=0, rtol=0) - assert allclose(normvalue_out2, normvalue_out0, atol=0, rtol=0) - - savegraph(graph, f"output/test_variables_00_{mode}.png", show=['all']) - savegraph(graph, f"output/test_variables_00_{mode}.pdf", show=['all']) -