From 90b77b935fbae3eb84fc2d44a566ad1ff90b5ad9 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Wed, 18 Jan 2023 21:31:38 +0100 Subject: [PATCH] More documentation fixes --- b_asic/core_operations.py | 11 +++--- b_asic/graph_component.py | 3 +- b_asic/operation.py | 55 +++++++++++++++++--------- b_asic/port.py | 17 +++++--- b_asic/signal.py | 4 +- b_asic/signal_flow_graph.py | 77 +++++++++++++++++++++++------------- b_asic/simulation.py | 40 +++++++++++++------ b_asic/special_operations.py | 12 ++++-- 8 files changed, 142 insertions(+), 77 deletions(-) diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py index 4b79eeed..ae78545e 100644 --- a/b_asic/core_operations.py +++ b/b_asic/core_operations.py @@ -1,4 +1,5 @@ -"""B-ASIC Core Operations Module. +""" +B-ASIC Core Operations Module. Contains some of the most commonly used mathematical operations. """ @@ -49,7 +50,7 @@ class Constant(AbstractOperation): @value.setter def value(self, value: Number) -> None: """Set the constant value of this operation.""" - return self.set_param("value", value) + self.set_param("value", value) class Addition(AbstractOperation): @@ -164,7 +165,7 @@ class AddSub(AbstractOperation): @is_add.setter def is_add(self, is_add: bool) -> None: """Set if operation is add.""" - return self.set_param("is_add", is_add) + self.set_param("is_add", is_add) class Multiplication(AbstractOperation): @@ -453,7 +454,7 @@ class ConstantMultiplication(AbstractOperation): @value.setter def value(self, value: Number) -> None: """Set the constant value of this operation.""" - return self.set_param("value", value) + self.set_param("value", value) class Butterfly(AbstractOperation): @@ -571,4 +572,4 @@ class SymmetricTwoportAdaptor(AbstractOperation): @value.setter def value(self, value: Number) -> None: """Set the constant value of this operation.""" - return self.set_param("value", value) + self.set_param("value", value) diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py index 624a42f3..32b8e170 100644 --- a/b_asic/graph_component.py +++ b/b_asic/graph_component.py @@ -1,4 +1,5 @@ -"""B-ASIC Graph Component Module. +""" +B-ASIC Graph Component Module. Contains the base for all components with an ID in a signal flow graph. """ diff --git a/b_asic/operation.py b/b_asic/operation.py index 9e5aa204..9ee6d5ff 100644 --- a/b_asic/operation.py +++ b/b_asic/operation.py @@ -38,7 +38,7 @@ class Operation(GraphComponent, SignalSourceProvider): """Operation interface. Operations are graph components that perform a certain function. - They are connected to eachother by signals through their input/output + They are connected to each other by signals through their input/output ports. Operations can be evaluated independently using evaluate_output(). @@ -82,7 +82,7 @@ class Operation(GraphComponent, SignalSourceProvider): self, src: Union[SignalSourceProvider, Number] ) -> "Union[Multiplication, ConstantMultiplication]": """Overloads the multiplication operator to make it return a new Multiplication operation - object that is connected to the self and other objects. If other is a number then + object that is connected to the self and other objects. If *src* is a number, then returns a ConstantMultiplication operation object instead. """ raise NotImplementedError @@ -91,8 +91,9 @@ class Operation(GraphComponent, SignalSourceProvider): def __rmul__( self, src: Union[SignalSourceProvider, Number] ) -> "Union[Multiplication, ConstantMultiplication]": - """Overloads the multiplication operator to make it return a new Multiplication operation - object that is connected to the self and other objects. If other is a number then + """ + Overloads the multiplication operator to make it return a new Multiplication operation + object that is connected to the self and other objects. If *src* is a number, then returns a ConstantMultiplication operation object instead. """ raise NotImplementedError @@ -101,7 +102,8 @@ class Operation(GraphComponent, SignalSourceProvider): def __truediv__( self, src: Union[SignalSourceProvider, Number] ) -> "Division": - """Overloads the division operator to make it return a new Division operation + """ + Overloads the division operator to make it return a new Division operation object that is connected to the self and other objects. """ raise NotImplementedError @@ -110,14 +112,16 @@ class Operation(GraphComponent, SignalSourceProvider): def __rtruediv__( self, src: Union[SignalSourceProvider, Number] ) -> "Division": - """Overloads the division operator to make it return a new Division operation + """ + Overloads the division operator to make it return a new Division operation object that is connected to the self and other objects. """ raise NotImplementedError @abstractmethod def __lshift__(self, src: SignalSourceProvider) -> Signal: - """Overloads the left shift operator to make it connect the provided signal source + """ + Overloads the left shift operator to make it connect the provided signal source to this operation's input, assuming it has exactly 1 input port. Returns the new signal. """ @@ -160,7 +164,8 @@ class Operation(GraphComponent, SignalSourceProvider): @property @abstractmethod def input_signals(self) -> Iterable[Signal]: - """Get all the signals that are connected to this operation's input ports, + """ + Get all the signals that are connected to this operation's input ports, in no particular order. """ raise NotImplementedError @@ -168,14 +173,16 @@ class Operation(GraphComponent, SignalSourceProvider): @property @abstractmethod def output_signals(self) -> Iterable[Signal]: - """Get all the signals that are connected to this operation's output ports, + """ + Get all the signals that are connected to this operation's output ports, in no particular order. """ raise NotImplementedError @abstractmethod def key(self, index: int, prefix: str = "") -> ResultKey: - """Get the key used to access the output of a certain output of this operation + """ + Get the key used to access the output of a certain output of this operation from the output parameter passed to current_output(s) or evaluate_output(s). """ raise NotImplementedError @@ -184,7 +191,8 @@ class Operation(GraphComponent, SignalSourceProvider): def current_output( self, index: int, delays: Optional[DelayMap] = None, prefix: str = "" ) -> Optional[Number]: - """Get the current output at the given index of this operation, if available. + """ + Get the current output at the given index of this operation, if available. The delays parameter will be used for lookup. The prefix parameter will be used as a prefix for the key string when looking for delays. See also: current_outputs, evaluate_output, evaluate_outputs. @@ -212,7 +220,7 @@ class Operation(GraphComponent, SignalSourceProvider): The bits_override parameter specifies a word length override when truncating inputs which ignores the word length specified by the input signal. The truncate parameter specifies whether input truncation should be enabled in the first - place. If set to False, input values will be used driectly without any bit truncation. + place. If set to False, input values will be used directly without any bit truncation. See also: evaluate_outputs, current_output, current_outputs. """ raise NotImplementedError @@ -257,20 +265,24 @@ class Operation(GraphComponent, SignalSourceProvider): @abstractmethod def inputs_required_for_output(self, output_index: int) -> Iterable[int]: - """Get the input indices of all inputs in this operation whose values are required in order to evaluate the output at the given output index. + """ + Get the input indices of all inputs in this operation whose values are + required in order to evaluate the output at the given output index. """ raise NotImplementedError @abstractmethod def truncate_input(self, index: int, value: Number, bits: int) -> Number: - """Truncate the value to be used as input at the given index to a certain bit length. + """ + Truncate the value to be used as input at the given index to a certain bit length. """ raise NotImplementedError @property @abstractmethod def latency(self) -> int: - """Get the latency of the operation, which is the longest time it takes from one of + """ + Get the latency of the operation, which is the longest time it takes from one of the operations inputport to one of the operations outputport. """ raise NotImplementedError @@ -294,7 +306,8 @@ class Operation(GraphComponent, SignalSourceProvider): @abstractmethod def set_latency_offsets(self, latency_offsets: Dict[str, int]) -> None: - """Sets the latency-offsets for the operations ports specified in the latency_offsets dictionary. + """ + Sets the latency-offsets for the operations ports specified in the latency_offsets dictionary. The latency offsets dictionary should be {'in0': 2, 'out1': 4} if you want to set the latency offset for the inport port with index 0 to 2, and the latency offset of the output port with index 1 to 4. """ @@ -405,6 +418,8 @@ class AbstractOperation(Operation, AbstractGraphComponent): if outp.latency_offset is None: outp.latency_offset = latency + self._execution_time = execution_time + @abstractmethod def evaluate(self, *inputs) -> Any: # pylint: disable=arguments-differ """Evaluate the operation and generate a list of output values given a list of input values. @@ -709,7 +724,7 @@ class AbstractOperation(Operation, AbstractGraphComponent): from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Input, Output - inputs = [Input() for i in range(self.input_count)] + inputs = [Input() for _ in range(self.input_count)] try: last_operations = self.evaluate(*inputs) @@ -788,7 +803,9 @@ class AbstractOperation(Operation, AbstractGraphComponent): input_values: Sequence[Number], bits_override: Optional[int] = None, ) -> Sequence[Number]: - """Truncate the values to be used as inputs to the bit lengths specified by the respective signals connected to each input. + """ + Truncate the values to be used as inputs to the bit lengths specified + by the respective signals connected to each input. """ args = [] for i, input_port in enumerate(self.inputs): @@ -938,4 +955,4 @@ class AbstractOperation(Operation, AbstractGraphComponent): ] for k in range(len(self.outputs)) ] - return (input_coords, output_coords) + return input_coords, output_coords diff --git a/b_asic/port.py b/b_asic/port.py index ea7930c2..a7598466 100644 --- a/b_asic/port.py +++ b/b_asic/port.py @@ -147,7 +147,8 @@ class SignalSourceProvider(ABC): class InputPort(AbstractPort): - """Input port. + """ + Input port. May have one or zero signals connected to it. """ @@ -190,7 +191,8 @@ class InputPort(AbstractPort): @property def connected_source(self) -> Optional["OutputPort"]: - """Get the output port that is currently connected to this input port, + """ + Get the output port that is currently connected to this input port, or None if it is unconnected. """ return ( @@ -198,7 +200,8 @@ class InputPort(AbstractPort): ) def connect(self, src: SignalSourceProvider, name: Name = "") -> Signal: - """Connect the provided signal source to this input port by creating a new signal. + """ + Connect the provided signal source to this input port by creating a new signal. Returns the new signal. """ assert ( @@ -208,14 +211,16 @@ class InputPort(AbstractPort): return Signal(source=src.source, destination=self, name=name) def __lshift__(self, src: SignalSourceProvider) -> Signal: - """Overloads the left shift operator to make it connect the provided signal source to this input port. - Returns the new signal. + """ + Overloads the left shift operator to make it connect the provided + signal source to this input port. Returns the new signal. """ return self.connect(src) class OutputPort(AbstractPort, SignalSourceProvider): - """Output port. + """ + Output port. May have zero or more signals connected to it. """ diff --git a/b_asic/signal.py b/b_asic/signal.py index 7e42bb08..b374400e 100644 --- a/b_asic/signal.py +++ b/b_asic/signal.py @@ -80,8 +80,8 @@ class Signal(AbstractGraphComponent): connect to the entered destination InputPort. Also connect the entered destination port to the signal if it hasn't already been connected. - Keywords argments: - - dest: InputPort to connect as destination to the signal. + Keywords arguments: + - dest : InputPort to connect as destination to the signal. """ if dest is not self._destination: self.remove_destination() diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index ddec697f..e351c6cb 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -158,7 +158,7 @@ class SFG(AbstractOperation): self._input_operations.append(new_input_op) self._original_input_signals_to_indices[signal] = input_index - # Setup input operations, starting from indices ater input signals. + # Setup input operations, starting from indices after input signals. if inputs is not None: for input_index, input_op in enumerate(inputs, input_signal_count): assert ( @@ -204,7 +204,6 @@ class SFG(AbstractOperation): ), "Duplicate output operations supplied to SFG constructor." new_output_op = self._add_component_unconnected_copy(output_op) for signal in output_op.input(0).signals: - new_signal = None if signal in self._original_components_to_new: # Signal was already added when setting up inputs. new_signal = self._original_components_to_new[signal] @@ -390,13 +389,15 @@ class SFG(AbstractOperation): """ if len(self.inputs) != len(self.input_operations): raise IndexError( - f"Number of inputs does not match the number of" - f" input_operations in SFG." + f"Number of inputs ({len(self.inputs)}) does not match the" + f" number of input_operations ({len(self.input_operations)})" + " in SFG." ) if len(self.outputs) != len(self.output_operations): raise IndexError( - f"Number of outputs does not match the number of" - f" output_operations SFG." + f"Number of outputs ({len(self.outputs)}) does not match the" + f" number of output_operations ({len(self.output_operations)})" + " in SFG." ) if len(self.input_signals) == 0: return False @@ -419,13 +420,15 @@ class SFG(AbstractOperation): @property def input_operations(self) -> Sequence[Operation]: - """Get the internal input operations in the same order as their respective input ports. + """ + Get the internal input operations in the same order as their respective input ports. """ return self._input_operations @property def output_operations(self) -> Sequence[Operation]: - """Get the internal output operations in the same order as their respective output ports. + """ + Get the internal output operations in the same order as their respective output ports. """ return self._output_operations @@ -498,11 +501,14 @@ class SFG(AbstractOperation): def find_by_type_name( self, type_name: TypeName ) -> Sequence[GraphComponent]: - """Find all components in this graph with the specified type name. + """ + Find all components in this graph with the specified type name. Returns an empty sequence if no components were found. - Keyword arguments: - type_name: The type_name of the desired components. + Parameters + ========== + type_name : TypeName + The TypeName of the desired components. """ reg = f"{type_name}[0-9]+" p = re.compile(reg) @@ -515,8 +521,11 @@ class SFG(AbstractOperation): """Find the graph component with the specified ID. Returns None if the component was not found. - Keyword arguments: - graph_id: Graph ID of the desired component. + Parameters + ========== + + graph_id : GraphID + Graph ID of the desired component. """ return self._components_by_id.get(graph_id, None) @@ -524,8 +533,11 @@ class SFG(AbstractOperation): """Find all graph components with the specified name. Returns an empty sequence if no components were found. - Keyword arguments: - name: Name of the desired component(s) + Parameters + ========== + + name : Name + Name of the desired component(s) """ return self._components_by_name.get(name, []) @@ -536,9 +548,13 @@ class SFG(AbstractOperation): return a sequence of the keys to use when fetching their results from a simulation. - Keyword arguments: - name: Name of the desired component(s) - output_index: The desired output index to get the result from + Parameters + ========== + + name : Name + Name of the desired component(s) + output_index : int, default: 0 + The desired output index to get the result from """ keys = [] for comp in self.find_by_name(name): @@ -553,9 +569,11 @@ class SFG(AbstractOperation): Find and replace all components matching either on GraphID, Type or both. Then return a new deepcopy of the sfg with the replaced component. - Arguments: - component: The new component(s), e.g. Multiplication - graph_id: The GraphID to match the component to replace. + Parameters + ========== + + component : The new component(s), e.g. Multiplication + graph_id : The GraphID to match the component to replace. """ sfg_copy = self() # Copy to not mess with this SFG. @@ -586,13 +604,17 @@ class SFG(AbstractOperation): def insert_operation( self, component: Operation, output_comp_id: GraphID ) -> Optional["SFG"]: - """Insert an operation in the SFG after a given source operation. - The source operation output count must match the input count of the operation as well as the output + """ + Insert an operation in the SFG after a given source operation. + The source operation output count must match the input count of the operation + as well as the output. Then return a new deepcopy of the sfg with the inserted component. - Arguments: - component: The new component, e.g. Multiplication. - output_comp_id: The source operation GraphID to connect from. + Parameters + ========== + + component : The new component, e.g. Multiplication. + output_comp_id : The source operation GraphID to connect from. """ # Preserve the original SFG by creating a copy. @@ -621,7 +643,7 @@ class SFG(AbstractOperation): # Recreate the newly coupled SFG so that all attributes are correct. return sfg_copy() - def remove_operation(self, operation_id: GraphID) -> "SFG": + def remove_operation(self, operation_id: GraphID) -> Union["SFG", None]: """Returns a version of the SFG where the operation with the specified GraphID removed. The operation has to have the same amount of input- and output ports or a ValueError will be raised. If no operation with the entered operation_id is found then returns None and does nothing. @@ -955,7 +977,6 @@ class SFG(AbstractOperation): while op_stack: original_op = op_stack.pop() # Add or get the new copy of the operation. - new_op = None if original_op not in self._original_components_to_new: new_op = self._add_component_unconnected_copy(original_op) self._components_dfs_order.append(new_op) diff --git a/b_asic/simulation.py b/b_asic/simulation.py index 99d470c7..4017328c 100644 --- a/b_asic/simulation.py +++ b/b_asic/simulation.py @@ -1,4 +1,5 @@ -"""B-ASIC Simulation Module. +""" +B-ASIC Simulation Module. Contains a class for simulating the result of an SFG given a set of input values. """ @@ -35,7 +36,8 @@ InputProvider = Union[Number, Sequence[Number], InputFunction] class Simulation: - """Simulation of an SFG. + """ + Simulation of an SFG. Use FastSimulation (from the C++ extension module) for a more effective simulation when running many iterations. @@ -68,7 +70,9 @@ class Simulation: self.set_inputs(input_providers) def set_input(self, index: int, input_provider: InputProvider) -> None: - """Set the input function used to get values for the specific input at the given index to the internal SFG. + """ + Set the input function used to get values for the specific input at the + given index to the internal SFG. """ if index < 0 or index >= len(self._input_functions): raise IndexError( @@ -92,7 +96,8 @@ class Simulation: def set_inputs( self, input_providers: Sequence[Optional[InputProvider]] ) -> None: - """Set the input functions used to get values for the inputs to the internal SFG. + """ + Set the input functions used to get values for the inputs to the internal SFG. """ if len(input_providers) != self._sfg.input_count: raise ValueError( @@ -109,7 +114,8 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run one iteration of the simulation and return the resulting output values. + """ + Run one iteration of the simulation and return the resulting output values. """ return self.run_for(1, save_results, bits_override, truncate) @@ -120,7 +126,8 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run the simulation until its iteration is greater than or equal to the given iteration + """ + Run the simulation until its iteration is greater than or equal to the given iteration and return the output values of the last iteration. """ result = [] @@ -151,7 +158,9 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run a given number of iterations of the simulation and return the output values of the last iteration. + """ + Run a given number of iterations of the simulation and return the output + values of the last iteration. """ return self.run_until( self._iteration + iterations, save_results, bits_override, truncate @@ -163,7 +172,9 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run the simulation until the end of its input arrays and return the output values of the last iteration. + """ + Run the simulation until the end of its input arrays and return the output + values of the last iteration. """ if self._input_length is None: raise IndexError("Tried to run unlimited simulation") @@ -178,10 +189,14 @@ class Simulation: @property def results(self) -> ResultArrayMap: - """Get a mapping from result keys to numpy arrays containing all results, including intermediate values, - calculated for each iteration up until now that was run with save_results enabled. + """ + Get a mapping from result keys to numpy arrays containing all results, including + intermediate values, calculated for each iteration up until now that was run with + save_results enabled. The mapping is indexed using the key() method of Operation with the appropriate output index. - Example result after 3 iterations: {"c1": [3, 6, 7], "c2": [4, 5, 5], "bfly1.0": [7, 0, 0], "bfly1.1": [-1, 0, 2], "0": [7, -2, -1]} + Example result after 3 iterations:: + + {"c1": [3, 6, 7], "c2": [4, 5, 5], "bfly1.0": [7, 0, 0], "bfly1.1": [-1, 0, 2], "0": [7, -2, -1]} """ return {key: np.array(value) for key, value in self._results.items()} @@ -190,6 +205,7 @@ class Simulation: self._results.clear() def clear_state(self) -> None: - """Clear all current state of the simulation, except for the results and iteration. + """ + Clear all current state of the simulation, except for the results and iteration. """ self._delays.clear() diff --git a/b_asic/special_operations.py b/b_asic/special_operations.py index bcd462d7..02a80dac 100644 --- a/b_asic/special_operations.py +++ b/b_asic/special_operations.py @@ -1,4 +1,5 @@ -"""B-ASIC Special Operations Module. +""" +B-ASIC Special Operations Module. Contains operations with special purposes that may be treated differently from normal operations in an SFG. @@ -19,7 +20,8 @@ from b_asic.port import SignalSourceProvider class Input(AbstractOperation): - """Input operation. + """ + Input operation. Marks an input port to an SFG. Its value will be updated on each iteration when simulating the SFG. @@ -83,7 +85,8 @@ class Input(AbstractOperation): class Output(AbstractOperation): - """Output operation. + """ + Output operation. Marks an output port to an SFG. The SFG will forward its input to the corresponding output signal @@ -126,7 +129,8 @@ class Output(AbstractOperation): class Delay(AbstractOperation): - """Unit delay operation. + """ + Unit delay operation. Represents one unit of delay in a circuit, typically a clock cycle. Can be thought of as a register or a D flip-flop. -- GitLab