-
Notifications
You must be signed in to change notification settings - Fork 0
/
op.py
123 lines (103 loc) · 3.64 KB
/
op.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""spdaot.op
Overview:
TODO
Classes:
Op: wrapper class for functions considered as elements of an algebra or
group equipped with an action
"""
from numbers import Number
from types import FunctionType, LambdaType
from .frosting import compose
class Op:
"""
TODO: docstring
"""
def __init__(self, func, name=None):
"""Initialize self by setting _f equal to the argument."""
self._f = func
try:
self.name = str(name)
except:
self.name = None
def __mul__(self, other):
"""
Attempt to compose or multiply depending on context.
If other is an Op or a function, returns the appropriate
composition. Otherwise, tries to pointwise multiply if
possible.
"""
if isinstance(other, Op):
return Op(compose(self._f, other._f))
elif isinstance(other, FunctionType) or isinstance(other, LambdaType):
return Op(compose(self._f, other))
else:
try:
return Op(lambda x: self._f(x) * other)
except:
return NotImplemented
def __rmul__(self, other):
"""
Attempt to compose or multiply depending on context.
If other is an Op or a function, returns the appropriate
composition. Otherwise, tries to pointwise multiply if
possible.
"""
# the case isinstance(other, Op) will never happen, since in that
# case, other.__mul__() will be called instead
if isinstance(other, FunctionType) or isinstance(other, LambdaType):
return Op(compose(other, self._f))
else:
try:
return Op(lambda x: other * self._f(x))
except:
return NotImplemented
def __add__(self, other):
"""
Attempt to add two operators.
If other is an Op or a function, returns the appropriate
pointwise sum. If other is a Number, interprets other as a constant
function and does the same.
"""
if isinstance(other, Op):
return Op(lambda x: self._f(x) + other._f(x))
elif isinstance(other, FunctionType) or isinstance(other, LambdaType):
return Op(lambda x: self._f(x) + other(x))
else:
try:
return Op(lambda x: self._f(x) + other)
except:
assert Number # silence Flake8 until we implement this TODO
return NotImplemented
def __radd__(self, other):
"""
Attempt to add two operators.
If other is an Op or a function, returns the appropriate
pointwise sum. If other is a Number, interprets other as a constant
function and does the same.
"""
if isinstance(other, Op):
return Op(lambda x: self._f(x) + other._f(x))
elif isinstance(other, FunctionType) or isinstance(other, LambdaType):
return Op(lambda x: self._f(x) + other(x))
else:
try:
return Op(lambda x: self._f(x) + other)
except:
return NotImplemented
def __sub__(self, other):
"""Return self - other."""
return self + -1 * other
def __rsub__(self, other):
"""Return other - self."""
return other + -1 * self
def __call__(self, other):
"""Act on other with self._f."""
return self._f(other)
def __str__(self):
"""Print self.name"""
return self.name
def __repr__(self):
"""Print self.name"""
return self.name
identity = Op(lambda x: x, name='id')
"""Op object for the identity operator."""