diff --git a/b_asic/resources.py b/b_asic/resources.py index b33de2eb4c40aaebfd89fc8207eb5cf922866e98..f5ab1c76f8d611b111f3c57f61fee9c751caba0a 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -1006,6 +1006,9 @@ class ProcessCollection: fig.savefig(f, format="svg") # type: ignore return f.getvalue() + # SVG is valid HTML. This is useful for e.g. sphinx-gallery + _repr_html_ = _repr_svg_ + def __repr__(self): return ( f"ProcessCollection({self._collection}, {self._schedule_time}," diff --git a/b_asic/schedule.py b/b_asic/schedule.py index d35fcca72741f2489d28a1b8150cdc75863cc533..242a9525e23b19ffa6777bc7043ea185d634032b 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -1064,3 +1064,6 @@ class Schedule: fig.savefig(buffer, format="svg") return buffer.getvalue() + + # SVG is valid HTML. This is useful for e.g. sphinx-gallery + _repr_html_ = _repr_svg_ diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index 43c6d085a6ebf05ea6c12061319693442dc1f40e..e9e47845955c118aaecd843c8cfb2f799850ba05 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -1490,6 +1490,14 @@ class SFG(AbstractOperation): def _repr_png_(self): return self.sfg_digraph()._repr_mimebundle_(include=["image/png"])["image/png"] + def _repr_svg_(self): + return self.sfg_digraph()._repr_mimebundle_(include=["image/svg+xml"])[ + "image/svg+xml" + ] + + # SVG is valid HTML. This is useful for e.g. sphinx-gallery + _repr_html_ = _repr_svg_ + def show( self, fmt: Optional[str] = None, diff --git a/examples/connectmultiplesfgs.py b/examples/connectmultiplesfgs.py index 2a0786010f3efa9b90592362a1320f8eb6c1bbf1..4ab897b43ab189b941002282720cd69f52a54568 100644 --- a/examples/connectmultiplesfgs.py +++ b/examples/connectmultiplesfgs.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ ======================== Connecting multiple SFGs @@ -14,46 +12,40 @@ SFGs but the operations of these. To do this, one will have to use the method :func:`~b_asic.signal_flow_graph.SFG.connect_external_signals_to_components`. This example illustrates how it can be done. +""" -.. jupyter-execute:: - - from b_asic.sfg_generators import wdf_allpass - from b_asic.signal_flow_graph import SFG - from b_asic.special_operations import Input, Output - - # Generate allpass branches for fifth-ordet LWDF filter - allpass1 = wdf_allpass([0.2, 0.5]) - allpass2 = wdf_allpass([-0.5, 0.2, 0.5]) - - in_lwdf = Input() - allpass1 << in_lwdf - allpass2 << in_lwdf - out_lwdf = Output((allpass1 + allpass2) * 0.5) - - # Create SFG of LWDF with two internal SFGs - sfg_with_sfgs = SFG( - [in_lwdf], [out_lwdf], name="LWDF with separate internals SFGs for allpass branches" - ) - -The resulting SFG looks like: +from b_asic.sfg_generators import wdf_allpass +from b_asic.signal_flow_graph import SFG +from b_asic.special_operations import Input, Output -.. jupyter-execute:: +# Generate allpass branches for fifth-ordet LWDF filter +allpass1 = wdf_allpass([0.2, 0.5]) +allpass2 = wdf_allpass([-0.5, 0.2, 0.5]) - sfg_with_sfgs +in_lwdf = Input() +allpass1 << in_lwdf +allpass2 << in_lwdf +out_lwdf = Output((allpass1 + allpass2) * 0.5) +# Create SFG of LWDF with two internal SFGs +sfg_with_sfgs = SFG( + [in_lwdf], [out_lwdf], name="LWDF with separate internals SFGs for allpass branches" +) -Now, to create a LWDF where the SFGs are flattened. Note that the original SFGs -``allpass1`` and ``allpass2`` currently cannot be printed etc after this operation. +# %% +# The resulting SFG looks like: -.. jupyter-execute:: +sfg_with_sfgs - allpass1.connect_external_signals_to_components() - allpass2.connect_external_signals_to_components() - flattened_sfg = SFG([in_lwdf], [out_lwdf], name="Flattened LWDF") +# %% +# Now, to create a LWDF where the SFGs are flattened. Note that the original SFGs +# ``allpass1`` and ``allpass2`` currently cannot be printed etc after this operation. -Resulting in: +allpass1.connect_external_signals_to_components() +allpass2.connect_external_signals_to_components() +flattened_sfg = SFG([in_lwdf], [out_lwdf], name="Flattened LWDF") -.. jupyter-execute:: +# %% +# Resulting in: - flattened_sfg -""" +flattened_sfg diff --git a/examples/firstorderiirfilter.py b/examples/firstorderiirfilter.py index 16baebebe0353cb4205e4ce3afb1b209f9cebe00..9f02c0910d71d85af677006e00c7543a2ae4c115 100644 --- a/examples/firstorderiirfilter.py +++ b/examples/firstorderiirfilter.py @@ -7,11 +7,12 @@ In this example, a direct form first-order IIR filter is designed. First, we need to import the operations that will be used in the example: """ -from b_asic.core_operations import Addition, ConstantMultiplication +from b_asic.core_operations import ConstantMultiplication from b_asic.special_operations import Delay, Input, Output # %% -# Then, we continue by defining the input and delay element, which we can optionally name. +# Then, we continue by defining the input and delay element, which we can optionally +# name. input = Input(name="My input") delay = Delay(name="The only delay") @@ -27,15 +28,17 @@ a1 = ConstantMultiplication(0.5, delay) first_addition = a1 + input # %% -# Or by creating them, but connecting the input later. Each operation has a function :func:`~b_asic.operation.Operation.input` -# that is used to access a specific input (or output, by using :func:`~b_asic.operation.Operation.output`). +# Or by creating them, but connecting the input later. Each operation has a function +# :func:`~b_asic.operation.Operation.input`that is used to access a specific input +# (or output, by using :func:`~b_asic.operation.Operation.output`). b1 = ConstantMultiplication(0.7) b1.input(0).connect(delay) # %% -# The latter is useful when there is not a single order to create the signal flow graph, e.g., for recursive algorithms. -# In this example, we could not connect the output of the delay as that was not yet available. +# The latter is useful when there is not a single order to create the signal flow +# graph, e.g., for recursive algorithms. In this example, we could not connect the +# output of the delay as that was not yet available. # # There is also a shorthand form to connect signals using the ``<<`` operator: @@ -47,47 +50,27 @@ delay << first_addition output = Output(b1 + first_addition) # %% -# Now, we should create a signal flow graph, but first it must be imported (normally, this should go at the top of the file). +# Now, we should create a signal flow graph, but first it must be imported (normally, +# this should go at the top of the file). -from b_asic.signal_flow_graph import SFG +from b_asic.signal_flow_graph import SFG # noqa: E402 # %% -# The signal flow graph is defined by its inputs and outputs, so these must be provided. As, in general, there can be -# multiple inputs and outputs, there should be provided as a list or a tuple. +# The signal flow graph is defined by its inputs and outputs, so these must be +# provided. As, in general, there can be multiple inputs and outputs, there should +# be provided as a list or a tuple. firstorderiir = SFG([input], [output]) # %% -# If this is executed in an enriched terminal, such as a Jupyter Notebook, Jupyter QtConsole, or Spyder, just typing -# the variable name will return a graphical representation of the signal flow graph. +# If this is executed in an enriched terminal, such as a Jupyter Notebook, Jupyter +# QtConsole, or Spyder, just typing the variable name will return a graphical +# representation of the signal flow graph. firstorderiir # %% -# This will look something like -# -# .. graphviz:: -# -# digraph { -# rankdir=LR -# in1 [shape=cds] -# in1 -> add1 -# out1 [shape=cds] -# add2 -> out1 -# add1 [shape=ellipse] -# cmul1 -> add1 -# cmul1 [shape=ellipse] -# add1 -> t1 -# t1 [shape=square] -# add1 -> add2 -# add2 [shape=ellipse] -# cmul2 -> add2 -# cmul2 [shape=ellipse] -# t1 -> cmul2 -# t1 -> cmul1 -# } -# -# For now, we can print the precendence relations of the SFG +# For now, we can print the precedence relations of the SFG firstorderiir.print_precedence_graph() # %% @@ -145,21 +128,23 @@ firstorderiir.print_precedence_graph() # add2 [label=add2 shape=ellipse] # } # -# As seen, each operation has an id, in addition to the optional name. This can be used to access the operation. -# For example, +# As seen, each operation has an id, in addition to the optional name. +# This can be used to access the operation. For example, firstorderiir.find_by_id('cmul1') # %% -# Note that this operation differs from ``a1`` defined above as the operations are copied and recreated once inserted -# into a signal flow graph. +# Note that this operation differs from ``a1`` defined above as the operations are +# copied and recreated once inserted into a signal flow graph. # -# The signal flow graph can also be simulated. For this, we must import :class:`.Simulation`. +# The signal flow graph can also be simulated. For this, we must import +# :class:`.Simulation`. -from b_asic.simulation import Simulation +from b_asic.simulation import Simulation # noqa: E402 # %% -# The :class:`.Simulation` class require that we provide inputs. These can either be arrays of values or we can use functions -# that provides the values when provided a time index. +# The :class:`.Simulation` class require that we provide inputs. These can either be +# arrays of values or we can use functions that provides the values when provided a +# time index. # # Let us create a simulation that simulates a short impulse response: @@ -171,18 +156,18 @@ sim = Simulation(firstorderiir, [[1, 0, 0, 0, 0]]) sim.run() # %% -# The returned value is the output after the final iteration. However, we may often be interested in the results from -# the whole simulation. -# The results from the simulation, which is a dictionary of all the nodes in the signal flow graph, -# can be obtained as +# The returned value is the output after the final iteration. However, we may often be +# interested in the results from the whole simulation. +# The results from the simulation, which is a dictionary of all the nodes in the signal +# flow graph, can be obtained as sim.results # %% -# Hence, we can obtain the results that we are interested in and, for example, plot the output and the value after the -# first addition: +# Hence, we can obtain the results that we are interested in and, for example, plot the +# output and the value after the first addition: -import matplotlib.pyplot as plt +import matplotlib.pyplot as plt # noqa: E402 plt.plot(sim.results['0'], label="Output") plt.plot(sim.results['add1'], label="After first addition") @@ -193,24 +178,27 @@ plt.show() # %% # To compute and plot the frequency response, it is possible to use mplsignal -from mplsignal.freq_plots import freqz_fir +from mplsignal.freq_plots import freqz_fir # noqa: E402 freqz_fir(sim.results["0"]) plt.show() # %% -# As seen, the output has not converged to zero, leading to that the frequency-response may not be correct, so we want -# to simulate for a longer time. -# Instead of just adding zeros to the input array, we can use a function that generates the impulse response instead. -# There are a number of those defined in B-ASIC for convenience, and the one for an impulse response is called :class:`.Impulse`. +# As seen, the output has not converged to zero, leading to that the frequency-response +# may not be correct, so we want to simulate for a longer time. +# Instead of just adding zeros to the input array, we can use a function that generates +# the impulse response instead. +# There are a number of those defined in B-ASIC for convenience, and the one for an +# impulse response is called :class:`.Impulse`. -from b_asic.signal_generator import Impulse +from b_asic.signal_generator import Impulse # noqa: E402 sim = Simulation(firstorderiir, [Impulse()]) # %% -# Now, as the functions will not have an end, we must run the simulation for a given number of cycles, say 30. +# Now, as the functions will not have an end, we must run the simulation for a given +# number of cycles, say 30. # This is done using :func:`~b_asic.simulation.Simulation.run_for` instead: sim.run_for(30) diff --git a/examples/folding_example_with_architecture.py b/examples/folding_example_with_architecture.py index 6f226f917b132177e8b934551140ef23c41356a5..8eea8e4d3bf8b47a10f02a60d9161df620232d49 100644 --- a/examples/folding_example_with_architecture.py +++ b/examples/folding_example_with_architecture.py @@ -35,6 +35,10 @@ out1 = Output(add1 + add3, "OUT") sfg = SFG(inputs=[in1], outputs=[out1], name="Bi-quad folding example") +# %% +# The SFG looks like: +sfg + # %% # Set latencies and execution times sfg.set_latency_of_type(ConstantMultiplication.type_name(), 2) diff --git a/examples/introduction.py b/examples/introduction.py index 8e1747997460a180d55765d7870881522b4d4030..2cf182b3db8eb3df174bd52557810eb627b3b102 100644 --- a/examples/introduction.py +++ b/examples/introduction.py @@ -15,3 +15,7 @@ a = Addition(i, c) d.input(0).connect(a) sfg = SFG([i], [o]) + +# %% +# The SFG looks like: +sfg diff --git a/examples/schedulingexample.py b/examples/schedulingexample.py index 889e99bcfe290df0b5716f5c1f4a24fe34e98a44..44e3f9468a1b8440bb2d4704ff6a267b2a62fa52 100644 --- a/examples/schedulingexample.py +++ b/examples/schedulingexample.py @@ -28,3 +28,6 @@ node3 = node2 + node5 node4 << node3 sfg = SFG([node1], [out], name="Scheduling example") +# %% +# THe SFG looks like +sfg diff --git a/examples/threepointwinograddft.py b/examples/threepointwinograddft.py index 17ec5c42248e4635677dcca13381a6c0d47ec1de..68b8afbd3997c4b05476fbedea19e8ca9b0ac6c5 100644 --- a/examples/threepointwinograddft.py +++ b/examples/threepointwinograddft.py @@ -37,6 +37,10 @@ sfg = SFG( name="3-point Winograd DFT", ) +# %% +# The SFG looks like +sfg + # %% # Set latencies and execution times sfg.set_latency_of_type(ConstantMultiplication.type_name(), 2)