Module ldict.core.rshift

Expand source code
#  Copyright (c) 2021. Davi Pereira dos Santos
#  This file is part of the ldict project.
#  Please respect the license - more about this in the section (*) below.
#
#  ldict is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  ldict is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with ldict.  If not, see <http://www.gnu.org/licenses/>.
#
#  (*) Removing authorship by any means, e.g. by distribution of derived
#  works or verbatim, obfuscated, compiled or rewritten versions of any
#  part of this work is illegal and unethical regarding the effort and
#  time spent here.
#
#  ldict is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  ldict is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with ldict.  If not, see <http://www.gnu.org/licenses/>.
#
#  (*) Removing authorship by any means, e.g. by distribution of derived
#  works or verbatim, obfuscated, compiled or rewritten versions of any
#  part of this work is illegal and unethical regarding the effort and
#  time spent here.
from inspect import signature
from types import FunctionType
from typing import Union

from lange import AP, GP

from ldict.core.inspection import (
    extract_dynamic_input,
    extract_output,
    extract_body,
    CodeExtractionException,
)
from ldict.core.inspection import extract_input
from ldict.exception import InconsistentLange, UndefinedSeed, DependenceException
from ldict.lazyval import LazyVal
from ldict.parameter.let import AbstractLet


def handle_dict(data, dictlike, rnd):
    """
    >>> from ldict import ldict
    >>> d = ldict(x=5, y=7, z=8)
    >>> di = handle_dict(d.frozen.data, {"y":None}, None)
    >>> di
    {'x': 5, 'z': 8}
    >>> handle_dict(di, {"w":lambda x,z: x**z}, None)
    {'x': 5, 'z': 8, 'w': →(x z)}
    """
    data = data.copy()
    for k, v in dictlike.items():
        if v is None:
            del data[k]
        else:
            from ldict.core.ldict_ import Ldict

            if callable(v):
                if (r := lazify(data, k, v, rnd, is_multi_output=False)) is not None:
                    data[k] = r
            elif isinstance(v, Ldict):
                data[k] = v.frozen
            else:
                data[k] = v
    return data


def lazify(data, output_field: Union[list, str], f, rnd, is_multi_output) -> Union[dict, LazyVal]:
    """Create lazy values and handle metafields.
    >>> from ldict import ldict, let
    >>> from random import Random
    >>> (d := ldict(x=5) >> Random(0) >> (lambda x, a=1, b=[1, 2, 3, ... , 8]: {"y": a*x + b, "_parameters": ...}))
    {
        "x": 5,
        "y": "→(a b x)",
        "_parameters": {
            "a": 1,
            "b": 7
        }
    }
    >>> d.y
    12
    >>> d = ldict(x=5) >> Random(0) >> let(
    ...     (lambda x, a=1, b=[1, 2, 3, ... , 8]: {"y": a*x + b, "_parameters": ...}),
    ...     a=3, b=5
    ... )
    >>> d
    {
        "x": 5,
        "y": "→(a b x)",
        "_parameters": {
            "a": 3,
            "b": 5
        }
    }
    >>> d.y
    20
    >>> def f(input="a", output="b", **kwargs):
    ...     return {output: kwargs[input], "_history": ...}
    >>> f.metadata = {"id": "fffffff----------------------------fffff",
    ...             "name": "f",
    ...             "description": "Copy.",
    ...             "code": ...,
    ...             "parameters": ...}
    >>> d = ldict(a=5) >> let(f, output="b")
    >>> d.b
    5
    >>> d
    {
        "a": 5,
        "b": 5,
        "_history": {
            "fffffff----------------------------fffff": {
                "name": "f",
                "description": "Copy.",
                "code": "def f(input='a', output='b', **kwargs):\\nreturn {output: kwargs[input], '_history': ...}",
                "parameters": {
                    "input": "a",
                    "output": "b"
                }
            }
        }
    }
    >>> def f(input=["a", "b"], output=None, **kwargs):
    ...     return {output: kwargs[input[0]] * kwargs[input[1]]}
    >>> d = ldict(a=5, b=7) >> let(f, output="c")
    >>> d.c
    35
    >>> d
    {
        "a": 5,
        "b": 7,
        "c": 35
    }
    """

    """
    >>> def f(input=["a", "b"], output=None, **kwargs):
    ...     return {kwargs[output[0]]: kwargs[input[0]], kwargs[output[1]]: kwargs[input[1]]}
    >>> d = ldict(a=5, b=7) >> let(f, output=["c", "d"])
    >>> d.c
    35
    >>> d
    {
        "a": 5,
        "b": 7,
        "c": 35
    }
    """
    # TODO (minor): simplify to improve readability of this function
    config, f = (f.config, f.f) if isinstance(f, AbstractLet) else ({}, f)
    input_fields, parameters, optional = extract_input(f)
    noop = False
    if "_" in input_fields:
        noop = True
        del input_fields["_"]
        if isinstance(f, AbstractLet):  # pragma: no cover
            raise Exception("Cannot let parameters have values for a noop function")
    for k, v in config.items():
        parameters[k] = v
    if isinstance(f, FunctionType):
        try:
            body = extract_body(f)
        except CodeExtractionException as e:
            body = f"<{e}>"
        dynamic_input = extract_dynamic_input("".join(body))
    else:
        body = None
        if not (hasattr(f, "metadata") and "input" in f.metadata and "output" in f.metadata):  # pragma: no cover
            raise Exception(f"Missing 'metadata' containing 'input' and 'output' keys for custom callable '{type(f)}'")
        dynamic_input = []
    dynamic_output = []
    if hasattr(f, "metadata"):
        if not dynamic_input and "input" in f.metadata and "dynamic" in f.metadata["input"]:
            dynamic_input = f.metadata["input"]["dynamic"]
        if "output" in f.metadata and "dynamic" in f.metadata["output"]:
            dynamic_output = f.metadata["output"]["dynamic"]

    # Process dynamic_input.
    multidynamicinput = set()
    for par in dynamic_input:
        if par not in parameters:  # pragma: no cover
            raise Exception(f"Parameter '{par}' value is not available:", parameters)
        if parameters[par] == "[]":
            parameters[par] = []
        if isinstance(parameters[par], list):
            for k in parameters[par]:
                input_fields[k] = None
            multidynamicinput.add(par)
        elif isinstance(parameters[par], dict):
            for k in parameters[par]:
                input_fields[k] = None
            multidynamicinput.add(par)
        else:
            input_fields[parameters[par]] = None

    dynio = multidynamicinput | set(dynamic_output)
    deps = prepare_deps(data, input_fields, parameters, rnd, dynio, optional)
    for k, v in parameters.items():
        parameters[k] = deps[k]
    if noop:

        def la(**deps_out):
            f(**deps_out)
            return deps_out

        lazies = []
        # REMINDER: noop uses input fields as output
        dic = {k: LazyVal(k, la, deps, data, lazies) for k in input_fields}
        lazies.extend(dic.values())
        deps["_"] = None
        return dic

    newidx = 0
    if hasattr(f, "metadata"):
        step = f.metadata.copy()
        if "id" in step:
            newidx = step.pop("id")
            if "_" in newidx:  # pragma: no cover
                raise Exception(f"'id' cannot have '_': {newidx}")
        for k in ["input", "output", "function"]:
            if k in step:
                del step[k]
        if "code" in f.metadata and f.metadata["code"] is ...:
            if body is None:  # pragma: no cover
                raise Exception(f"Cannot autofill 'metadata.code' for custom callable '{type(f)}'")
            head = f"def f{str(signature(f))}:"
            code = head + "\n" + "\n".join(body)
            f.metadata["code"] = code
            step["code"] = code
        if "parameters" in f.metadata and f.metadata["parameters"] is ...:
            step["parameters"] = parameters
        if "function" in f.metadata and f.metadata["function"] is ...:
            # REMINDER: it is not clear yet whether somebody wants this...
            if hasattr(f, "pickle_dump"):
                f.metadata["function"] = f.pickle_dump
            else:
                import dill

                dump = dill.dumps(f, protocol=5)
                f.metadata["function"] = dump
                f.pickle_dump = dump  # Memoize
    else:
        step = {}
    if output_field == "extract":
        explicit, meta, meta_ellipsed = extract_output(f, body, deps, is_multi_output, dynamic_output)
        lazies = []
        dic = {k: LazyVal(k, f, deps, data, lazies) for k in explicit + meta}
        lazies.extend(dic.values())
        for metaf in meta_ellipsed:
            if metaf == "_code":
                if hasattr(f, "metadata") and "code" in f.metadata:
                    dic["_code"] = f.metadata["code"]
                else:
                    if body is None:  # pragma: no cover
                        raise Exception(f"Missing 'metadata' containing 'code' key for custom callable '{type(f)}'")
                    head = f"def f{str(signature(f))}:"
                    dic["_code"] = head + "\n" + "\n".join(body)
            elif metaf == "_parameters":
                dic["_parameters"] = parameters
            elif metaf == "_function":
                # REMINDER: it even more unclear whether somebody wants this...
                if hasattr(f, "pickle_dump"):
                    dic["_function"] = f.pickle_dump
                else:
                    import dill

                    dump = dill.dumps(f, protocol=5)
                    dic["_function"] = dump
                    f.pickle_dump = dump  # Memoize
            elif metaf == "_history":
                if "_history" in data:
                    if isinstance(data["_history"], LazyVal):
                        data["_history"] = data["_history"]()
                    last = list(data["_history"].keys())[-1]
                    if isinstance(last, int):
                        newidx = last + 1
                    elif newidx == 0:
                        newidx = ...
                    dic["_history"] = data["_history"].copy()
                else:
                    dic["_history"] = {}
                dic["_history"][newidx] = step
            else:  # pragma: no cover
                raise Exception(f"'...' is not defined for '{metaf}'.")
        return dic
    else:
        return LazyVal(output_field, f, deps, data, None)


def prepare_deps(data, input, parameters, rnd, multi, optional):
    """Build a dict containing all needed dependencies (values) to apply a function:
        input fields and parameters.

    Parameter values are given in let() or sampled according to a range using a random number generator that provides a 'choice' method.
    A range is specified as the default value of the parameter in the function signature.
    """
    deps = {}
    for k, v in parameters.items():
        # TODO existence of multidynamic output is raising exception here
        #  because it will only be extracted at the end of lazify
        #  A workaround is to declare multidynamic output at f.metadata, instead of detecting it.
        if isinstance(v, list) and k not in multi and k not in optional:
            if rnd is None:
                raise UndefinedSeed(
                    "\nMissing Random object (or some object with the method 'choice') "
                    "before parameterized function application.\n"
                    "Example: ldict(x=5) >> Random(42) >> (lambda x, a=[1,2,3]: {'y': a * x**2})\n"
                    f"field={k}\n"
                    f"values={v}\n{parameters=}\n{multi=}\n"
                    f"Another possible cause: Multidynamic output cannot be detected by now, "
                    f"please declare multidynamic output at f.metadata['output']"
                )
            deps[k] = rnd.choice(expand(v))
        elif v is None:
            raise DependenceException(f"'None' value for parameter '{k}'.", deps.keys())
        else:
            deps[k] = v
    for k in input:
        if k in data:
            deps[k] = data[k]
        if k not in optional:
            if k not in deps:
                raise DependenceException(f"Missing field '{k}'.", data.keys())
            if deps[k] is None:
                raise DependenceException(f"'None' value for field '{k}'.", data.keys())
    return deps


def expand(lst):
    """Evaluate list representing A. or G. progression

    >>> expand([1,2,3,...,9])
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> expand([1,2,4,...,20])
    [1, 2, 4, 8, 16]

    Parameters
    ----------
    lst

    Returns
    -------

    """
    return list(list2progression(lst)) if Ellipsis in lst else lst


