This page was generated from dialect/graphblas_dialect_tutorials/graphblas_lower/debugging_ops.ipynb.

Debugging Ops

This example will go over some ops from the GraphBLAS dialect that are useful for debugging.

Let’s first import some necessary modules and generate an instance of our JIT engine.


import mlir_graphblas
import numpy as np
from mlir_graphblas.tools.utils import sparsify_array

engine = mlir_graphblas.MlirJitEngine()
Using development graphblas-opt: /Users/pnguyen/code/mlir-graphblas/mlir_graphblas/src/build/bin/graphblas-opt

Here are the passes we’ll use.


passes = [
    "--graphblas-structuralize",
    "--graphblas-dwim",
    "--graphblas-optimize",
    "--graphblas-lower",
    "--sparsification",
    "--sparse-tensor-conversion",
    "--linalg-bufferize",
    "--func-bufferize",
    "--tensor-bufferize",
    "--finalizing-bufferize",
    "--convert-linalg-to-loops",
    "--convert-vector-to-llvm",
    "--convert-math-to-llvm",
    "--convert-math-to-libm",
    "--convert-scf-to-cf",
    "--convert-memref-to-llvm",
    "--convert-openmp-to-llvm",
    "--convert-arith-to-llvm",
    "--convert-std-to-llvm",
    "--reconcile-unrealized-casts",
]

Overview of graphblas.comment

Comments in MLIR disappear when roundtripped through mlir-opt or graphblas-opt.

graphblas.comment is an op that acts as a workaround to preserve comments in MLIR code. It has an attribute that contains the code comment. When passed through --graphblas-lower, the op simply disappears. graphblas.comment is intended to be a no-op.

We’ll demonstrate this by inserting several graphblas.comment ops into a function and show that it does not affect behavior. Here is some MLIR code to perform a conventional matrix-multiply by using graphblas.matrix_multiply with the plus-times semiring. Note that both of the input matrices are CSR matrices. Thus, we’ll need to convert the layout of the second operand in our code below.


mlir_text = """
#CSR64 = #sparse_tensor.encoding<{
  dimLevelType = [ "dense", "compressed" ],
  dimOrdering = affine_map<(i,j) -> (i,j)>,
  pointerBitWidth = 64,
  indexBitWidth = 64
}>

#CSC64 = #sparse_tensor.encoding<{
  dimLevelType = [ "dense", "compressed" ],
  dimOrdering = affine_map<(i,j) -> (j,i)>,
  pointerBitWidth = 64,
  indexBitWidth = 64
}>

module {
    func @matrix_multiply_plus_times(%a: tensor<?x?xf64, #CSR64>, %b: tensor<?x?xf64, #CSR64>) -> tensor<?x?xf64, #CSR64> {
        %b_csc = graphblas.convert_layout %b : tensor<?x?xf64, #CSR64> to tensor<?x?xf64, #CSC64>
        %answer = graphblas.matrix_multiply %a, %b_csc { semiring = "plus_times" } : (tensor<?x?xf64, #CSR64>, tensor<?x?xf64, #CSC64>) to tensor<?x?xf64, #CSR64>
        return %answer : tensor<?x?xf64, #CSR64>
    }
    func @matrix_multiply_plus_times_with_comments(%a: tensor<?x?xf64, #CSR64>, %b: tensor<?x?xf64, #CSR64>) -> tensor<?x?xf64, #CSR64> {
        graphblas.comment { comment = "here is the first comment!" }
        graphblas.comment { comment = "here is the second comment!" }
        %b_csc = graphblas.convert_layout %b : tensor<?x?xf64, #CSR64> to tensor<?x?xf64, #CSC64>
        graphblas.comment { comment = "here is the third comment!" }
        %answer = graphblas.matrix_multiply %a, %b_csc { semiring = "plus_times" } : (tensor<?x?xf64, #CSR64>, tensor<?x?xf64, #CSC64>) to tensor<?x?xf64, #CSR64>
        graphblas.comment { comment = "here is the fourth comment!" }
        graphblas.comment { comment = "here is the fifth comment!" }
        return %answer : tensor<?x?xf64, #CSR64>
    }
}
"""

engine.add(mlir_text, passes)

['matrix_multiply_plus_times', 'matrix_multiply_plus_times_with_comments']

Let’s generate some inputs to show that the graphblas.comment ops don’t affect behavior.


A_dense = np.array(
    [
        [1, 0, 0, 0],
        [2, 0, 3, 4],
        [0, 0, 0, 0],
        [0, 0, 5, 6],
    ],
    dtype=np.float64
)
A = sparsify_array(A_dense, [False, True])

B_dense = np.array(
    [
        [0, 7, 0, 7],
        [0, 1, 0, 0],
        [0, 1, 7, 0],
        [0, 7, 2, 0],
    ],
    dtype=np.float64
)
B = sparsify_array(B_dense, [False, True])

no_comment_result = engine.matrix_multiply_plus_times(A, B)
comment_result = engine.matrix_multiply_plus_times_with_comments(A, B)

np.all(no_comment_result.toarray() == comment_result.toarray())

True

Overview of graphblas.print

Writing MLIR code can be difficult.

Print statements can help debugging undesired behavior, e.g. segmentation faults.

graphblas.print let’s us print things. For example, take a look at this MLIR code.

module {
  func @print_arbitrary_content() -> () {
      %c99 = constant 99 : index
      %0 = constant 1.3 : f32
      %1 = constant 34 : i8
      graphblas.print %0, %0, %0, %0 { strings = ["first line : "] } : f32, f32, f32, f32
      graphblas.print %c99 { strings = ["second line : ", " string_a", " string_b", " string_c"] } : index
      graphblas.print %0, %1 { strings = ["third line : ", " |\\"| "] } : f32, i8
      return
  }
}

It’ll print the following:

first line : 1.3 1.3 1.3 1.3
second line : 99 string_a  string_b  string_c
third line : 1.3 |"| 34