Skip to content

tikz

fdpyutils.tikz.matshow.TikzMatrix

TikzMatrix(mat: Union[Tensor, ndarray])

Class to visualize a matrix with TikZ.

Attributes:

  • TEMPLATE (str) –

    Template TikZ code containing placeholders that will be substituted with content when saving a figure.

Examples:

>>> from numpy import linspace
>>> mat = linspace(0, 1, num=30).reshape(3, 10)
>>> savepath = "mat.tex"
>>> tikz_mat = TikzMatrix(mat)
>>> tikz_mat.highlight(1, 0, fill="blue", fill_opacity=0.5)
>>> # NOTE to compile, you need `pdflatex`
>>> tikz_mat.save(savepath, compile=False)
  • Example image
  • I used this to visualize the structured matrices in our SINGD paper.

Store the matrix internally.

Parameters:

  • mat (Union[Tensor, ndarray]) –

    The matrix that will be visualized as PyTorch tensor or NumPy array.

Raises:

  • ValueError

    If the supplied array is not 2d.

Source code in fdpyutils/tikz/matshow.py
def __init__(self, mat: Union[Tensor, ndarray]) -> None:
    """Store the matrix internally.

    Args:
        mat: The matrix that will be visualized as PyTorch tensor or NumPy array.

    Raises:
        ValueError: If the supplied array is not 2d.
    """
    if mat.ndim != 2:
        raise ValueError(f"Expected 2d array. Got {mat.ndim}d.")
    self.mat = mat

    self.grid = "major"
    self.colormap = "colormap/blackwhite"
    self.vmin: Union[float, None] = 0.0  # color bar minimum
    self.vmax: Union[float, None] = 1.0  # color bar maximum

    self.extra_commands = []
    self.extra_preamble = []

highlight

highlight(column: int, row: int, fill: str = 'red', fill_opacity: float = 0.5) -> None

Highlight a pixel in the matrix.

Parameters:

  • column (int) –

    column index of the pixel to highlight.

  • row (int) –

    row index of the pixel to highlight.

  • fill (str, default: 'red' ) –

    colour to fill the pixel with. Default: 'red'.

  • fill_opacity (float, default: 0.5 ) –

    opacity of the highlighting. Default: 0.5.

Source code in fdpyutils/tikz/matshow.py
def highlight(
    self, column: int, row: int, fill: str = "red", fill_opacity: float = 0.5
) -> None:
    """Highlight a pixel in the matrix.

    Args:
        column: column index of the pixel to highlight.
        row: row index of the pixel to highlight.
        fill: colour to fill the pixel with. Default: `'red'`.
        fill_opacity: opacity of the highlighting. Default: `0.5`.
    """
    self.extra_commands.append(
        rf"\draw [line width=0, fill={fill}, fill opacity={fill_opacity}] "
        + f"({column}, {row}) rectangle ++(1, 1);"
    )

save

save(savepath: str, compile: bool = False)

Save the matrix plot as standalone TikZ figure and maybe build to pdf.

Parameters:

  • savepath (str) –

    Path to save the figure to (including '.tex').

  • compile (bool, default: False ) –

    Whether to compile the TikZ figure to pdf. Default is False.

