projekter/yquant

Unable to access node in subcircuit

Closed this issue · 7 comments

This is probably not a bug, but I don't know how to give a name to a subcircuit so that tikz can access it with (subcircuit name-node name). Tikz says that no shape with that name is available. What do I have to do to properly name the subcircuit? Thanks.

Please always submit some example code, so that I know what you do, expect and what does not work. I tried to reproduce this problem, but for me, it works:

\documentclass{article}

\usepackage[compat=0.4]{yquant}
\usepackage{braket}

\begin{document}
   \begin{tikzpicture}
      \begin{yquant}
         qubit a[3];
         
         h a;
         [name=sub]
         subcircuit {
            qubit q;
            [ancilla]
            qubit {$\ket0$} r;
            qubit s;
            
            cnot q | r;
            [name=inner]
            cnot r | s;
         } (a[1, 2]);
         [name=outer]
         h a;
      \end{yquant}
      \draw (sub-0-inner-p0) to[in=20, out=50] (outer-0);
   \end{tikzpicture}
\end{document}

image

Here, TikZ is perfectly able to draw the line between the first positive control (-p0) of the CNOT named inner (so the "local" name within the subcircuit is inner-p0) and the first target register (-0) in the outer circuit (which gives the name outer-0).
To make the step from the inner name inner-p0 to the outer name, you prepend the name of the subcircuit. Maybe here was the problem, and I should probably make this more explicit in the manual. The name of the subcircuit is sub. However, whenever you use a gate - and a subcircuit is a gate, though a pretty special one - and assign a name, then there are lots of possible nodes that could receive the name, which is why yquant introduces all those suffixes. So in general, the name sub will not be the actual name, but sub-0, sub-1, ... for the targets, sub-p0, ... for the positive and sub-n0, ... for the negative controls. So the actual name of your subcircuit is sub-0, not sub. Whenever there is just a single (target|positive control|negative control) present, yquant additionally creates an alias of the zero-suffixed node to the name without the suffix. This is why you can refer to the target of the CNOT as inner-0 or just inner, or its positive control as inner-p0 or just inner-p. However, this aliasing behavior is not aware of the fact that subcircuits are special, it just aliases the "outer" enclosing subcircuit node itself (so the rectangle with the name sub-0 is also available as sub), not all the included nodes (which would potentially be a lot of aliases). Hence, you must always specify the full name. In this case, it is sub-0-inner-p0 (or just sub-0-inner-p).

So the last commit contains two changes:

  • I included an additional explanation in section 4.2 regarding named gates in named subcircuits:

    Note that here, <subcircuit name> is the full name of the subcircuit, which includes the -\idx suffix, unless there is only a single target register.

  • And as you can see in the last half-sentence, I augmented the aliaser: With this new commit, also all the nodes in subcircuits will be properly aliased, so now you can also use sub-inner-p or sub-inner-p0 instead of sub-0-inner-p or sub-0-inner-p0. This should make the overall naming behavior much more consistent. But be aware of the exponential growth in node names (= entries in the TeX hash table, actually five entries per node) introduced by this, so don't name nodes unless you need to do so.

Additionally, I think it would be nice to add another section of examples that specifically showcase yquant's unique features, which definitely includes subcircuits. So I'd appreciate it if you want to contribute an actually meaningful quantum circuit that makes use of named nodes in subcircuits, which I can then include in the documentation before version 0.4.1 goes to CTAN.

Wow, this is interesting. Here's something that I came up with and which I hope focuses much more on what is actually happening logically.

First solution, which does not use subcircuits at all.