def list2progression(lst):
    """Convert list representing A. or G. progression to lange

    >>> list2progression([1,2,3,...,9])
    [1 2 .+. 9]
    >>> list2progression([1,2,4,...,16])
    [1 2 .*. 16]

    Parameters
    ----------
    lst

    Returns
    -------

    """
    # TODO move this to lange package
    try:
        diff1 = lst[1] - lst[0]
        diff2 = lst[2] - lst[1]
        ratio1 = lst[1] / lst[0]
        ratio2 = lst[2] / lst[1]
    except:
        raise InconsistentLange(f"Cannot identify whether this is a G. or A. progression: {lst}")
    newlst = [lst[0], lst[1], ..., lst[-1]]
    if diff1 == diff2:
        return AP(*newlst)
    elif ratio1 == ratio2:
        return GP(*newlst)
    else:
        raise InconsistentLange(f"Cannot identify whether this is a G. or A. progression: {lst}")

Functions

def expand(lst)

Evaluate list representing A. or G. progression

>>> expand([1,2,3,...,9])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> expand([1,2,4,...,20])
[1, 2, 4, 8, 16]

Parameters

lst
 

Returns

Expand source code
def expand(lst):
    """Evaluate list representing A. or G. progression

    >>> expand([1,2,3,...,9])
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> expand([1,2,4,...,20])
    [1, 2, 4, 8, 16]

    Parameters
    ----------
    lst

    Returns
    -------

    """
    return list(list2progression(lst)) if Ellipsis in lst else lst
def handle_dict(data, dictlike, rnd)
>>> from ldict import ldict
>>> d = ldict(x=5, y=7, z=8)
>>> di = handle_dict(d.frozen.data, {"y":None}, None)
>>> di
{'x': 5, 'z': 8}
>>> handle_dict(di, {"w":lambda x,z: x**z}, None)
{'x': 5, 'z': 8, 'w': →(x z)}
Expand source code
def handle_dict(data, dictlike, rnd):
    """
    >>> from ldict import ldict
    >>> d = ldict(x=5, y=7, z=8)
    >>> di = handle_dict(d.frozen.data, {"y":None}, None)
    >>> di
    {'x': 5, 'z': 8}
    >>> handle_dict(di, {"w":lambda x,z: x**z}, None)
    {'x': 5, 'z': 8, 'w': →(x z)}
    """
    data = data.copy()
    for k, v in dictlike.items():
        if v is None:
            del data[k]
        else:
            from ldict.core.ldict_ import Ldict

            if callable(v):
                if (r := lazify(data, k, v, rnd, is_multi_output=False)) is not None:
                    data[k] = r
            elif isinstance(v, Ldict):
                data[k] = v.frozen
            else:
                data[k] = v
    return data
def lazify(data, output_field: Union[list, str], f, rnd, is_multi_output) ‑> Union[dict, LazyVal]

Create lazy values and handle metafields.

>>> from ldict import ldict, let
>>> from random import Random
>>> (d := ldict(x=5) >> Random(0) >> (lambda x, a=1, b=[1, 2, 3, ... , 8]: {"y": a*x + b, "_parameters": ...}))
{
    "x": 5,
    "y": "→(a b x)",
    "_parameters": {
        "a": 1,
        "b": 7
    }
}
>>> d.y
12
>>> d = ldict(x=5) >> Random(0) >> let(
...     (lambda x, a=1, b=[1, 2, 3, ... , 8]: {"y": a*x + b, "_parameters": ...}),
...     a=3, b=5
... )
>>> d
{
    "x": 5,
    "y": "→(a b x)",
    "_parameters": {
        "a": 3,
        "b": 5
    }
}
>>> d.y
20
>>> def f(input="a", output="b", **kwargs):
...     return {output: kwargs[input], "_history": ...}
>>> f.metadata = {"id": "fffffff----------------------------fffff",
...             "name": "f",
...             "description": "Copy.",
...             "code": ...,
...             "parameters": ...}
>>> d = ldict(a=5) >> let(f, output="b")
>>> d.b
5
>>> d
{
    "a": 5,
    "b": 5,
    "_history": {
        "fffffff----------------------------fffff": {
            "name": "f",
            "description": "Copy.",
            "code": "def f(input='a', output='b', **kwargs):\nreturn {output: kwargs[input], '_history': ...}",
            "parameters": {
                "input": "a",
                "output": "b"
            }
        }
    }
}
>>> def f(input=["a", "b"], output=None, **kwargs):
...     return {output: kwargs[input[0]] * kwargs[input[1]]}
>>> d = ldict(a=5, b=7) >> let(f, output="c")
>>> d.c
35
>>> d
{
    "a": 5,
    "b": 7,
    "c": 35
}
Expand source code
def lazify(data, output_field: Union[list, str], f, rnd, is_multi_output) -> Union[dict, LazyVal]:
    """Create lazy values and handle metafields.
    >>> from ldict import ldict, let
    >>> from random import Random
    >>> (d := ldict(x=5) >> Random(0) >> (lambda x, a=1, b=[1, 2, 3, ... , 8]: {"y": a*x + b, "_parameters": ...}))
    {
        "x": 5,
        "y": "→(a b x)",
        "_parameters": {
            "a": 1,
            "b": 7
        }
    }
    >>> d.y
    12
    >>> d = ldict(x=5) >> Random(0) >> let(
    ...     (lambda x, a=1, b=[1, 2, 3, ... , 8]: {"y": a*x + b, "_parameters": ...}),
    ...     a=3, b=5
    ... )
    >>> d
    {
        "x": 5,
        "y": "→(a b x)",
        "_parameters": {
            "a": 3,
            "b": 5
        }
    }
    >>> d.y
    20
    >>> def f(input="a", output="b", **kwargs):
    ...     return {output: kwargs[input], "_history": ...}
    >>> f.metadata = {"id": "fffffff----------------------------fffff",
    ...             "name": "f",
    ...             "description": "Copy.",
    ...             "code": ...,
    ...             "parameters": ...}
    >>> d = ldict(a=5) >> let(f, output="b")
    >>> d.b
    5
    >>> d
    {
        "a": 5,
        "b": 5,
        "_history": {
            "fffffff----------------------------fffff": {
                "name": "f",
                "description": "Copy.",
                "code": "def f(input='a', output='b', **kwargs):\\nreturn {output: kwargs[input], '_history': ...}",
                "parameters": {
                    "input": "a",
                    "output": "b"
                }
            }
        }
    }
    >>> def f(input=["a", "b"], output=None, **kwargs):
    ...     return {output: kwargs[input[0]] * kwargs[input[1]]}
    >>> d = ldict(a=5, b=7) >> let(f, output="c")
    >>> d.c
    35
    >>> d
    {
        "a": 5,
        "b": 7,
        "c": 35
    }
    """

    """
    >>> def f(input=["a", "b"], output=None, **kwargs):
    ...     return {kwargs[output[0]]: kwargs[input[0]], kwargs[output[1]]: kwargs[input[1]]}
    >>> d = ldict(a=5, b=7) >> let(f, output=["c", "d"])
    >>> d.c
    35
    >>> d
    {
        "a": 5,
        "b": 7,
        "c": 35
    }
    """
    # TODO (minor): simplify to improve readability of this function
    config, f = (f.config, f.f) if isinstance(f, AbstractLet) else ({}, f)
    input_fields, parameters, optional = extract_input(f)
    noop = False
    if "_" in input_fields:
        noop = True
        del input_fields["_"]
        if isinstance(f, AbstractLet):  # pragma: no cover
            raise Exception("Cannot let parameters have values for a noop function")
    for k, v in config.items():
        parameters[k] = v
    if isinstance(f, FunctionType):
        try:
            body = extract_body(f)
        except CodeExtractionException as e:
            body = f"<{e}>"
        dynamic_input = extract_dynamic_input("".join(body))
    else:
        body = None
        if not (hasattr(f, "metadata") and "input" in f.metadata and "output" in f.metadata):  # pragma: no cover
            raise Exception(f"Missing 'metadata' containing 'input' and 'output' keys for custom callable '{type(f)}'")
        dynamic_input = []
    dynamic_output = []
    if hasattr(f, "metadata"):
        if not dynamic_input and "input" in f.metadata and "dynamic" in f.metadata["input"]:
            dynamic_input = f.metadata["input"]["dynamic"]
        if "output" in f.metadata and "dynamic" in f.metadata["output"]:
            dynamic_output = f.metadata["output"]["dynamic"]

    # Process dynamic_input.
    multidynamicinput = set()
    for par in dynamic_input:
        if par not in parameters:  # pragma: no cover
            raise Exception(f"Parameter '{par}' value is not available:", parameters)
        if parameters[par] == "[]":
            parameters[par] = []
        if isinstance(parameters[par], list):
            for k in parameters[par]:
                input_fields[k] = None
            multidynamicinput.add(par)
        elif isinstance(parameters[par], dict):
            for k in parameters[par]:
                input_fields[k] = None
            multidynamicinput.add(par)
        else:
            input_fields[parameters[par]] = None

    dynio = multidynamicinput | set(dynamic_output)
    deps = prepare_deps(data, input_fields, parameters, rnd, dynio, optional)
    for k, v in parameters.items():
        parameters[k] = deps[k]
    if noop:

        def la(**deps_out):
            f(**deps_out)
            return deps_out

        lazies = []
        # REMINDER: noop uses input fields as output
        dic = {k: LazyVal(k, la, deps, data, lazies) for k in input_fields}
        lazies.extend(dic.values())
        deps["_"] = None
        return dic

    newidx = 0
    if hasattr(f, "metadata"):
        step = f.metadata.copy()
        if "id" in step:
            newidx = step.pop("id")
            if "_" in newidx:  # pragma: no cover
                raise Exception(f"'id' cannot have '_': {newidx}")
        for k in ["input", "output", "function"]:
            if k in step:
                del step[k]
        if "code" in f.metadata and f.metadata["code"] is ...:
            if body is None:  # pragma: no cover
                raise Exception(f"Cannot autofill 'metadata.code' for custom callable '{type(f)}'")
            head = f"def f{str(signature(f))}:"
            code = head + "\n" + "\n".join(body)
            f.metadata["code"] = code
            step["code"] = code
        if "parameters" in f.metadata and f.metadata["parameters"] is ...:
            step["parameters"] = parameters
        if "function" in f.metadata and f.metadata["function"] is ...:
            # REMINDER: it is not clear yet whether somebody wants this...
            if hasattr(f, "pickle_dump"):
                f.metadata["function"] = f.pickle_dump
            else:
                import dill

                dump = dill.dumps(f, protocol=5)
                f.metadata["function"] = dump
                f.pickle_dump = dump  # Memoize
    else:
        step = {}
    if output_field == "extract":
        explicit, meta, meta_ellipsed = extract_output(f, body, deps, is_multi_output, dynamic_output)
        lazies = []
        dic = {k: LazyVal(k, f, deps, data, lazies) for k in explicit + meta}
        lazies.extend(dic.values())
        for metaf in meta_ellipsed:
            if metaf == "_code":
                if hasattr(f, "metadata") and "code" in f.metadata:
                    dic["_code"] = f.metadata["code"]
                else:
                    if body is None:  # pragma: no cover
                        raise Exception(f"Missing 'metadata' containing 'code' key for custom callable '{type(f)}'")
                    head = f"def f{str(signature(f))}:"
                    dic["_code"] = head + "\n" + "\n".join(body)
            elif metaf == "_parameters":
                dic["_parameters"] = parameters
            elif metaf == "_function":
                # REMINDER: it even more unclear whether somebody wants this...
                if hasattr(f, "pickle_dump"):
                    dic["_function"] = f.pickle_dump
                else:
                    import dill

                    dump = dill.dumps(f, protocol=5)
                    dic["_function"] = dump
                    f.pickle_dump = dump  # Memoize
            elif metaf == "_history":
                if "_history" in data:
                    if isinstance(data["_history"], LazyVal):
                        data["_history"] = data["_history"]()
                    last = list(data["_history"].keys())[-1]
                    if isinstance(last, int):
                        newidx = last + 1
                    elif newidx == 0:
                        newidx = ...
                    dic["_history"] = data["_history"].copy()
                else:
                    dic["_history"] = {}
                dic["_history"][newidx] = step
            else:  # pragma: no cover
                raise Exception(f"'...' is not defined for '{metaf}'.")
        return dic
    else:
        return LazyVal(output_field, f, deps, data, None)
