Completed
Push — dev ( 50f4e4...37cb74 )
by Patrik
27s queued 16s
created

solph._plumbing.valid_sequence()   B

Complexity

Conditions 8

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 35
rs 7.3333
c 0
b 0
f 0
cc 8
nop 2
1
# -*- coding: utf-8 -*-
2
3
"""Helpers to fit scalar values into sequences.
4
5
SPDX-FileCopyrightText: Uwe Krien <[email protected]>
6
SPDX-FileCopyrightText: Simon Hilpert
7
SPDX-FileCopyrightText: Cord Kaldemeyer
8
SPDX-FileCopyrightText: henhuy
9
10
SPDX-License-Identifier: MIT
11
12
"""
13
import warnings
14
from collections import abc
15
from itertools import repeat
16
17
import numpy as np
18
19
20
def sequence(iterable_or_scalar):
21
    """Checks if an object is iterable (except string) or scalar and returns
22
    the an numpy array of the sequence if object is an iterable or an
23
    'emulated'  sequence object of class _FakeSequence if object is a scalar.
24
25
    Parameters
26
    ----------
27
    iterable_or_scalar : iterable or None or int or float
28
29
    Examples
30
    --------
31
    >>> y = sequence([1,2,3,4,5,6,7,8,9,10,11])
32
    >>> y[0]
33
    1
34
35
    >>> y[10]
36
    11
37
38
    >>> import pandas as pd
39
    >>> s1 = sequence(pd.Series([1,5,9]))
40
    >>> s1[2]
41
    9
42
43
    >>> x = sequence(10)
44
    >>> x[0]
45
    10
46
47
    >>> x[10]
48
    10
49
50
    """
51
    if isinstance(iterable_or_scalar, str):
52
        return iterable_or_scalar
53
    elif isinstance(iterable_or_scalar, abc.Iterable):
54
        return np.array(iterable_or_scalar)
55
    else:
56
        return _FakeSequence(value=iterable_or_scalar)
57
58
59
def valid_sequence(sequence, length: int) -> bool:
60
    """Checks if an object is a numpy array of at least the given length
61
    or an 'emulated' sequence object of class _FakeSequence.
62
    If unset, the latter is set to the required lenght.
63
64
    """
65
    if sequence[0] is None:
66
        return False
67
68
    if isinstance(sequence, _FakeSequence):
69
        if sequence.size is None:
70
            sequence.size = length
71
72
        if sequence.size == length:
73
            return True
74
        else:
75
            return False
76
77
    if isinstance(sequence, np.ndarray):
78
        if sequence.size == length:
79
            return True
80
        # --- BEGIN: To be removed for versions >= v0.6 ---
81
        elif sequence.size > length:
82
            warnings.warn(
83
                "Sequence longer than needed"
84
                f" ({sequence.size} items instead of {length})."
85
                " This will be trated as an error in the future.",
86
                FutureWarning,
87
            )
88
            return True
89
        # --- END ---
90
        else:
91
            raise ValueError(f"Lentgh of {sequence} should be {length}.")
92
93
    return False
94
95
96
class _FakeSequence:
97
    """Emulates a list whose length is not known in advance.
98
99
    Parameters
100
    ----------
101
    value : scalar
102
    length : integer
103
104
105
    Examples
106
    --------
107
    >>> s = _FakeSequence(value=42, length=5)
108
    >>> s
109
    [42, 42, 42, 42, 42]
110
    >>> s = _FakeSequence(value=42)
111
    >>> # undefined lenght, access still works
112
    >>> s[1337]
113
    42
114
    """
115
116
    def __init__(self, value, length=None):
117
        self._value = value
118
        self._length = length
119
120
    @property
121
    def size(self):
122
        return self._length
123
124
    @size.setter
125
    def size(self, value):
126
        self._length = value
127
128
    def __getitem__(self, _):
129
        return self._value
130
131
    def __repr__(self):
132
        if self._length is not None:
133
            return str([i for i in self])
134
        else:
135
            return f"[{self._value}, {self._value}, ..., {self._value}]"
136
137
    def __len__(self):
138
        return self._length
139
140
    def __iter__(self):
141
        return repeat(self._value, self._length)
142
143
    def max(self):
144
        return self._value
145
146
    def min(self):
147
        return self._value
148
149
    def sum(self):
150
        if self._length is None:
151
            return np.inf
152
        else:
153
            return self._length * self._value
154
155
    def to_numpy(self, length=None):
156
        if length is not None:
157
            return np.full(length, self._value)
158
        else:
159
            return np.full(len(self), self._value)
160
161
    @property
162
    def value(self):
163
        return self._value
164