% \usetikzlibrary{fit,calc}
   \begin{tikzpicture}[highlight/.style={draw, dashed, red, semithick},
                       equals/.style={draw=none, inner xsep=5mm}]
      \yquantset{operator/separation=3.5mm, register/separation=2mm}
      \coordinate (dotpos) at (0, .5);
      \begin{yquant}
         [name=dot0]
         qubit {$\ket0$} a[4];
         qubit {$\ket1$} b;
         
         h -;
         [name=dot1]
         cnot b | a[3];
         cnot b | a[0];
         [name=dot2]
         h -;
         
         [name=alignposition, equals]
         box {$=$} (-);
         discard -;
         
         [name=dot3, operator/separation=0pt]
         init {$\ket0$} a;
         [operator/separation=0pt]
         init {$\ket1$} b;
         
         [name=box1L]
         h a[1-], b;
         [name=dot4]
         h a[1-2];
         cnot b | a[3];
         [name=box1R]
         h a[3], b;
         [name=dot5]
         h a[0], b;
         cnot b | a[0];
         [name=dot6]
         h a[0], b;
      \end{yquant}
      \node[highlight, fit=(box1L-2) (box1R-1)] {};
      \node[highlight, fit=(dot5-0) (dot6-1)] {};
      \foreach \i in {0, ..., 6} {
         \node at (dotpos -| dot\i-0) {$\vdots$};
      }
      \begin{scope}[shift={($(alignposition.south west)-(0,1.4)$)}]
         \coordinate (dotpos) at (0, .5);
         \begin{yquant}
            nobit a[4];
            nobit b;
            
            [name=alignposition, equals, operator/separation=0pt]
            box {$=$} (-);
            discard -;
            
            [name=dot0, operator/separation=0pt]
            init {$\ket0$} a;
            [operator/separation=0pt]
            init {$\ket1$} b;
            
            cnot a[3] | b;
            [name=dot1]
            cnot a[0] | b;
         \end{yquant}
         \foreach \i in {0, ..., 1} {
            \node at (dotpos -| dot\i-0) {$\vdots$};
         }
      \end{scope}
   \end{tikzpicture}

Second solution, which uses subcircuits for the first line.

   \begin{tikzpicture}[highlight/.style={draw, dashed, red, semithick}]
      \yquantset{operator/separation=3.5mm, register/separation=2mm}
      \coordinate (dotpos) at (0, .6);
      
      \begin{yquant}[operators/subcircuit/frameless]
         nobit x[5];
         [name=sub]
         subcircuit {
            [name=dot0, out]
            qubit {$\ket0$} a[4];
            [out]
            qubit {$\ket1$} b;
            
            h -;
            [name=dot1]
            cnot b | a[3];
            cnot b | a[0];
            [name=dot2]
            h -;
         } (-);
         
         discard -;
         [name=alignposition, draw=none]
         box {$=$} (-);
         
         [name=sub]
         subcircuit {
            [name=dot3, out]
            qubit {$\ket0$} a[4];
            [out]
            qubit {$\ket1$} b;
            
            [name=box1L]
            h a[1-], b;
            [name=dot4]
            h a[1-2];
            cnot b | a[3];
            [name=box1R]
            h a[3], b;
            [name=dot5]
            h a[0], b;
            cnot b | a[0];
            [name=dot6]
            h a[0], b;
         } (-);
      \end{yquant}
      \node[highlight, fit=(sub-box1L-2) (sub-box1R-1)] {};
      \node[highlight, fit=(sub-dot5-0) (sub-dot6-1)] {};
      \foreach \i in {0, ..., 6} {
         \node at (dotpos -| sub-dot\i-0) {$\vdots$};
      }
      \begin{scope}[shift={($(alignposition.south west)-(0,1.4)$)}]
         \coordinate (dotpos) at (0, .5);
         \begin{yquant}
            nobit a[4];
            nobit b;
            
            [name=alignposition, operator/separation=0pt, draw=none]
            box {$=$} (-);
            discard -;
            
            [name=dot0]
            init {$\ket0$} a;
            init {$\ket1$} b;
            
            cnot a[3] | b;
            [name=dot1]
            cnot a[0] | b;
         \end{yquant}
         \foreach \i in {0, ..., 1} {
            \node at (dotpos -| dot\i-0) {$\vdots$};
         }
      \end{scope}
   \end{tikzpicture}