def list2progression(lst)

Convert list representing A. or G. progression to lange

>>> list2progression([1,2,3,...,9])
[1 2 .+. 9]
>>> list2progression([1,2,4,...,16])
[1 2 .*. 16]

Parameters

lst
 

Returns

Expand source code
def list2progression(lst):
    """Convert list representing A. or G. progression to lange

    >>> list2progression([1,2,3,...,9])
    [1 2 .+. 9]
    >>> list2progression([1,2,4,...,16])
    [1 2 .*. 16]

    Parameters
    ----------
    lst

    Returns
    -------

    """
    # TODO move this to lange package
    try:
        diff1 = lst[1] - lst[0]
        diff2 = lst[2] - lst[1]
        ratio1 = lst[1] / lst[0]
        ratio2 = lst[2] / lst[1]
    except:
        raise InconsistentLange(f"Cannot identify whether this is a G. or A. progression: {lst}")
    newlst = [lst[0], lst[1], ..., lst[-1]]
    if diff1 == diff2:
        return AP(*newlst)
    elif ratio1 == ratio2:
        return GP(*newlst)
    else:
        raise InconsistentLange(f"Cannot identify whether this is a G. or A. progression: {lst}")
def prepare_deps(data, input, parameters, rnd, multi, optional)

Build a dict containing all needed dependencies (values) to apply a function: input fields and parameters.

