|
|
@@ -1,5 +1,8 @@
|
|
|
"""
|
|
|
This file contains the standard library for CBD building blocks.
|
|
|
+
|
|
|
+.. versionchanged:: 1.6
|
|
|
+ Replaced the :code:`MultiplexerBlock` with the :code:`BlendBlock`.
|
|
|
"""
|
|
|
from pyCBD.Core import BaseBlock, CBD
|
|
|
import math
|
|
|
@@ -12,7 +15,7 @@ __all__ = ['ConstantBlock', 'NegatorBlock', 'InverterBlock',
|
|
|
'MinBlock', 'MaxBlock',
|
|
|
'LessThanBlock', 'EqualsBlock', 'LessThanOrEqualsBlock',
|
|
|
'NotBlock', 'OrBlock', 'AndBlock',
|
|
|
- 'MultiplexerBlock', 'SplitBlock',
|
|
|
+ 'BlendBlock', 'SplitBlock',
|
|
|
'DelayBlock', 'DeltaTBlock', 'TimeBlock', 'LoggingBlock',
|
|
|
'AddOneBlock', 'DerivatorBlock', 'IntegratorBlock',
|
|
|
'Clock', 'SequenceBlock']
|
|
|
@@ -204,6 +207,10 @@ class ModuloBlock(BaseBlock):
|
|
|
:Output Ports:
|
|
|
**OUT1** -- The remainder after division.
|
|
|
|
|
|
+ Warning:
|
|
|
+ The order of inputs is important in this block, so a default port name cannot be identified.
|
|
|
+ Will always raise a :code:`ValueError`.
|
|
|
+
|
|
|
See Also:
|
|
|
`math.fmod <https://docs.python.org/3.8/library/math.html#math.fmod>`_
|
|
|
"""
|
|
|
@@ -215,6 +222,7 @@ class ModuloBlock(BaseBlock):
|
|
|
self.appendToSignal(math.fmod(self.getInputSignal(curIteration, "IN1").value, self.getInputSignal(curIteration, "IN2").value))
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
|
|
|
|
|
|
@@ -241,6 +249,10 @@ class RootBlock(BaseBlock):
|
|
|
|
|
|
Raises:
|
|
|
ZeroDivisionError: When the input is less than :code:`tolerance`.
|
|
|
+
|
|
|
+ Warning:
|
|
|
+ The order of inputs is important in this block, so a default port name cannot be identified.
|
|
|
+ Will always raise a :code:`ValueError`.
|
|
|
"""
|
|
|
def __init__(self, block_name, tolerance=1e-30):
|
|
|
BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
|
|
|
@@ -253,6 +265,7 @@ class RootBlock(BaseBlock):
|
|
|
self.appendToSignal(self.getInputSignal(curIteration, "IN1").value ** (1 / input))
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
|
|
|
|
|
|
@@ -273,6 +286,10 @@ class PowerBlock(BaseBlock):
|
|
|
|
|
|
:Output Ports:
|
|
|
**OUT1** -- The :code:`IN2`-th power of :code:`IN1`.
|
|
|
+
|
|
|
+ Warning:
|
|
|
+ The order of inputs is important in this block, so a default port name cannot be identified.
|
|
|
+ Will always raise a :code:`ValueError`.
|
|
|
"""
|
|
|
def __init__(self, block_name):
|
|
|
BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
|
|
|
@@ -281,6 +298,7 @@ class PowerBlock(BaseBlock):
|
|
|
self.appendToSignal(self.getInputSignal(curIteration, "IN1").value ** self.getInputSignal(curIteration, "IN2").value)
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
|
|
|
|
|
|
@@ -348,6 +366,11 @@ class ClampBlock(BaseBlock):
|
|
|
|
|
|
:Output Ports:
|
|
|
**OUT1** -- The clamped value.
|
|
|
+
|
|
|
+ Warning:
|
|
|
+ The order of operands is important in this block if constants are being used,
|
|
|
+ so a default port name cannot be identified in that case.
|
|
|
+ Will raise a :code:`ValueError` if invalid.
|
|
|
"""
|
|
|
def __init__(self, block_name, min=-1, max=1, use_const=True):
|
|
|
BaseBlock.__init__(self, block_name, ["IN1"] if use_const else ["IN1", "IN2", "IN3"], ["OUT1"])
|
|
|
@@ -366,6 +389,7 @@ class ClampBlock(BaseBlock):
|
|
|
self.appendToSignal(min(max(x, min_), max_))
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
if not self._use_const:
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
return super(ClampBlock, self).defaultInputPortNameIdentifier()
|
|
|
@@ -423,57 +447,54 @@ class GenericBlock(BaseBlock):
|
|
|
return repr
|
|
|
|
|
|
|
|
|
-class MultiplexerBlock(BaseBlock):
|
|
|
+class BlendBlock(BaseBlock):
|
|
|
"""
|
|
|
- The multiplexer block will output the signal from an input, based on the index
|
|
|
- :code:`select`.
|
|
|
+ The blend block will output the combination of :code:`IN1` and :code:`IN2`,
|
|
|
+ mixed for an :code:`IN3` amount. In other words:
|
|
|
+
|
|
|
+ .. math::
|
|
|
+ OUT1 = (IN3 \\cdot IN1) + ((1 - IN3) \\cdot IN2)
|
|
|
+
|
|
|
+ Commonly, this block is also known as a :code:`mix()` operation, where :code:`IN3`
|
|
|
+ represents the blending alpha. When :code:`IN3` is an integer, this block basically
|
|
|
+ becomes an inline multiplexer.
|
|
|
+
|
|
|
+ .. versionadded:: 1.6
|
|
|
+ Replaces the :code:`MultiplexerBlock`, given that is an special case of
|
|
|
+ a :code:`BlendBlock`.
|
|
|
|
|
|
Args:
|
|
|
block_name (str): The name of the block.
|
|
|
- numberOfInputs (int): The amount of input ports to choose from. Defaults to 2.
|
|
|
- zero (bool): When :code:`True`, the index is zero-based. Otherwise,
|
|
|
- the :code:`select` signal is interpreted as 1-indexed.
|
|
|
- Defaults to :code:`True`.
|
|
|
|
|
|
:Input Ports:
|
|
|
- - **select** -- The input index to choose from. When out of range, an exception
|
|
|
- is thrown.
|
|
|
- - **IN1** -- The first option.
|
|
|
- - **IN2** -- The second option.
|
|
|
- - ...
|
|
|
+ - **IN1** -- The first value.
|
|
|
+ - **IN2** -- The second vale.
|
|
|
+ - **IN3** -- The amount of blending between the first value and the second.
|
|
|
+ This is expected to be a value between 0 and 1. When 0, :code:`IN2`
|
|
|
+ is output. When 1, :code:`IN1` is yielded.
|
|
|
|
|
|
:Output Ports:
|
|
|
**OUT1** -- The input to which the operation was applied.
|
|
|
|
|
|
Raises:
|
|
|
- IndexError: When the :code:`select` input is out of range.
|
|
|
+ ValueError: When the :code:`IN3` input is out of range.
|
|
|
|
|
|
- Note:
|
|
|
- This block is not optimized in the simulator. Even though there can be a part of the
|
|
|
- block that is not necessary to be computed, the dependency graph still includes both
|
|
|
- inputs. While this is a definite optimization, recomputing the dependency graph at
|
|
|
- each iteration is much less ideal.
|
|
|
+ Warning:
|
|
|
+ The order of operands is important in this block, so a default port name cannot be identified.
|
|
|
+ Will always raise a :code:`ValueError`.
|
|
|
"""
|
|
|
- def __init__(self, block_name, numberOfInputs=2, zero=True):
|
|
|
- BaseBlock.__init__(self, block_name, ["select"] + ["IN%i" % (x + 1) for x in range(numberOfInputs)], ["OUT1"])
|
|
|
- self.__numberOfInputs = numberOfInputs
|
|
|
- self.__zero = zero
|
|
|
+ def __init__(self, block_name):
|
|
|
+ BaseBlock.__init__(self, block_name, ["IN1", "IN2", "IN3"], ["OUT1"])
|
|
|
|
|
|
def compute(self, curIteration):
|
|
|
- select = self.getInputSignal(curIteration, "select").value
|
|
|
- if self.__zero:
|
|
|
- select += 1
|
|
|
- if select < 0 or select > self.__numberOfInputs:
|
|
|
- raise IndexError("Select input out of range for block %s" % self.getPath())
|
|
|
- self.appendToSignal(self.getInputSignal(curIteration, "IN%d" % select).value)
|
|
|
-
|
|
|
- def getNumberOfInputs(self):
|
|
|
- """
|
|
|
- Gets the number of input ports to choose from.
|
|
|
- """
|
|
|
- return self.__numberOfInputs
|
|
|
+ a = self.getInputSignal(curIteration, "IN1").value
|
|
|
+ b = self.getInputSignal(curIteration, "IN2").value
|
|
|
+ alpha = self.getInputSignal(curIteration, "IN3").value
|
|
|
+ if alpha > 1 or alpha < 0: raise ValueError("IN3 input out of range; expected value between 0 and 1.")
|
|
|
+ self.appendToSignal(alpha * a + (1 - alpha) * b)
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
|
|
|
|
|
|
@@ -592,7 +613,11 @@ class LessThanBlock(BaseBlock):
|
|
|
:Output Ports:
|
|
|
**OUT1** -- Yields a 1 when :code:`IN1` is smaller than :code:`IN2`, otherwise 0.
|
|
|
In Python, 0 is a *falsy* value and can therefore be used as boolean check, or as computation
|
|
|
- value for other equation in the CBD. Similaryly, 1 is a *truthy* value.
|
|
|
+ value for other equation in the CBD. Similarly, 1 is a *truthy* value.
|
|
|
+
|
|
|
+ Warning:
|
|
|
+ The order of operands is important in this block, so a default port name cannot be identified.
|
|
|
+ Will always raise a :code:`ValueError`.
|
|
|
|
|
|
Tip:
|
|
|
To check the "greater than" relationship, you can swap the :code:`IN1` and :code:`IN2` inputs
|
|
|
@@ -609,6 +634,7 @@ class LessThanBlock(BaseBlock):
|
|
|
self.appendToSignal(1 if gisv("IN1") < gisv("IN2") else 0)
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
|
|
|
|
|
|
@@ -657,6 +683,10 @@ class LessThanOrEqualsBlock(BaseBlock):
|
|
|
In Python, 0 is a *falsy* value and can therefore be used as boolean check, or as computation
|
|
|
value for other equation in the CBD. Similaryly, 1 is a *truthy* value.
|
|
|
|
|
|
+ Warning:
|
|
|
+ The order of operands is important in this block, so a default port name cannot be identified.
|
|
|
+ Will always raise a :code:`ValueError`.
|
|
|
+
|
|
|
Tip:
|
|
|
To check the "greater than or equal" relationship, you can swap the :code:`IN1` and :code:`IN2` inputs
|
|
|
around.
|
|
|
@@ -672,6 +702,7 @@ class LessThanOrEqualsBlock(BaseBlock):
|
|
|
self.appendToSignal(1 if gisv("IN1") <= gisv("IN2") else 0)
|
|
|
|
|
|
def defaultInputPortNameIdentifier(self):
|
|
|
+ """:meta private:"""
|
|
|
raise ValueError("The order of the operands is important for this block. Please provide a port name.")
|
|
|
|
|
|
|
|
|
@@ -1047,13 +1078,6 @@ class IntegratorBlock(CBD):
|
|
|
self.addConnection("sumState", "delayState", input_port_name="IN1")
|
|
|
self.addConnection("sumState", "OUT1")
|
|
|
|
|
|
- def getDependencies(self, curIteration):
|
|
|
- # Treat dependencies differently.
|
|
|
- # For instance, at the first iteration (curIteration == 0), the block only depends on the IC;
|
|
|
- if curIteration == 0:
|
|
|
- return [self.getInputPortByName("IC").getIncoming().source]
|
|
|
- return []
|
|
|
-
|
|
|
|
|
|
class Clock(BaseBlock):
|
|
|
"""
|