What did I do?

  • I draw all the vertical dots with TikZ. Main reason: They occur so often that I think they disturb the flow of reading the circuit if you use the invisible-box in a nobit-line approach. Second main reason: I want to properly center the dots with respect to the gate where they occur (although this would also work if you make the boxes wide enough). So I name all the gates that should have the dots and afterwards draw them. The vertical position is defined once in terms of the y-position of the dotpos coordinate.
  • I put the second line in a second yquant environment. We are talking about the very same wires, so it is logically misleading to define them again with different names in the same circuit. And not only that, it is quite error-prone, I would say. What if you want to continue the aligned equations? You need new names all the way long.
  • In order to properly left-align the second equation, I name the invisible box that contains the equals sign with alignposition. If I re-use this box in the second line, then what I want to have is that the left position of the new box is the same as the left position of the old box, and to start a bit below. However, in the second circuit, the box will be at the origin plus one operator separation (which is the initial separation that all operators get). So what I do is that I put the whole second circuit in a scope and shift it to the south west anchor of the box plus a bit of vertical margin. And I temporarily disable the initial shift just for this box, which gives the alignment.
  • Observe that in the second circuit, I also name the equals-box alignposition. This is not really necessary here, but if you want to add a third line in the equation, just copy the scope verbatim. The newest name always overwrites the previous one, so the third scope would be aligned below the second one. This makes it very easy to produce longer equations. And if you need a page break, just start a new tikzpicture and put \coordinate (alignposition) at (0, 0); at the beginning (or probably some small fixed indent).
  • The first solution works completely without subcircuits. You don't really need subcircuits for aligned circuit equations where all the wires are identical. Only if some of the parts of the equation suddenly have more wires (internal ancillas), then subcircuits will be the definitive way to go. The advantage in not using subcircuits is that you don't have to prefix your names. The disadvantage is that the first part of the equation looks a bit different from the other parts - you have to define your registers first, whereas in the other parts, you just re-initialize them and also have to remove the operator separation for the initializations.
  • The second solution uses subcircuits (already with the new naming syntax). The advantage is that now the left-hand side and the right-hand side are defined in the very same manner and also clearly grouped in the code. The disadvantage is that you also have to name the subcircuits in order to get access to the inner nodes. Here, I used a nasty trick: Since I don't really care about the subcircuit nodes themselves, I just assign them the same names, and the internal names are as before, so that I can still easily loop over them.

It would probably be worth thinking about some convenience macros that facilitate writing circuit equations without having to repeat so much...

The latest release 0.5 (currently on master) again addresses both issues:

  • It introduces a name mangling configuration key which allows you to specify how yquant should handle named nodes in subcircuits. The default behavior is prefix or discard: Use the name of the subcircuit as a prefix, or don't make the nodes available at all. But you can now also choose prefix or transparent, transparent, or discard, where the transparent option means that there is no prefix at all. See the documentation for /yquant/operators/subcircuit/name mangling.
  • It introduces the groups library from #11. With this library, we can produce the simplest and cleanest solution of your problem yet:
% \useyquantlanguage{groups}
\def\drawdots#1{%
   \foreach \i in {0, ..., #1} {
      \node at (dotpos -| dot\i-0) {$\vdots$};
   }
}
\begin{tikzpicture}[highlight/.style={draw, dashed, red, semithick}]
   \begin{yquantgroup}[operator/separation=3.5mm, register/separation=2mm]
      \registers{
         \coordinate (dotpos) at (0, .6);
         [name=dot0]
         qubit {$\ket0$} a[4];
         qubit {$\ket1$} b;
      }
      \circuit{
         h -;
         [name=dot1]
         cnot b | a[3];
         cnot b | a[0];
         [name=dot2]
         h -;
         
         \drawdots2
      }
      \equals*
      \circuit{
         [name=box1L]
         h a[1-], b;
         [name=dot1]
         h a[1-2];
         cnot b | a[3];
         [name=box1R]
         h a[3], b;
         [name=dot2]
         h a[0], b;
         cnot b | a[0];
         [name=dot3]
         h a[0], b;
         
         \drawdots3
         \node[highlight, fit=(box1L-2) (box1R-1)] {};
         \node[highlight, fit=(dot2-0) (dot3-1)] {};
      }
      \\[1.3cm]
      \shiftright
      \equals
      \circuit{
         cnot a[3] | b;
         [name=dot1]
         cnot a[0] | b;
         
         \drawdots1
      }
   \end{yquantgroup}
\end{tikzpicture}