Skip to content
Snippets Groups Projects
Commit bb6605f0 authored by Angus Lothian's avatar Angus Lothian :dark_sunglasses:
Browse files

Change usage of type_name from property to function and add function for...

Change usage of type_name from property to function and add function for returning all components of the entered type
parent 16972202
No related branches found
No related tags found
2 merge requests!67WIP: B-ASIC version 1.0.0 hotfix,!65B-ASIC version 1.0.0
......@@ -5,9 +5,7 @@ TODO: More info.
from b_asic.core_operations import *
from b_asic.graph_component import *
from b_asic.operation import *
from b_asic.precedence_chart import *
from b_asic.port import *
from b_asic.schema import *
from b_asic.signal_flow_graph import *
from b_asic.signal import *
from b_asic.simulation import *
......
......@@ -18,16 +18,16 @@ class Constant(AbstractOperation):
"""
def __init__(self, value: Number = 0, name: Name = ""):
super().__init__(input_count = 0, output_count = 1, name = name)
super().__init__(input_count=0, output_count=1, name=name)
self.set_param("value", value)
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "c"
def evaluate(self):
return self.param("value")
@property
def value(self) -> Number:
"""Get the constant value of this operation."""
......@@ -45,10 +45,11 @@ class Addition(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=1,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "add"
def evaluate(self, a, b):
......@@ -61,10 +62,11 @@ class Subtraction(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=1,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "sub"
def evaluate(self, a, b):
......@@ -77,10 +79,11 @@ class Multiplication(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=1,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "mul"
def evaluate(self, a, b):
......@@ -93,10 +96,11 @@ class Division(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=1,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "div"
def evaluate(self, a, b):
......@@ -109,10 +113,11 @@ class Min(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=1,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "min"
def evaluate(self, a, b):
......@@ -127,10 +132,11 @@ class Max(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 1, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=1,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "max"
def evaluate(self, a, b):
......@@ -145,10 +151,11 @@ class SquareRoot(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
super().__init__(input_count=1, output_count=1,
name=name, input_sources=[src0])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "sqrt"
def evaluate(self, a):
......@@ -161,10 +168,11 @@ class ComplexConjugate(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
super().__init__(input_count=1, output_count=1,
name=name, input_sources=[src0])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "conj"
def evaluate(self, a):
......@@ -177,10 +185,11 @@ class Absolute(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
super().__init__(input_count=1, output_count=1,
name=name, input_sources=[src0])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "abs"
def evaluate(self, a):
......@@ -193,11 +202,12 @@ class ConstantMultiplication(AbstractOperation):
"""
def __init__(self, value: Number = 0, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
super().__init__(input_count=1, output_count=1,
name=name, input_sources=[src0])
self.set_param("value", value)
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "cmul"
def evaluate(self, a):
......@@ -221,10 +231,11 @@ class Butterfly(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 2, output_count = 2, name = name, input_sources = [src0, src1])
super().__init__(input_count=2, output_count=2,
name=name, input_sources=[src0, src1])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "bfly"
def evaluate(self, a, b):
......
......@@ -20,9 +20,9 @@ class GraphComponent(ABC):
TODO: More info.
"""
@property
@classmethod
@abstractmethod
def type_name(self) -> TypeName:
def type_name(cls) -> TypeName:
"""Get the type name of this graph component"""
raise NotImplementedError
......@@ -112,7 +112,7 @@ class AbstractGraphComponent(GraphComponent):
@name.setter
def name(self, name: Name) -> None:
self._name = name
@property
def graph_id(self) -> GraphID:
return self._graph_id
......@@ -136,7 +136,7 @@ class AbstractGraphComponent(GraphComponent):
new_component.name = copy(self.name)
new_component.graph_id = copy(self.graph_id)
for name, value in self.params.items():
new_component.set_param(copy(name), deepcopy(value)) # pylint: disable=no-member
new_component.set_param(copy(name), deepcopy(value)) # pylint: disable=no-member
return new_component
def traverse(self) -> Generator[GraphComponent, None, None]:
......@@ -149,4 +149,4 @@ class AbstractGraphComponent(GraphComponent):
for neighbor in component.neighbors:
if neighbor not in visited:
visited.add(neighbor)
fontier.append(neighbor)
\ No newline at end of file
fontier.append(neighbor)
......@@ -14,11 +14,12 @@ from b_asic.graph_component import GraphComponent, AbstractGraphComponent, Name
from b_asic.port import SignalSourceProvider, InputPort, OutputPort
from b_asic.signal import Signal
ResultKey = NewType("ResultKey", str)
ResultMap = Mapping[ResultKey, Optional[Number]]
MutableResultMap = MutableMapping[ResultKey, Optional[Number]]
RegisterMap = Mapping[ResultKey, Number]
MutableRegisterMap = MutableMapping[ResultKey, Number]
OutputKey = NewType("OutputKey", str)
OutputMap = Mapping[OutputKey, Optional[Number]]
MutableOutputMap = MutableMapping[OutputKey, Optional[Number]]
RegisterMap = Mapping[OutputKey, Number]
MutableRegisterMap = MutableMapping[OutputKey, Number]
class Operation(GraphComponent, SignalSourceProvider):
"""Operation interface.
......@@ -134,9 +135,9 @@ class Operation(GraphComponent, SignalSourceProvider):
raise NotImplementedError
@abstractmethod
def key(self, index: int, prefix: str = "") -> ResultKey:
"""Get the key used to access the result of a certain output of this operation
from the results parameter passed to current_output(s) or evaluate_output(s).
def key(self, index: int, prefix: str = "") -> OutputKey:
"""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
......@@ -150,7 +151,7 @@ class Operation(GraphComponent, SignalSourceProvider):
raise NotImplementedError
@abstractmethod
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableOutputMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
"""Evaluate the output at the given index of this operation with the given input values.
The results parameter will be used to store any results (including intermediate results) for caching.
The registers parameter will be used to get the current value of any intermediate registers that are encountered, and be updated with their new values.
......@@ -167,7 +168,7 @@ class Operation(GraphComponent, SignalSourceProvider):
raise NotImplementedError
@abstractmethod
def evaluate_outputs(self, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Sequence[Number]:
def evaluate_outputs(self, input_values: Sequence[Number], results: Optional[MutableOutputMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Sequence[Number]:
"""Evaluate all outputs of this operation given the input values.
See evaluate_output for more information.
"""
......@@ -196,55 +197,65 @@ class AbstractOperation(Operation, AbstractGraphComponent):
def __init__(self, input_count: int, output_count: int, name: Name = "", input_sources: Optional[Sequence[Optional[SignalSourceProvider]]] = None):
super().__init__(name)
self._input_ports = [InputPort(self, i) for i in range(input_count)] # Allocate input ports.
self._output_ports = [OutputPort(self, i) for i in range(output_count)] # Allocate output ports.
self._input_ports = [InputPort(self, i) for i in range(input_count)]
self._output_ports = [OutputPort(self, i) for i in range(output_count)]
# Connect given input sources, if any.
if input_sources is not None:
source_count = len(input_sources)
if source_count != input_count:
raise ValueError(f"Wrong number of input sources supplied to Operation (expected {input_count}, got {source_count})")
raise ValueError(
f"Wrong number of input sources supplied to Operation (expected {input_count}, got {source_count})")
for i, src in enumerate(input_sources):
if src is not None:
self._input_ports[i].connect(src.source)
@abstractmethod
def evaluate(self, *inputs) -> Any: # pylint: disable=arguments-differ
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."""
raise NotImplementedError
def __add__(self, src: Union[SignalSourceProvider, Number]) -> "Addition":
from b_asic.core_operations import Constant, Addition # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Constant, Addition
return Addition(self, Constant(src) if isinstance(src, Number) else src)
def __radd__(self, src: Union[SignalSourceProvider, Number]) -> "Addition":
from b_asic.core_operations import Constant, Addition # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Constant, Addition
return Addition(Constant(src) if isinstance(src, Number) else src, self)
def __sub__(self, src: Union[SignalSourceProvider, Number]) -> "Subtraction":
from b_asic.core_operations import Constant, Subtraction # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Constant, Subtraction
return Subtraction(self, Constant(src) if isinstance(src, Number) else src)
def __rsub__(self, src: Union[SignalSourceProvider, Number]) -> "Subtraction":
from b_asic.core_operations import Constant, Subtraction # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Constant, Subtraction
return Subtraction(Constant(src) if isinstance(src, Number) else src, self)
def __mul__(self, src: Union[SignalSourceProvider, Number]) -> "Union[Multiplication, ConstantMultiplication]":
from b_asic.core_operations import Multiplication, ConstantMultiplication # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Multiplication, ConstantMultiplication
return ConstantMultiplication(src, self) if isinstance(src, Number) else Multiplication(self, src)
def __rmul__(self, src: Union[SignalSourceProvider, Number]) -> "Union[Multiplication, ConstantMultiplication]":
from b_asic.core_operations import Multiplication, ConstantMultiplication # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Multiplication, ConstantMultiplication
return ConstantMultiplication(src, self) if isinstance(src, Number) else Multiplication(src, self)
def __truediv__(self, src: Union[SignalSourceProvider, Number]) -> "Division":
from b_asic.core_operations import Constant, Division # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Constant, Division
return Division(self, Constant(src) if isinstance(src, Number) else src)
def __rtruediv__(self, src: Union[SignalSourceProvider, Number]) -> "Division":
from b_asic.core_operations import Constant, Division # Import here to avoid circular imports.
# Import here to avoid circular imports.
from b_asic.core_operations import Constant, Division
return Division(Constant(src) if isinstance(src, Number) else src, self)
@property
def input_count(self) -> int:
return len(self._input_ports)
......@@ -282,8 +293,8 @@ class AbstractOperation(Operation, AbstractGraphComponent):
for s in p.signals:
result.append(s)
return result
def key(self, index: int, prefix: str = "") -> ResultKey:
def key(self, index: int, prefix: str = "") -> OutputKey:
key = prefix
if self.output_count != 1:
if key:
......@@ -292,15 +303,17 @@ class AbstractOperation(Operation, AbstractGraphComponent):
elif not key:
key = str(index)
return key
def current_output(self, index: int, registers: Optional[RegisterMap] = None, prefix: str = "") -> Optional[Number]:
return None
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableOutputMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
if index < 0 or index >= self.output_count:
raise IndexError(f"Output index out of range (expected 0-{self.output_count - 1}, got {index})")
raise IndexError(
f"Output index out of range (expected 0-{self.output_count - 1}, got {index})")
if len(input_values) != self.input_count:
raise ValueError(f"Wrong number of input values supplied to operation (expected {self.input_count}, got {len(input_values)})")
raise ValueError(
f"Wrong number of input values supplied to operation (expected {self.input_count}, got {len(input_values)})")
if results is None:
results = {}
if registers is None:
......@@ -309,13 +322,16 @@ class AbstractOperation(Operation, AbstractGraphComponent):
values = self.evaluate(*self.truncate_inputs(input_values))
if isinstance(values, collections.abc.Sequence):
if len(values) != self.output_count:
raise RuntimeError(f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got {len(values)})")
raise RuntimeError(
f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got {len(values)})")
elif isinstance(values, Number):
if self.output_count != 1:
raise RuntimeError(f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got 1)")
raise RuntimeError(
f"Operation evaluated to incorrect number of outputs (expected {self.output_count}, got 1)")
values = (values,)
else:
raise RuntimeError(f"Operation evaluated to invalid type (expected Sequence/Number, got {values.__class__.__name__})")
raise RuntimeError(
f"Operation evaluated to invalid type (expected Sequence/Number, got {values.__class__.__name__})")
if self.output_count == 1:
results[self.key(index, prefix)] = values[index]
......@@ -327,7 +343,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
def current_outputs(self, registers: Optional[RegisterMap] = None, prefix: str = "") -> Sequence[Optional[Number]]:
return [self.current_output(i, registers, prefix) for i in range(self.output_count)]
def evaluate_outputs(self, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Sequence[Number]:
def evaluate_outputs(self, input_values: Sequence[Number], results: Optional[MutableOutputMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Sequence[Number]:
return [self.evaluate_output(i, input_values, results, registers, prefix) for i in range(self.output_count)]
def split(self) -> Iterable[Operation]:
......@@ -348,7 +364,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
def inputs_required_for_output(self, output_index: int) -> Iterable[int]:
if output_index < 0 or output_index >= self.output_count:
raise IndexError(f"Output index out of range (expected 0-{self.output_count - 1}, got {output_index})")
return [i for i in range(self.input_count)] # By default, assume each output depends on all inputs.
return [i for i in range(self.input_count)] # By default, assume each output depends on all inputs.
@property
def neighbors(self) -> Iterable[GraphComponent]:
......@@ -361,7 +377,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
raise TypeError(
f"{self.__class__.__name__} cannot be used as an input source because it has {diff} than 1 output")
return self.output(0)
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."""
n = value
......@@ -379,7 +395,8 @@ class AbstractOperation(Operation, AbstractGraphComponent):
args.append(input_values[i])
else:
if isinstance(input_values[i], complex):
raise TypeError("Complex value cannot be truncated to {bits} bits as requested by the signal connected to input #{i}")
raise TypeError(
"Complex value cannot be truncated to {bits} bits as requested by the signal connected to input #{i}")
args.append(self.truncate_input(i, input_values[i], bits))
else:
args.append(input_values[i])
......
......@@ -5,7 +5,7 @@ TODO: More info.
from abc import ABC, abstractmethod
from copy import copy
from typing import NewType, Optional, List, Iterable, TYPE_CHECKING
from typing import Optional, List, Iterable, TYPE_CHECKING
from b_asic.signal import Signal
from b_asic.graph_component import Name
......
"""@package docstring
B-ASIC Precedence Chart Module.
TODO: More info.
"""
from b_asic.signal_flow_graph import SFG
class PrecedenceChart:
"""Precedence chart constructed from a signal flow graph.
TODO: More info.
"""
sfg: SFG
# TODO: More members.
def __init__(self, sfg: SFG):
self.sfg = sfg
# TODO: Implement.
# TODO: More stuff.
"""@package docstring
B-ASIC Schema Module.
TODO: More info.
"""
from b_asic.precedence_chart import PrecedenceChart
class Schema:
"""Schema constructed from a precedence chart.
TODO: More info.
"""
pc: PrecedenceChart
# TODO: More members.
def __init__(self, pc: PrecedenceChart):
self.pc = pc
# TODO: Implement.
# TODO: More stuff.
......@@ -25,14 +25,14 @@ class Signal(AbstractGraphComponent):
self.set_destination(destination)
self.set_param("bits", bits)
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "s"
@property
def neighbors(self) -> Iterable[GraphComponent]:
return [p.operation for p in [self.source, self.destination] if p is not None]
@property
def source(self) -> Optional["OutputPort"]:
"""Return the source OutputPort of the signal."""
......@@ -103,5 +103,6 @@ class Signal(AbstractGraphComponent):
def bits(self, bits: Optional[int]) -> None:
"""Set the number of bits that operations using this signal as an input should truncate received values to.
None = unlimited."""
assert bits is None or (isinstance(bits, int) and bits >= 0), "Bits must be non-negative."
self.set_param("bits", bits)
\ No newline at end of file
assert bits is None or (isinstance(bits, int)
and bits >= 0), "Bits must be non-negative."
self.set_param("bits", bits)
This diff is collapsed.
......@@ -7,7 +7,7 @@ from collections import defaultdict
from numbers import Number
from typing import List, Dict, DefaultDict, Callable, Sequence, Mapping, Union, Optional
from b_asic.operation import ResultKey, ResultMap
from b_asic.operation import OutputKey, OutputMap
from b_asic.signal_flow_graph import SFG
......@@ -33,7 +33,8 @@ class Simulation:
self._results = defaultdict(dict)
self._registers = {}
self._iteration = 0
self._input_functions = [lambda _: 0 for _ in range(self._sfg.input_count)]
self._input_functions = [
lambda _: 0 for _ in range(self._sfg.input_count)]
self._current_input_values = [0 for _ in range(self._sfg.input_count)]
self._latest_output_values = [0 for _ in range(self._sfg.output_count)]
self._save_results = save_results
......@@ -43,7 +44,8 @@ class Simulation:
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."""
if index < 0 or index >= len(self._input_functions):
raise IndexError(f"Input index out of range (expected 0-{len(self._input_functions) - 1}, got {index})")
raise IndexError(
f"Input index out of range (expected 0-{len(self._input_functions) - 1}, got {index})")
if callable(input_provider):
self._input_functions[index] = input_provider
elif isinstance(input_provider, Number):
......@@ -54,7 +56,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."""
if len(input_providers) != self._sfg.input_count:
raise ValueError(f"Wrong number of inputs supplied to simulation (expected {self._sfg.input_count}, got {len(input_providers)})")
raise ValueError(
f"Wrong number of inputs supplied to simulation (expected {self._sfg.input_count}, got {len(input_providers)})")
self._input_functions = [None for _ in range(self._sfg.input_count)]
for index, input_provider in enumerate(input_providers):
if input_provider is not None:
......@@ -78,8 +81,10 @@ class Simulation:
and return the resulting output values.
"""
while self._iteration < iteration:
self._current_input_values = [self._input_functions[i](self._iteration) for i in range(self._sfg.input_count)]
self._latest_output_values = self._sfg.evaluate_outputs(self._current_input_values, self._results[self._iteration], self._registers)
self._current_input_values = [self._input_functions[i](
self._iteration) for i in range(self._sfg.input_count)]
self._latest_output_values = self._sfg.evaluate_outputs(
self._current_input_values, self._results[self._iteration], self._registers)
if not self._save_results:
del self._results[self.iteration]
self._iteration += 1
......@@ -95,7 +100,7 @@ class Simulation:
return self._iteration
@property
def results(self) -> Mapping[int, ResultMap]:
def results(self) -> Mapping[int, OutputMap]:
"""Get a mapping of all results, including intermediate values, calculated for each iteration up until now.
The outer mapping maps from iteration number to value mapping. The value mapping maps output port identifiers to values.
Example: {0: {"c1": 3, "c2": 4, "bfly1.0": 7, "bfly1.1": -1, "0": 7}}
......@@ -110,4 +115,4 @@ class Simulation:
"""Clear all current state of the simulation, except for the results and iteration."""
self._registers.clear()
self._current_input_values = [0 for _ in range(self._sfg.input_count)]
self._latest_output_values = [0 for _ in range(self._sfg.output_count)]
\ No newline at end of file
self._latest_output_values = [0 for _ in range(self._sfg.output_count)]
......@@ -6,7 +6,7 @@ TODO: More info.
from numbers import Number
from typing import Optional, Sequence
from b_asic.operation import AbstractOperation, ResultKey, RegisterMap, MutableResultMap, MutableRegisterMap
from b_asic.operation import AbstractOperation, OutputKey, RegisterMap, MutableOutputMap, MutableRegisterMap
from b_asic.graph_component import Name, TypeName
from b_asic.port import SignalSourceProvider
......@@ -17,13 +17,13 @@ class Input(AbstractOperation):
"""
def __init__(self, name: Name = ""):
super().__init__(input_count = 0, output_count = 1, name = name)
super().__init__(input_count=0, output_count=1, name=name)
self.set_param("value", 0)
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "in"
def evaluate(self):
return self.param("value")
......@@ -44,10 +44,11 @@ class Output(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""):
super().__init__(input_count = 1, output_count = 0, name = name, input_sources = [src0])
super().__init__(input_count=1, output_count=0,
name=name, input_sources=[src0])
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "out"
def evaluate(self, _):
......@@ -60,11 +61,12 @@ class Register(AbstractOperation):
"""
def __init__(self, src0: Optional[SignalSourceProvider] = None, initial_value: Number = 0, name: Name = ""):
super().__init__(input_count = 1, output_count = 1, name = name, input_sources = [src0])
super().__init__(input_count=1, output_count=1,
name=name, input_sources=[src0])
self.set_param("initial_value", initial_value)
@property
def type_name(self) -> TypeName:
@classmethod
def type_name(cls) -> TypeName:
return "reg"
def evaluate(self, a):
......@@ -74,13 +76,15 @@ class Register(AbstractOperation):
if registers is not None:
return registers.get(self.key(index, prefix), self.param("initial_value"))
return self.param("initial_value")
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableResultMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
def evaluate_output(self, index: int, input_values: Sequence[Number], results: Optional[MutableOutputMap] = None, registers: Optional[MutableRegisterMap] = None, prefix: str = "") -> Number:
if index != 0:
raise IndexError(f"Output index out of range (expected 0-0, got {index})")
raise IndexError(
f"Output index out of range (expected 0-0, got {index})")
if len(input_values) != 1:
raise ValueError(f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
raise ValueError(
f"Wrong number of inputs supplied to SFG for evaluation (expected 1, got {len(input_values)})")
key = self.key(index, prefix)
value = self.param("initial_value")
if registers is not None:
......@@ -88,4 +92,4 @@ class Register(AbstractOperation):
registers[key] = self.truncate_inputs(input_values)[0]
if results is not None:
results[key] = value
return value
\ No newline at end of file
return value
......@@ -164,3 +164,14 @@ class TestButterfly:
test_operation = Butterfly()
assert test_operation.evaluate_output(0, [2+1j, 3-2j]) == 5-1j
assert test_operation.evaluate_output(1, [2+1j, 3-2j]) == -1+3j
class TestDepends:
def test_depends_addition(self):
add1 = Addition()
assert set(add1.inputs_required_for_output(0)) == {0, 1}
def test_depends_butterfly(self):
bfly1 = Butterfly()
assert set(bfly1.inputs_required_for_output(0)) == {0, 1}
assert set(bfly1.inputs_required_for_output(1)) == {0, 1}
from b_asic import Addition, Butterfly
class TestDepends:
def test_depends_addition(self):
add1 = Addition()
assert set(add1.inputs_required_for_output(0)) == {0, 1}
def test_depends_butterfly(self):
bfly1 = Butterfly()
assert set(bfly1.inputs_required_for_output(0)) == {0, 1}
assert set(bfly1.inputs_required_for_output(1)) == {0, 1}
def test_depends_sfg(self, sfg_two_inputs_two_outputs):
assert set(sfg_two_inputs_two_outputs.inputs_required_for_output(0)) == {0, 1}
assert set(sfg_two_inputs_two_outputs.inputs_required_for_output(1)) == {0, 1}
def test_depends_sfg_independent(self, sfg_two_inputs_two_outputs_independent):
assert set(sfg_two_inputs_two_outputs_independent.inputs_required_for_output(0)) == {0}
assert set(sfg_two_inputs_two_outputs_independent.inputs_required_for_output(1)) == {1}
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment