CompositeBloq Graph Drawing#

qualtran.drawing includes classes for drawing a CompositeBloq as a directed acyclic graph. This uses the popular package graphviz to render the drawings. The simplest way to get started is by calling show_bloq.

Basic Usage#

from qualtran.drawing import show_bloq

# Use some test bloqs to show drawing features
from qualtran.bloqs.for_testing import TestAtom, TestParallelCombo

Each constituent bloq is a table whose header contains the bloq name. Each row is a register. Edges represent connections between soquets. Each edge is labeled with the bitsize of the connected registers. Dangling soquets (corresponding to the enclosing bloq’s registers) are drawn as plain text.

show_bloq(TestAtom())
../_images/9af31f3f16eef94d59677dcc05490dbc146713ed362b627aeb14abda32f42a14.svg
show_bloq(TestParallelCombo())
../_images/269725ea7295fe9731a9a1108b6c2598043895d4488984264f49eefd25d13560.svg
cbloq = TestParallelCombo().decompose_bloq()
show_bloq(cbloq)
../_images/dc324859a93faccf306a1be351cfb7b0b1b0b56c7f464fc23e9dbdc564425d34.svg

Advanced Usage#

PrettyGraphDrawer and GraphDrawer#

These classes contain the complete functionality for translating a composite bloq into a graphviz-compatible graph.

By default, we use PrettyGraphDrawer which abbreviates names, hides some details of reshaping bloqs, and chooses drawing parameters to give a compact visualization of the bloq. For debugging purposes or to serve as a base class for modifying drawing parameters to your liking, consider GraphDrawer which relies solely on graphviz defaults.

from qualtran.drawing import PrettyGraphDrawer

PrettyGraphDrawer(cbloq).get_svg()
../_images/dc324859a93faccf306a1be351cfb7b0b1b0b56c7f464fc23e9dbdc564425d34.svg
from qualtran.drawing import GraphDrawer

GraphDrawer(cbloq).get_svg()
../_images/3bc0efde8a3eb18b256d890a09610065db8499e2611dfc653e1c4446a1171090.svg

ClassicalSimGraphDrawer#

This simple extension of GraphDrawer annotates each edge according to classical data that flows through bloqs supporting the classical simulation protocol.

from qualtran.drawing import ClassicalSimGraphDrawer
from qualtran.bloqs.mcmt import MultiAnd

ClassicalSimGraphDrawer(
    bloq=MultiAnd((1,1,1,1)).decompose_bloq(), 
    vals=dict(ctrl=[1,1,0,1])
).get_svg()
../_images/436622bcd9d692ab200c5cc389977aed6414e9635663dc91a51e88f155689bfe.svg

Graphviz and pydot#

We use pydot to manupulate graphviz graphs. You can get that graph directly:

graph = GraphDrawer(cbloq).get_graph()
print(graph.to_string())
digraph my_graph {
rankdir=LR;
subgraph {
rank=same;
reg_G7 [label=reg, shape=plaintext];
}
TestAtom [label=<<TABLE >
  <TR><TD colspan="2">TestAtom</TD></TR>
  <TR><TD colspan="2" port="q_G6">q</TD></TR>
</TABLE>>, shape=plain];
Join [label=<<TABLE >
  <TR><TD colspan="2">Join</TD></TR>
  <TR><TD  port="reg_G13">reg[0]</TD><TD rowspan="3" port="reg_G12">reg</TD></TR>
  <TR><TD  port="reg_G11">reg[1]</TD></TR>
  <TR><TD  port="reg_G17">reg[2]</TD></TR>
</TABLE>>, shape=plain];
TestAtom_G0 [label=<<TABLE >
  <TR><TD colspan="2">TestAtom</TD></TR>
  <TR><TD colspan="2" port="q_G10">q</TD></TR>
</TABLE>>, shape=plain];
TestAtom_G2 [label=<<TABLE >
  <TR><TD colspan="2">TestAtom</TD></TR>
  <TR><TD colspan="2" port="q_G16">q</TD></TR>
</TABLE>>, shape=plain];
Split [label=<<TABLE >
  <TR><TD colspan="2">Split</TD></TR>
  <TR><TD rowspan="3" port="reg_G14">reg</TD><TD  port="reg_G5">reg[0]</TD></TR>
  <TR><TD  port="reg_G15">reg[1]</TD></TR>
  <TR><TD  port="reg_G9">reg[2]</TD></TR>
</TABLE>>, shape=plain];
subgraph {
rank=same;
reg_G8 [label=reg, shape=plaintext];
}
reg_G7:e -> Split:reg_G14:w [label=3];
Split:reg_G5:e -> TestAtom_G2:q_G16:w [label=1];
Split:reg_G15:e -> TestAtom:q_G6:w [label=1];
Split:reg_G9:e -> TestAtom_G0:q_G10:w [label=1];
TestAtom_G2:q_G16:e -> Join:reg_G13:w [label=1];
TestAtom:q_G6:e -> Join:reg_G11:w [label=1];
TestAtom_G0:q_G10:e -> Join:reg_G17:w [label=1];
Join:reg_G12:e -> reg_G8:w [label=3];
}

Register Groups#

We group left and right registers with shared names. This section shows some usual and unusual register specifications to test the graphviz logic for making the table rows match up.

from qualtran import Bloq, Register, Signature, Side, QAny

class SignatureBloq(Bloq):
    """Placeholder bloq that lets you specify its signature."""
    def __init__(self, signature: Signature):
        self._signature = signature
        
    @property
    def signature(self) -> Signature:
        return self._signature
        
    def __str__(self):
        return 'Bloq'
bloq = SignatureBloq(Signature([
    Register('x', QAny(100)),
    Register('y', QAny(200)),
]))
show_bloq(bloq)
../_images/2beffb426a2926958eb534561150c08151d4d74e26f64abee9859dc95fbaed0b.svg
bloq = SignatureBloq(Signature([
    Register('x', QAny(100)),
    Register('a', QAny(2), shape=(2,), side=Side.LEFT),
    Register('y', QAny(200)),
]))
show_bloq(bloq)
../_images/fc36432a0a4ce2b08e69cb108eead5ee7e313fda7f9f63f489b9590f26c4e710.svg
bloq = SignatureBloq(Signature([
    Register('x', QAny(100)),
    Register('a', QAny(2), shape=(2,2), side=Side.LEFT),
    Register('a', QAny(8),  side=Side.RIGHT),
    Register('y', QAny(200)),
]))
show_bloq(bloq)
../_images/ca9cc2f6d11c2399d6a5e9b8c8a162af22052ef980f4e4ed706c943275f493bd.svg
bloq = SignatureBloq(Signature([
    Register('x', QAny(100)),
    Register('a', QAny(2), shape=(2,2), side=Side.LEFT),
    Register('a', QAny(4), shape=(2,), side=Side.RIGHT),
    Register('y', QAny(200)),
]))
show_bloq(bloq)
../_images/c3046efa940f6d5f50bd3076b97335c973d648315f999879754f1574d75064b9.svg