Source code in fdpyutils/tikz/matshow.py
def save(self, savepath: str, compile: bool = False):
    """Save the matrix plot as standalone TikZ figure and maybe build to pdf.

    Args:
        savepath: Path to save the figure to (including `'.tex'`).
        compile: Whether to compile the TikZ figure to pdf. Default is `False`.
    """
    template = self.TEMPLATE

    # NOTE matrix plot requires two rows and columns to figure out the cell size.
    # For single rows/columns we expand them into two and then limit their
    # visibility by setting the axis limits.
    y_max, x_max = self.mat.shape

    mat = self.mat
    if y_max == 1:
        assert isinstance(mat, Tensor)
        mat = mat.expand(2, -1)
    elif x_max == 1:
        assert isinstance(mat, Tensor)
        mat = mat.expand(-1, 2)
    rows, cols = mat.shape

    content = ["x\ty\tz"]
    content.extend(
        f"{float(col + 0.5)}\t{float(row + 0.5)}\t{mat[row, col]}"
        for row, col in product(range(rows), range(cols))
    )
    content = "\n".join(content)

    for placeholder, replacement in [
        ("PLACEHOLDER_CONTENT", content),
        ("PLACEHOLDER_XMAX", str(x_max)),
        ("PLACEHOLDER_YMAX", str(y_max)),
        ("PLACEHOLDER_WIDTH", str(2 * x_max)),
        ("PLACEHOLDER_HEIGHT", str(2 * y_max)),
        ("PLACEHOLDER_ROWS", str(rows)),
        ("PLACEHOLDER_COLS", str(cols)),
        ("PLACEHOLDER_COLORMAP", self.colormap),
        ("PLACEHOLDER_GRID", self.grid),
        ("PLACEHOLDER_EXTRA_COMMANDS", "\n".join(self.extra_commands)),
        ("PLACEHOLDER_EXTRA_PREAMBLE", "\n".join(self.extra_preamble)),
        (
            "PLACEHOLDER_COLORBAR_MIN",
            (
                f"% color bar minimum\n    point meta min={self.vmin}"
                if self.vmin is not None
                else ""
            ),
        ),
        (
            "PLACEHOLDER_COLORBAR_MAX",
            (
                f"% color bar maximum\n    point meta max={self.vmax}"
                if self.vmax is not None
                else ""
            ),
        ),
    ]:
        template = template.replace(placeholder, replacement)

    write(template, savepath, compile=compile)

fdpyutils.tikz.tensorshow.TikzTensor

TikzTensor(tensor: Union[Tensor, ndarray])

Class to visualize a tensor with TikZ.

Attributes:

  • TEMPLATE

    Template TikZ code containing placeholders that will be replaced with content before compilation.

Examples:

>>> from torch import linspace, Size
>>> shape = Size((1, 2, 3, 4, 10))
>>> tensor = linspace(0, 1, shape.numel()).reshape(shape)
>>> savepath = "tensor.tex"
>>> tikz_tensor = TikzTensor(tensor)
>>> tikz_tensor.highlight((0, 0, 0, 3, 1), fill="green", fill_opacity=0.5)
>>> # NOTE to compile, you need `pdflatex`
>>> tikz_tensor.save(savepath, compile=False)
  • Example image

Store the tensor internally.

Parameters:

  • tensor (Union[Tensor, ndarray]) –

    The tensor to visualize. At most 5d.

Raises:

  • NotImplementedError

    If the tensor has more than 5 dimensions.

Source code in fdpyutils/tikz/tensorshow.py
def __init__(self, tensor: Union[Tensor, ndarray]) -> None:
    """Store the tensor internally.

    Args:
        tensor: The tensor to visualize. At most 5d.

    Raises:
        NotImplementedError: If the tensor has more than 5 dimensions.
    """
    max_dim = 5
    ndim = len(tensor.shape)
    if ndim > max_dim:
        raise NotImplementedError(
            f"(d>{max_dim})-tensors are not supported. Got {ndim}d."
        )

    # reshape the tensor into 5d if it has less dimensions
    missing = max_dim - ndim
    shape = (1,) * missing + tensor.shape
    self.tensor = tensor.reshape(shape)

    # stores the style of highlighted pixels
    self.highlighted_entries: List[Tuple[Tuple[int, int, int], Dict]] = []

save

save(savepath: str, compile: bool = True) -> None

Save the TikZ code to visualize the tensor to a file and maybe compile it.

The approach has two stages:

  1. Generate .tex and .pdf files for 2d fibres of the tensor.
  2. Compose the fibres into a TikZ picture displaying the entire tensor.

Parameters:

  • savepath (str) –

    The path to save the TikZ code to.

  • compile (bool, default: True ) –

    Whether to compile the TikZ code to a PDF.

