Newer
Older
Angus Lothian
committed
Contains some of the most commonly used mathematical operations.
from typing import Dict, Iterable, Optional, Set
from numpy import abs as np_abs
from numpy import conjugate, sqrt
Angus Lothian
committed
from b_asic.graph_component import Name, TypeName
from b_asic.operation import AbstractOperation, Operation
Angus Lothian
committed
Gives a specified value that remains constant for every iteration.
.. math:: y = \text{value}
Parameters
==========
value : Number, default: 0
The constant value.
name : Name, optional
Operation name.
def __init__(self, value: Number = 0, name: Name = Name("")):
Angus Lothian
committed
"""Construct a Constant operation with the given value."""
super().__init__(
input_count=0,
output_count=1,
Angus Lothian
committed
self.set_param("value", value)
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
def evaluate(self):
return self.param("value")
@property
Angus Lothian
committed
def value(self) -> Number:
"""Get the constant value of this operation."""
return self.param("value")
@value.setter
def value(self, value: Number) -> None:
"""Set the constant value of this operation."""
Angus Lothian
committed
Gives the result of adding two inputs.
.. math:: y = x_0 + x_1
Parameters
==========
src0, src1 : SignalSourceProvider, optional
The two signals to add.
name : Name, optional
Operation name.
latency : int, optional
Operation latency (delay from input to output in time units).
latency_offsets : dict[str, int], optional
Used if inputs have different arrival times, e.g.,
``{"in0": 0, "in1": 1}`` which corresponds to *src1* arriving one
time unit later than *src0*. If not provided and *latency* is
provided, set to zero if not explicitly provided. So the previous
example can be written as ``{"in1": 1}`` only.
execution_time : int, optional
Operation execution time (time units before operator can be
reused).
See also
========
AddSub
Angus Lothian
committed
"""
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
"""
Construct an Addition operation.
"""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
def evaluate(self, a, b):
return a + b
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
if any(c == 0.0 for c in constants):
print("One input is 0!")
Angus Lothian
committed
Gives the result of subtracting the second input from the first one.
.. math:: y = x_0 - x_1
Parameters
==========
src0, src1 : SignalSourceProvider, optional
The two signals to subtract.
name : Name, optional
Operation name.
latency : int, optional
Operation latency (delay from input to output in time units).
latency_offsets : dict[str, int], optional
Used if inputs have different arrival times, e.g.,
``{"in0": 0, "in1": 1}`` which corresponds to *src1* arriving one
time unit later than *src0*. If not provided and *latency* is
provided, set to zero if not explicitly provided. So the previous
example can be written as ``{"in1": 1}`` only.
execution_time : int, optional
Operation execution time (time units before operator can be
reused).
See also
========
AddSub
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a Subtraction operation."""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
def evaluate(self, a, b):
return a - b
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
if any(c == 0.0 for c in constants):
print("One input is 0!")
class AddSub(AbstractOperation):
Two-input addition or subtraction operation.
Gives the result of adding or subtracting two inputs.
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
.. math::
y = \begin{cases}
x_0 + x_1,& \text{is_add} = \text{True}\\
x_0 - x_1,& \text{is_add} = \text{False}
\end{cases}
This is used to later map additions and subtractions to the same
operator.
Parameters
==========
is_add : bool, default: True
If True, the operation is an addition, if False, a subtraction.
src0, src1 : SignalSourceProvider, optional
The two signals to add or subtract.
name : Name, optional
Operation name.
latency : int, optional
Operation latency (delay from input to output in time units).
latency_offsets : dict[str, int], optional
Used if inputs have different arrival times, e.g.,
``{"in0": 0, "in1": 1}`` which corresponds to *src1* arriving one
time unit later than *src0*. If not provided and *latency* is
provided, set to zero if not explicitly provided. So the previous
example can be written as ``{"in1": 1}`` only.
execution_time : int, optional
Operation execution time (time units before operator can be
reused).
See also
========
Addition, Subtraction
def __init__(
self,
is_add: bool = True,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
"""Construct an Addition/Subtraction operation."""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
self.set_param("is_add", is_add)
@classmethod
def type_name(cls) -> TypeName:
def evaluate(self, a, b):
return a + b if self.is_add else a - b
@property
def is_add(self) -> Number:
"""Get if operation is add."""
return self.param("is_add")
@is_add.setter
def is_add(self, is_add: bool) -> None:
"""Set if operation is add."""
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
if any(c == 0.0 for c in constants):
print("One input is 0!")
def _propagate_constant_parameters(
self, valid_operations: Optional[Set["Operation"]] = None
) -> None:
print(f"Can turn into {'Addition' if self.is_add else 'Subtraction'}")
return
Angus Lothian
committed
Gives the result of multiplying two inputs.
.. math:: y = x_0 \times x_1
Parameters
==========
src0, src1 : SignalSourceProvider, optional
The two signals to multiply.
name : Name, optional
Operation name.
latency : int, optional
Operation latency (delay from input to output in time units).
latency_offsets : dict[str, int], optional
Used if inputs have different arrival times, e.g.,
``{"in0": 0, "in1": 1}`` which corresponds to *src1* arriving one
time unit later than *src0*. If not provided and *latency* is
provided, set to zero if not explicitly provided. So the previous
example can be written as ``{"in1": 1}`` only.
execution_time : int, optional
Operation execution time (time units before operator can be
reused).
See also
========
ConstantMultiplication
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a Multiplication operation."""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
def evaluate(self, a, b):
return a * b
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
if any(c == 0.0 for c in constants):
print("One input is 0!")
if any(c == 1.0 for c in constants):
print("One input is 1!")
print("Can turn into ConstantMultiplication")
Angus Lothian
committed
Gives the result of dividing the first input by the second one.
.. math:: y = \frac{x_0}{x_1}
See also
========
Reciprocal
Angus Lothian
committed
"""
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a Division operation."""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
def evaluate(self, a, b):
return a / b
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
numerator, denominator = constants
if numerator == 0.0:
print("Result is 0!")
if denominator is not None:
print("Can turn into ConstantMultiplication")
Angus Lothian
committed
class Min(AbstractOperation):
Angus Lothian
committed
Gives the minimum value of two inputs.
.. math:: y = \min\{x_0 , x_1\}
.. note:: Only real-valued numbers are supported.
See also
========
Max
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a Min operation."""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
def evaluate(self, a, b):
if isinstance(a, complex) or isinstance(b, complex):
raise ValueError(
"core_operations.Min does not support complex numbers."
)
Angus Lothian
committed
return a if a < b else b
Angus Lothian
committed
Gives the maximum value of two inputs.
.. math:: y = \max\{x_0 , x_1\}
.. note:: Only real-valued numbers are supported.
See also
========
Min
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a Max operation."""
super().__init__(
input_count=2,
output_count=1,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
if isinstance(a, complex) or isinstance(b, complex):
raise ValueError(
"core_operations.Max does not support complex numbers."
)
Angus Lothian
committed
class SquareRoot(AbstractOperation):
Angus Lothian
committed
Gives the square root of its input.
.. math:: y = \sqrt{x}
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a SquareRoot operation."""
super().__init__(
input_count=1,
output_count=1,
input_sources=[src0],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
def evaluate(self, a):
return sqrt(complex(a))
Angus Lothian
committed
class ComplexConjugate(AbstractOperation):
Angus Lothian
committed
Gives the complex conjugate of its input.
.. math:: y = x^*
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a ComplexConjugate operation."""
super().__init__(
input_count=1,
output_count=1,
input_sources=[src0],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
return conjugate(a)
Angus Lothian
committed
class Absolute(AbstractOperation):
Angus Lothian
committed
Gives the absolute value of its input.
.. math:: y = |x|
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct an Absolute operation."""
super().__init__(
input_count=1,
output_count=1,
input_sources=[src0],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
return np_abs(a)
Angus Lothian
committed
class ConstantMultiplication(AbstractOperation):
Angus Lothian
committed
Gives the result of multiplying its input by a specified value.
.. math:: y = x_0 \times \text{value}
def __init__(
self,
value: Number = 0,
src0: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
"""Construct a ConstantMultiplication operation with the given value.
"""
super().__init__(
input_count=1,
output_count=1,
input_sources=[src0],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
self.set_param("value", value)
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
return a * self.param("value")
Angus Lothian
committed
def value(self) -> Number:
"""Get the constant value of this operation."""
return self.param("value")
@value.setter
def value(self, value: Number) -> None:
"""Set the constant value of this operation."""
def _propagate_constant_parameters(
self, valid_operations: Optional[Set["Operation"]] = None
) -> None:
if self.value == 0.0:
print("Constant is zero!")
if self.value == 1.0:
print("Constant is zero!")
return
Angus Lothian
committed
class Butterfly(AbstractOperation):
r"""
Radix-2 Butterfly operation.
Angus Lothian
committed
Gives the result of adding its two inputs, as well as the result of
subtracting the second input from the first one.
.. math::
\begin{eqnarray}
y_0 & = & x_0 + x_1\\
y_1 & = & x_0 - x_1
\end{eqnarray}
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a Butterfly operation."""
super().__init__(
input_count=2,
output_count=2,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
def evaluate(self, a, b):
return a + b, a - b
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
if any(c == 0.0 for c in constants):
print("One input is 0!")
Angus Lothian
committed
class MAD(AbstractOperation):
Angus Lothian
committed
Gives the result of multiplying the first input by the second input and
then adding the third input.
.. math:: y = x_0 \times x_1 + x_2
Angus Lothian
committed
"""
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
src2: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
Angus Lothian
committed
"""Construct a MAD operation."""
super().__init__(
input_count=3,
output_count=1,
input_sources=[src0, src1, src2],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
Angus Lothian
committed
@classmethod
def type_name(cls) -> TypeName:
Angus Lothian
committed
def evaluate(self, a, b, c):
return a * b + c
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
a, b, c = constants
if a == 0.0 or b == 0.0:
print("One multiplier input is zero!")
if a == 1.0 or b == 1.0:
print("One multiplier input is one!")
if any(c == 0.0):
print("Adder input is zero!")
class SymmetricTwoportAdaptor(AbstractOperation):
.. math::
\begin{eqnarray}
y_0 & = & x_1 + \text{value}\times\left(x_1 - x_0\right)\\
y_1 & = & x_0 + \text{value}\times\left(x_1 - x_0\right)
\end{eqnarray}
def __init__(
self,
value: Number = 0,
src0: Optional[SignalSourceProvider] = None,
src1: Optional[SignalSourceProvider] = None,
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
"""Construct a SymmetricTwoportAdaptor operation."""
super().__init__(
input_count=2,
output_count=2,
input_sources=[src0, src1],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
self.set_param("value", value)
@classmethod
def type_name(cls) -> TypeName:
def evaluate(self, a, b):
return b + tmp, a + tmp
@property
def value(self) -> Number:
"""Get the constant value of this operation."""
return self.param("value")
@value.setter
def value(self, value: Number) -> None:
"""Set the constant value of this operation."""
def _propagate_some_constants(
self,
constants: Iterable[Optional[Number]],
valid_operations: Optional[Set["Operation"]] = None,
) -> None:
if any(c == 0.0 for c in constants):
print("One input is 0!")
def _propagate_constant_parameters(
self, valid_operations: Optional[Set["Operation"]] = None
) -> None:
if self.value == 0.0:
print("Constant is zero!")
return
class Reciprocal(AbstractOperation):
r"""
Reciprocal operation.
Gives the reciprocal of its input.
.. math:: y = \frac{1}{x}
See also
========
Division
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
"""
def __init__(
self,
src0: Optional[SignalSourceProvider] = None,
name: Name = Name(""),
latency: Optional[int] = None,
latency_offsets: Optional[Dict[str, int]] = None,
execution_time: Optional[int] = None,
):
"""Construct an Reciprocal operation."""
super().__init__(
input_count=1,
output_count=1,
name=Name(name),
input_sources=[src0],
latency=latency,
latency_offsets=latency_offsets,
execution_time=execution_time,
)
@classmethod
def type_name(cls) -> TypeName:
return TypeName("rec")
def evaluate(self, a):
return 1 / a