liveBook · Manning

Block-diagonal unitary

As explained in Quantum Programming in Depth

A block-diagonal unitary is a unitary transformation represented by a block-diagonal matrix whose diagonal blocks are the active subtransformations.

A block-diagonal unitary has the form U = [[A, 0], [0, B]], where A and B are independent unitary matrices of equal size. For n qubits, U is 2^n × 2^n, and each block is 2^(n−1) × 2^(n−1). section 3.8

Interpret this structure as a quantum conditional: one control qubit selects whether A or B acts on the remaining qubits. This is the one-condition-qubit case of a multiplexer, while a diagonal unitary is the extreme case in which every qubit supplies a condition. section 3.8

Listing 3.9 Qiskit code for a two-qubit block-diagonal unitary
def apply_two_qubit_block_diagonal(a, b):
  circ = QuantumCircuit(2)
  circ.append(apply_one_qubit(b).control(1), [1, 0])
  circ.append(apply_one_qubit(a).control(1, ctrl_state=0), [1, 0])
  return circ

Implement the two-qubit case by controlling B on the most significant qubit and A when that qubit is in state 0; the qubit-index order matters. section 3.5.3

A practical implementation and verification workflow is:

  • Reuse a one-qubit unitary operation as the building block for each diagonal block. section 3.5.3
  • Detect zero off-diagonal blocks, extract the top-left and bottom-right matrices, and dispatch to the block-diagonal operation. section 3.8.2
  • Construct the complete matrix from A and B, compare it with the simulated operator, and test randomized unitary inputs. section 3.5.3
  • Limit tests to implemented cases when the arbitrary-unitary helper remains a placeholder; otherwise, recursive cosine-sine decomposition supports arbitrary sizes. section 3.8.2 section 3.10.2
Listing 3.16 Qiskit code for a two-block block-diagonal unitary
def apply_arbitrary_unitary(n, u):   #1
  if n == 1:
    return apply_one_qubit(u)   #2
  if n == 2:
    if all(isclose(v, 0) for v in    #3
           u[0][2:4] + u[1][2:4] +   #3
           u[2][0:2] + u[3][0:2]):   #3
      tl = [u[0][0:2], u[1][0:2]]    #3
      br = [u[2][2:4], u[3][2:4]]    #3
      return apply_two_qubit_block_diagonal(tl, br).decompose().to_gate()

    if all(isclose(v, 0) for v in    #4
           u[0][0:2] + u[1][0:2] +   #4
           u[2][2:4] + u[3][2:4]):   #4
      tr = [u[0][2:4], u[1][2:4]]    #4
      bl = [u[2][0:2], u[3][0:2]]    #4

      return apply_two_qubit_block_antidiagonal(tr, bl).decompose().to_gate()
    raise NotImplementedError("The case of " +
      "arbitrary 2-qubit unitaries is not implemented yet")
  raise NotImplementedError(
    "The case of 3+-qubit unitaries is not implemented yet")

def apply_two_block_diagonal(n, a, b):
  circ = QuantumCircuit(n)
  circ.append(apply_arbitrary_unitary(n - 1, b).control(1),   #5
              [n - 1] + list(range(n - 1)))
  circ.append(apply_arbitrary_unitary(n - 1, a).control(1, ctrl_state=0), 
              [n - 1] + list(range(n - 1)))
  return circ

The Qiskit helper demonstrates dispatch from matrix structure to the two-qubit block-diagonal implementation, with explicit failure for unsupported sizes or general cases. section 3.8.2

At full generality, block-diagonal unitaries become recursive components of arbitrary-unitary synthesis. A cosine-sine decomposition supplies left and right block-diagonal factors surrounding a CS matrix; Qiskit and Q# illustrate the same decomposition-oriented logic. section 3.8.3 section 3.10.2

How Quantum Programming in Depth covers it

How deep it goes
Defines the matrix structure, derives two-qubit construction and testing, then extends recursively through cosine-sine decomposition.
Examples
Uses two-qubit blocks A and B, Qiskit and Q# implementations, matrix comparisons, randomized tests, and decomposed arbitrary unitaries.
What it assumes
Assumes one-qubit unitary operations and matrix manipulation, while deriving controlled block selection from those building blocks.
What you can do after
Build it from scratch in Qiskit or Q# and verify its assembled matrix against simulation results.
How it connects
Connects multiplexers, controlled operations, cosine-sine decomposition, and arbitrary-unitary synthesis.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Quantum Programming in Depth. The section links point to the parts of the book it draws on.