Source code in fdpyutils/tikz/tensorshow.py
def save(self, savepath: str, compile: bool = True) -> None:
    """Save the TikZ code to visualize the tensor to a file and maybe compile it.

    The approach has two stages:

    1. Generate `.tex` and `.pdf` files for 2d fibres of the tensor.
    2. Compose the fibres into a TikZ picture displaying the entire tensor.

    Args:
        savepath: The path to save the TikZ code to.
        compile: Whether to compile the TikZ code to a PDF.
    """
    self._generate_fibres(savepath, compile=compile)
    self._combine_fibres(savepath, compile=compile)

highlight

highlight(idx: Tuple[int, ...], fill: str = 'VectorOrange', fill_opacity: float = 0.5) -> None

Highlight a pixel in the tensor.

Parameters:

  • idx (Tuple[int, ...]) –

    The index of the pixel to highlight.

  • fill (str, default: 'VectorOrange' ) –

    colour to fill the pixel with. Default: 'VectorOrange'.

  • fill_opacity (float, default: 0.5 ) –

    opacity of the highlighting. Default: 0.5.

Raises:

  • ValueError

    if the index has incorrect dimensions or is out of bounds.

Source code in fdpyutils/tikz/tensorshow.py
def highlight(
    self,
    idx: Tuple[int, ...],
    fill: str = "VectorOrange",
    fill_opacity: float = 0.5,
) -> None:
    """Highlight a pixel in the tensor.

    Args:
        idx: The index of the pixel to highlight.
        fill: colour to fill the pixel with. Default: `'VectorOrange'`.
        fill_opacity: opacity of the highlighting. Default: `0.5`.

    Raises:
        ValueError: if the index has incorrect dimensions or is out of bounds.
    """
    if len(idx) < len(self.tensor.shape):
        idx = (0,) * (len(self.tensor.shape) - len(idx)) + idx
    if len(idx) > len(self.tensor.shape):
        raise ValueError(
            f"Index ({len(idx)}d) has more dimensions than tensor ({len(idx)}d)."
        )
    if any(i >= s for i, s in zip(idx, self.tensor.shape)):
        raise ValueError(
            f"Index {idx} is out of bounds for tensor shape {self.tensor.shape}."
        )

    d1, d2, d3, d4, d5 = idx
    style = {"column": d5, "row": d4, "fill": fill, "fill_opacity": fill_opacity}
    entry = ((d1, d2, d3), style)
    if entry not in self.highlighted_entries:
        self.highlighted_entries.append(entry)

fdpyutils.tikz.conv2d.TikzConv2d

TikzConv2d(weight: Tensor, x: Tensor, savedir: str, stride: Tuple[int, int] = (1, 1), padding: Tuple[int, int] = (0, 0), dilation: Tuple[int, int] = (1, 1))

Class for visualizing 2d convolutions with TikZ.

Examples:

>>> from torch import manual_seed, rand
>>> _ = manual_seed(0)
>>> # convolution hyper-parameters
>>> N, C_in, I1, I2 = 2, 2, 4, 5
>>> G, C_out, K1, K2 = 1, 3, 2, 3
>>> P = (0, 1) # non-zero padding along one dimension
>>> weight = rand(C_out, C_in // G, K1, K2)
>>> x = rand(N, C_in, I1, I2)
>>> # NOTE to compile, you need `pdflatex`
>>> TikzConv2d(weight, x, "conv2d", padding=P).save(compile=False)
  • Example image (padded pixels are highlighted)
  • I used this code to create the visualizations for my talk at Perimeter Institute.

Store convolution tensors and hyper-parameters for the animated convolution.

Parameters:

  • weight (Tensor) –

    Convolution kernel. Has shape [C_out, C_in // G, K1, K2].

  • x (Tensor) –

    Input tensor. Has shape [N, C_in, I1, I2].

  • savedir (str) –

    Directory under which the TikZ code and pdf images are saved.

  • stride (Tuple[int, int], default: (1, 1) ) –

    Stride of the convolution. Default: (1, 1).

  • padding (Tuple[int, int], default: (0, 0) ) –

    Padding of the convolution. Default: (0, 0).

  • dilation (Tuple[int, int], default: (1, 1) ) –

    Dilation of the convolution. Default: (1, 1).

Raises:

  • ValueError

    If weight or x are not 4d tensors.

Source code in fdpyutils/tikz/conv2d.py
def __init__(
    self,
    weight: Tensor,
    x: Tensor,
    savedir: str,
    stride: Tuple[int, int] = (1, 1),
    padding: Tuple[int, int] = (0, 0),
    dilation: Tuple[int, int] = (1, 1),
):
    """Store convolution tensors and hyper-parameters for the animated convolution.

    Args:
        weight: Convolution kernel. Has shape `[C_out, C_in // G, K1, K2]`.
        x: Input tensor. Has shape `[N, C_in, I1, I2]`.
        savedir: Directory under which the TikZ code and pdf images are saved.
        stride: Stride of the convolution. Default: `(1, 1)`.
        padding: Padding of the convolution. Default: `(0, 0)`.
        dilation: Dilation of the convolution. Default: `(1, 1)`.

    Raises:
        ValueError: If `weight` or `x` are not 4d tensors.
    """
    if weight.ndim != 4 or x.ndim != 4:
        raise ValueError(
            f"Expected 4d `weight` and `x`, but got {weight.ndim}d and {x.ndim}d."
        )
    self.savedir = savedir

    # store hyper-parameters
    self.N, self.C_in, self.I1, self.I2 = x.shape
    self.C_out, _, self.K1, self.K2 = weight.shape
    self.G = self.C_in // weight.shape[1]
    self.S1, self.S2 = stride
    self.P1, self.P2 = padding
    self.D1, self.D2 = dilation

    # explicitly pad x
    x = pad(x, (self.P2, self.P2, self.P1, self.P1))

    # convolution index pattern tensors and output sizes
    self.pattern1 = index_pattern(
        self.I1 + 2 * self.P1, self.K1, stride=self.S1, padding=0, dilation=self.D1
    )
    self.pattern2 = index_pattern(
        self.I2 + 2 * self.P2, self.K2, stride=self.S2, padding=0, dilation=self.D2
    )
    self.O1 = self.pattern1.shape[1]
    self.O2 = self.pattern2.shape[1]

    # store all tensors with separated channel groups
    x = rearrange(x, "n (g c_in) i1 i2 -> n g c_in i1 i2", g=self.G)
    weight = rearrange(
        weight, "(g c_out) c_in k1 k2 -> g c_out c_in k1 k2", g=self.G
    )
    output = einsum(
        x,
        self.pattern1.float(),
        self.pattern2.float(),
        weight,
        "n g c_in i1 i2, k1 o1 i1, k2 o2 i2, g c_out c_in k1 k2 -> n g c_out o1 o2",
    )

    # normalize all tensors to use the colour map's full range
    self.x = self.normalize(x)
    self.weight = self.normalize(weight)
    self.output = self.normalize(output)

save

save(compile: bool = True)

Create the images of the convolution's input, weight, and output tensors.

This is done in two steps:

  1. Compile the input, weight, and output tensors.
  2. Combine them into one image.

Parameters:

  • compile (bool, default: True ) –

    Whether to compile the TikZ code into a pdf image. Default: True.

Source code in fdpyutils/tikz/conv2d.py
    def save(self, compile: bool = True):
        """Create the images of the convolution's input, weight, and output tensors.

        This is done in two steps:

        1. Compile the input, weight, and output tensors.
        2. Combine them into one image.

        Args:
            compile: Whether to compile the TikZ code into a pdf image. Default: `True`.
        """
        self._generate_tensors(compile=compile)

        TEX_TEMPLATE = r"""\documentclass[tikz]{standalone}

\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}
  \node (input) {\includegraphics{DATAPATH/tensors/input}};
  \node [right=1cm of input] (star) {$\star$};
  \node [right=1cm of star] (kernel) {%
    \includegraphics{DATAPATH/tensors/weight}%
  };
  \node [right=1cm of kernel] (equal) {$=$};
  \node [right=1cm of equal] (output) {%
    \includegraphics{DATAPATH/tensors/output}%
  };
\end{tikzpicture}
\end{document}"""
        code = TEX_TEMPLATE.replace("DATAPATH", self.savedir)
        savepath = path.join(self.savedir, "example.tex")
        write(code, savepath, compile=compile)

fdpyutils.tikz.conv2d.TikzConv2dAnimated

TikzConv2dAnimated(weight: Tensor, x: Tensor, savedir: str, stride: Tuple[int, int] = (1, 1), padding: Tuple[int, int] = (0, 0), dilation: Tuple[int, int] = (1, 1))

Class for visualizing animated 2d convolutions with TikZ.

Examples:

>>> from torch import manual_seed, rand
>>> _ = manual_seed(0)
>>> N, C_in, I1, I2 = 2, 2, 3, 4
>>> G, C_out, K1, K2 = 1, 3, 2, 3
>>> P = (0, 1) # non-zero padding along one dimension
>>> weight = rand(C_out, C_in // G, K1, K2)
>>> x = rand(N, C_in, I1, I2)
>>> # NOTE to compile, you need `pdflatex`
>>> TikzConv2dAnimated(weight, x, "conv2d", padding=P).save(
...     compile=False, max_frames=10
... )
  • Example animation (padding pixels are highlighted) If you set compile=True above, there will be an example.pdf file in the supplied directory. You can compile it to a .gif using the command
    convert -verbose -delay 100 -loop 0 -density 300 example.pdf example.gif
    
    which requires the imagemagick library.
  • I used this code to create the visualizations for my talk at Perimeter Institute.

Attributes:

  • GROUP_COLORS

    Colors used to highlight different channel groups.

Store convolution tensors and hyper-parameters for the animated convolution.

Parameters:

  • weight (Tensor) –

    Convolution kernel. Has shape [C_out, C_in // G, K1, K2].

  • x (Tensor) –

    Input tensor. Has shape [N, C_in, I1, I2].

  • savedir (str) –

    Directory under which the TikZ code and pdf images are saved.

  • stride (Tuple[int, int], default: (1, 1) ) –

    Stride of the convolution. Default: (1, 1).

  • padding (Tuple[int, int], default: (0, 0) ) –

    Padding of the convolution. Default: (0, 0).

  • dilation (Tuple[int, int], default: (1, 1) ) –

    Dilation of the convolution. Default: (1, 1).

Raises:

  • ValueError

    If weight or x are not 4d tensors.

Source code in fdpyutils/tikz/conv2d.py
def __init__(
    self,
    weight: Tensor,
    x: Tensor,
    savedir: str,
    stride: Tuple[int, int] = (1, 1),
    padding: Tuple[int, int] = (0, 0),
    dilation: Tuple[int, int] = (1, 1),
):
    """Store convolution tensors and hyper-parameters for the animated convolution.

    Args:
        weight: Convolution kernel. Has shape `[C_out, C_in // G, K1, K2]`.
        x: Input tensor. Has shape `[N, C_in, I1, I2]`.
        savedir: Directory under which the TikZ code and pdf images are saved.
        stride: Stride of the convolution. Default: `(1, 1)`.
        padding: Padding of the convolution. Default: `(0, 0)`.
        dilation: Dilation of the convolution. Default: `(1, 1)`.

    Raises:
        ValueError: If `weight` or `x` are not 4d tensors.
    """
    if weight.ndim != 4 or x.ndim != 4:
        raise ValueError(
            f"Expected 4d `weight` and `x`, but got {weight.ndim}d and {x.ndim}d."
        )
    self.savedir = savedir

    # store hyper-parameters
    self.N, self.C_in, self.I1, self.I2 = x.shape
    self.C_out, _, self.K1, self.K2 = weight.shape
    self.G = self.C_in // weight.shape[1]
    self.S1, self.S2 = stride
    self.P1, self.P2 = padding
    self.D1, self.D2 = dilation

    # explicitly pad x
    x = pad(x, (self.P2, self.P2, self.P1, self.P1))

    # convolution index pattern tensors and output sizes
    self.pattern1 = index_pattern(
        self.I1 + 2 * self.P1, self.K1, stride=self.S1, padding=0, dilation=self.D1
    )
    self.pattern2 = index_pattern(
        self.I2 + 2 * self.P2, self.K2, stride=self.S2, padding=0, dilation=self.D2
    )
    self.O1 = self.pattern1.shape[1]
    self.O2 = self.pattern2.shape[1]

    # store all tensors with separated channel groups
    x = rearrange(x, "n (g c_in) i1 i2 -> n g c_in i1 i2", g=self.G)
    weight = rearrange(
        weight, "(g c_out) c_in k1 k2 -> g c_out c_in k1 k2", g=self.G
    )
    output = einsum(
        x,
        self.pattern1.float(),
        self.pattern2.float(),
        weight,
        "n g c_in i1 i2, k1 o1 i1, k2 o2 i2, g c_out c_in k1 k2 -> n g c_out o1 o2",
    )

    # normalize all tensors to use the colour map's full range
    self.x = self.normalize(x)
    self.weight = self.normalize(weight)
    self.output = self.normalize(output)

save

save(compile: bool = True, max_frames: Optional[int] = None)

Create the frames of the convolution's input, weight, and output tensors.

This is done in two steps. For each frame:

  1. Generate the input, weight, and output tensors.
  2. Compile the full tensors into one pdf image.

Parameters:

  • compile (bool, default: True ) –

    Whether to compile the TikZ code into a pdf image. Default: True.

  • max_frames (Optional[int], default: None ) –

    Maximum number of frames to generate. Default: None.

Source code in fdpyutils/tikz/conv2d.py
    def save(self, compile: bool = True, max_frames: Optional[int] = None):
        """Create the frames of the convolution's input, weight, and output tensors.

        This is done in two steps. For each frame:

        1. Generate the input, weight, and output tensors.
        2. Compile the full tensors into one pdf image.

        Args:
            compile: Whether to compile the TikZ code into a pdf image. Default: `True`.
            max_frames: Maximum number of frames to generate. Default: `None`.
        """
        self._generate_tensors(compile=compile, max_frames=max_frames)

        num_frames = self.output.numel() if max_frames is None else max_frames
        frames = [
            r"""\begin{tikzpicture}
  \node (input) {\includegraphics{DATAPATH/tensors/input_frame_FRAME}};
  \node [right=1cm of input] (star) {$\star$};
  \node [right=1cm of star] (kernel) {%
    \includegraphics{DATAPATH/tensors/weight_frame_FRAME}%
  };
  \node [right=1cm of kernel] (equal) {$=$};
  \node [right=1cm of equal] (output) {%
    \includegraphics{DATAPATH/tensors/output_frame_FRAME}%
  };
\end{tikzpicture}""".replace(
                "DATAPATH", self.savedir
            ).replace(
                "FRAME", str(n)
            )
            for n in range(num_frames)
        ]
        code = r"""\documentclass[tikz]{standalone}

\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}
CONTENT
\end{document}""".replace(
            "CONTENT", "\n".join(frames)
        )
        savepath = path.join(self.savedir, "example.tex")
        write(code, savepath, compile=compile)

fdpyutils.tikz.unfold.TikzUnfoldAnimated

TikzUnfoldAnimated(weight: Tensor, x: Tensor, savedir: str, stride: Tuple[int, int] = (1, 1), padding: Tuple[int, int] = (0, 0), dilation: Tuple[int, int] = (1, 1))

Class for visualizing input unfolding of a 2d convolution with TikZ.

Examples:

>>> from torch import manual_seed, rand
>>> _ = manual_seed(0)
>>> N, C_in, I1, I2 = 1, 2, 5, 1
>>> G, C_out, K1, K2 = 1, 4, 3, 1
>>> P = (1, 0) # non-zero padding along one dimension
>>> weight = rand(C_out, C_in // G, K1, K2)
>>> x = rand(N, C_in, I1, I2)
>>> # NOTE to compile, you need `pdflatex`
>>> TikzUnfoldAnimated(weight, x, "unfold", padding=P).save(
...     compile=False, max_frames=10
... )
  • Example animation (left is matricized output, middle is matricized kernel, right is unfolded input) If you set compile=True above, there will be an example.pdf file in the supplied directory. You can compile it to a .gif using the command
    convert -verbose -delay 100 -loop 0 -density 300 example.pdf example.gif
    
    which requires the imagemagick library.
  • I used this code to create the visualizations for my talk at Perimeter Institute.

Store convolution hyper-parameters for the animated input unfolding.

Parameters:

  • weight (Tensor) –

    Convolution kernel. Has shape [C_out, C_in // G, K1, K2].

  • x (Tensor) –

    Input tensor. Has shape [N, C_in, I1, I2].

  • savedir (str) –

    Directory under which the TikZ code and pdf images are saved.

  • stride (Tuple[int, int], default: (1, 1) ) –

    Stride of the convolution. Default: (1, 1).

  • padding (Tuple[int, int], default: (0, 0) ) –

    Padding of the convolution. Default: (0, 0).

  • dilation (Tuple[int, int], default: (1, 1) ) –

    Dilation of the convolution. Default: (1, 1).

Source code in fdpyutils/tikz/unfold.py
def __init__(
    self,
    weight: Tensor,
    x: Tensor,
    savedir: str,
    stride: Tuple[int, int] = (1, 1),
    padding: Tuple[int, int] = (0, 0),
    dilation: Tuple[int, int] = (1, 1),
):
    """Store convolution hyper-parameters for the animated input unfolding.

    Args:
        weight: Convolution kernel. Has shape `[C_out, C_in // G, K1, K2]`.
        x: Input tensor. Has shape `[N, C_in, I1, I2]`.
        savedir: Directory under which the TikZ code and pdf images are saved.
        stride: Stride of the convolution. Default: `(1, 1)`.
        padding: Padding of the convolution. Default: `(0, 0)`.
        dilation: Dilation of the convolution. Default: `(1, 1)`.
    """
    super().__init__(
        weight, x, savedir, stride=stride, padding=padding, dilation=dilation
    )
    self.x_unfolded = einsum(
        self.x,
        self.pattern1.float(),
        self.pattern2.float(),
        "n g c i1 i2, k1 o1 i1, k2 o2 i2 -> n g (c k1 k2) (o1 o2)",
    )
    self.weight_as_mat = rearrange(
        self.weight, "g c_out c_in k1 k2 -> g c_out (c_in k1 k2)"
    )
    self.output_as_mat = rearrange(
        self.output, "n g c_out o1 o2 -> n g c_out (o1 o2)"
    )

save

save(compile: bool = True, max_frames: Optional[int] = None) -> None

Create frames of the unfolded input, matricized kernel and matricized output.

This is done in two steps. For each frame:

  1. Generate the individual tensors.
  2. Compile the full tensors into one pdf image.

Parameters:

  • compile (bool, default: True ) –

    Whether to compile the TikZ code into a pdf image. Default: True.

  • max_frames (Optional[int], default: None ) –

    Maximum number of frames to generate. Default: None.

Source code in fdpyutils/tikz/unfold.py
    def save(self, compile: bool = True, max_frames: Optional[int] = None) -> None:
        """Create frames of the unfolded input, matricized kernel and matricized output.

        This is done in two steps. For each frame:

        1. Generate the individual tensors.
        2. Compile the full tensors into one pdf image.

        Args:
            compile: Whether to compile the TikZ code into a pdf image. Default: `True`.
            max_frames: Maximum number of frames to generate. Default: `None`.
        """
        self._generate_tensors(compile=compile, max_frames=max_frames)

        num_frames = self.output.numel() if max_frames is None else max_frames
        frames = [
            r"""\begin{tikzpicture}
  \node (output) {\includegraphics{DATAPATH/tensors/output_frame_FRAME}};
  \node [right=1cm of output] (equal) {$=$};
  \node [right=1cm of equal] (kernel) {%
    \includegraphics{DATAPATH/tensors/weight_frame_FRAME}%
  };
  \node [right=1cm of kernel] (cdot) {$\cdot$};
  \node [right=1cm of cdot] (input) {%
    \includegraphics{DATAPATH/tensors/input_frame_FRAME}%
  };
\end{tikzpicture}""".replace(
                "DATAPATH", self.savedir
            ).replace(
                "FRAME", str(n)
            )
            for n in range(num_frames)
        ]
        code = r"""\documentclass[tikz]{standalone}

\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}
CONTENT
\end{document}""".replace(
            "CONTENT", "\n".join(frames)
        )
        savepath = path.join(self.savedir, "example.tex")
        write(code, savepath, compile=compile)

fdpyutils.tikz.unfold.TikzUnfoldWeightAnimated

TikzUnfoldWeightAnimated(weight: Tensor, x: Tensor, savedir: str, stride: Tuple[int, int] = (1, 1), padding: Tuple[int, int] = (0, 0), dilation: Tuple[int, int] = (1, 1))

Class for visualizing kernel unfolding of a 2d convolution with TikZ.

Examples:

>>> from torch import manual_seed, rand
>>> _ = manual_seed(0)
>>> N, C_in, I1, I2 = 1, 2, 5, 1
>>> G, C_out, K1, K2 = 1, 3, 4, 1
>>> P = (1, 0)
>>> weight = rand(C_out, C_in // G, K1, K2)
>>> x = rand(N, C_in, I1, I2)
>>> # NOTE to compile, you need `pdflatex`
>>> TikzUnfoldWeightAnimated(weight, x, "unfold_weight", padding=P).save(
...    compile=False
... )
  • Example animation (left is vectorized output, middle is unfolded kernel, right is vectorized input) If you set compile=True above, there will be an example.pdf file in the supplied directory. You can compile it to a .gif using the command
    convert -verbose -delay 100 -loop 0 -density 300 example.pdf example.gif
    
    which requires the imagemagick library.
  • I used this code to create the visualizations for my talk at Perimeter Institute.

Store convolution hyper-parameters for the animated input unfolding.

Parameters:

  • weight (Tensor) –

    Convolution kernel. Has shape [C_out, C_in // G, K1, K2].

  • x (Tensor) –

    Input tensor. Has shape [N, C_in, I1, I2].

  • savedir (str) –

    Directory under which the TikZ code and pdf images are saved.

  • stride (Tuple[int, int], default: (1, 1) ) –

    Stride of the convolution. Default: (1, 1).

  • padding (Tuple[int, int], default: (0, 0) ) –

    Padding of the convolution. Default: (0, 0).

  • dilation (Tuple[int, int], default: (1, 1) ) –

    Dilation of the convolution. Default: (1, 1).

Source code in fdpyutils/tikz/unfold.py
def __init__(
    self,
    weight: Tensor,
    x: Tensor,
    savedir: str,
    stride: Tuple[int, int] = (1, 1),
    padding: Tuple[int, int] = (0, 0),
    dilation: Tuple[int, int] = (1, 1),
):
    """Store convolution hyper-parameters for the animated input unfolding.

    Args:
        weight: Convolution kernel. Has shape `[C_out, C_in // G, K1, K2]`.
        x: Input tensor. Has shape `[N, C_in, I1, I2]`.
        savedir: Directory under which the TikZ code and pdf images are saved.
        stride: Stride of the convolution. Default: `(1, 1)`.
        padding: Padding of the convolution. Default: `(0, 0)`.
        dilation: Dilation of the convolution. Default: `(1, 1)`.
    """
    super().__init__(
        weight, x, savedir, stride=stride, padding=padding, dilation=dilation
    )
    self.x_as_vec = rearrange(self.x, "n g c i1 i2 -> n g (c i1 i2)")
    self.weight_unfolded = einsum(
        self.pattern1.float(),
        self.pattern2.float(),
        self.weight,
        "k1 o1 i1, k2 o2 i2, g c_out c_in k1 k2 -> g (c_out o1 o2) (c_in i1 i2)",
    )
    self.output_as_vec = rearrange(
        self.output, "n g c_out o1 o2 -> n g (c_out o1 o2)"
    )