Parameter values are given in let() or sampled according to a range using a random number generator that provides a 'choice' method. A range is specified as the default value of the parameter in the function signature.

Expand source code
def prepare_deps(data, input, parameters, rnd, multi, optional):
    """Build a dict containing all needed dependencies (values) to apply a function:
        input fields and parameters.

    Parameter values are given in let() or sampled according to a range using a random number generator that provides a 'choice' method.
    A range is specified as the default value of the parameter in the function signature.
    """
    deps = {}
    for k, v in parameters.items():
        # TODO existence of multidynamic output is raising exception here
        #  because it will only be extracted at the end of lazify
        #  A workaround is to declare multidynamic output at f.metadata, instead of detecting it.
        if isinstance(v, list) and k not in multi and k not in optional:
            if rnd is None:
                raise UndefinedSeed(
                    "\nMissing Random object (or some object with the method 'choice') "
                    "before parameterized function application.\n"
                    "Example: ldict(x=5) >> Random(42) >> (lambda x, a=[1,2,3]: {'y': a * x**2})\n"
                    f"field={k}\n"
                    f"values={v}\n{parameters=}\n{multi=}\n"
                    f"Another possible cause: Multidynamic output cannot be detected by now, "
                    f"please declare multidynamic output at f.metadata['output']"
                )
            deps[k] = rnd.choice(expand(v))
        elif v is None:
            raise DependenceException(f"'None' value for parameter '{k}'.", deps.keys())
        else:
            deps[k] = v
    for k in input:
        if k in data:
            deps[k] = data[k]
        if k not in optional:
            if k not in deps:
                raise DependenceException(f"Missing field '{k}'.", data.keys())
            if deps[k] is None:
                raise DependenceException(f"'None' value for field '{k}'.", data.keys())
    return deps