Z-register Operations

Operations on the Z-register allow users to manipulate complex-valued variables inside a quantum circuit. This section covers all the information needed to perform operations on the complex-valued variables stored in the Z-register.

What is the Z-register?

In MIMIQ, complex-valued variables are stored in a special register called the Z-register. Practically speaking, the Z-register is just a vector of complex numbers (Complex values) that can be manipulated with circuit operations, similarly to the quantum register (qubits) or the classical register (bits).

When visualizing a circuit, Z-register indices are represented with the letter z.

What can you do with the Z-register?

Operations involving the Z-register can be broadly divided into two types:

  • Operations that store information into the Z-register.

  • Operations that manipulate information already stored in the Z-register.

Storing information in the Z-register

Some operations compute values from the quantum state and store the result into the Z-register:

  • ExpectationValue: Computes the expectation value of an observable.

  • Amplitude: Stores the complex amplitude of a bitstring.

>>> c = Circuit()
>>> c.push(GateH(), 1)
2-qubit circuit with 1 instructions:
└── H @ q[1]

>>> c.push(GateCX(), 1, 2)
3-qubit circuit with 2 instructions:
├── H @ q[1]
└── CX @ q[1], q[2]

>>> c.push(ExpectationValue(PauliString("XX")), 1, 2, 1)  # qubits 1,2 -> z[1]
3-qubit, 2-zvar circuit with 3 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
└── ⟨XX⟩ @ q[1,2], z[1]

>>> c.push(ExpectationValue(PauliString("ZZ")), 1, 2, 2)  # qubits 1,2 -> z[2]
3-qubit, 3-zvar circuit with 4 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── ⟨XX⟩ @ q[1,2], z[1]
└── ⟨ZZ⟩ @ q[1,2], z[2]

>>> c.push(Amplitude(BitString(2)), 3)                      # bitstring -> z[3]
3-qubit, 4-zvar circuit with 5 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── ⟨XX⟩ @ q[1,2], z[1]
├── ⟨ZZ⟩ @ q[1,2], z[2]
└── Amplitude(bs"00") @ z[3]

Manipulating the Z-register

The following operations perform arithmetic on Z-register variables:

  • Add: Adds Z-register variables.

  • Multiply: Multiplies Z-register variables.

  • Pow: Raises a Z-register variable to a power.

>>> c.push(Add(3), 4, 1, 2)         # z[4] = z[1] + z[2]
3-qubit, 5-zvar circuit with 6 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── ⟨XX⟩ @ q[1,2], z[1]
├── ⟨ZZ⟩ @ q[1,2], z[2]
├── Amplitude(bs"00") @ z[3]
└── z[4] += 0.0 + z[1] + z[2]

>>> c.push(Multiply(3), 5, 1, 2)    # z[5] = z[1] * z[2]
3-qubit, 6-zvar circuit with 7 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── ⟨XX⟩ @ q[1,2], z[1]
├── ⟨ZZ⟩ @ q[1,2], z[2]
├── Amplitude(bs"00") @ z[3]
├── z[4] += 0.0 + z[1] + z[2]
└── z[5] *= 1.0 * z[1] * z[2]

>>> c.push(Multiply(1, 0.2), 3)     # z[3] *= 0.2
3-qubit, 6-zvar circuit with 8 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── ⟨XX⟩ @ q[1,2], z[1]
├── ⟨ZZ⟩ @ q[1,2], z[2]
├── Amplitude(bs"00") @ z[3]
├── z[4] += 0.0 + z[1] + z[2]
├── z[5] *= 1.0 * z[1] * z[2]
└── z[3] *= 0.2

>>> c.push(Pow(-1), 3)              # z[3] = z[3] ** -1
3-qubit, 6-zvar circuit with 9 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── ⟨XX⟩ @ q[1,2], z[1]
├── ⟨ZZ⟩ @ q[1,2], z[2]
├── Amplitude(bs"00") @ z[3]
├── z[4] += 0.0 + z[1] + z[2]
├── z[5] *= 1.0 * z[1] * z[2]
├── z[3] *= 0.2
└── z[3] = z[3]^(-1)

Additionally, a constant value can be added or multiplied in. For instance, Add(2, 0.5) adds 0.5 to both inputs before summing them.

Example: Ising Hamiltonian Expectation Value (Energy)

A more complex example is the computation of the expectation value of a 1D Ising Hamiltonian with transverse field:

\[H = - J \sum_{j=1}^{N-1} \sigma^z_j \sigma^z_{j+1} - h \sum_{j=1}^N \sigma^x_j\]

where ( sigma^z_j ) and ( sigma^x_j ) are Pauli matrices on the ( j )-th qubit, ( J ) is the coupling constant, and ( h ) is the transverse magnetic field.

The circuit to compute the expectation value is:

Example: Ising Hamiltonian Expectation Value (Energy)

A more complex example is the computation of the expectation value of a 1D Ising Hamiltonian with transverse field:

\[H = - J \sum_{j=0}^{N-2} \sigma^z_j \sigma^z_{j+1} - h \sum_{j=0}^{N-1} \sigma^x_j\]

where ( sigma^z_j ) and ( sigma^x_j ) are Pauli matrices on the ( j )-th qubit, ( J ) is the coupling constant, and ( h ) is the transverse magnetic field.

The circuit to compute the expectation value is:

>>> from mimiqcircuits import *

>>> N = 4        # number of qubits
>>> J = 1.0      # coupling strength
>>> h = 0.5      # transverse field

>>> c = Circuit()

>>> for j in range(N - 1):
...     newz = c.num_zvars()
...     _ = c.push(ExpectationValue(PauliString("ZZ")), j, j + 1, newz)
...     _ = c.push(Multiply(1, -J), newz)

>>> c
4-qubit, 3-zvar circuit with 6 instructions:
├── ⟨ZZ⟩ @ q[0,1], z[0]
├── z[0] *= -1.0
├── ⟨ZZ⟩ @ q[1,2], z[1]
├── z[1] *= -1.0
├── ⟨ZZ⟩ @ q[2,3], z[2]
└── z[2] *= -1.0


>>> for j in range(N):
...     newz = c.num_zvars()
...     _ = c.push(ExpectationValue(GateX()), j, newz)
...     _ = c.push(Multiply(1, -h), newz)

>>> c
4-qubit, 7-zvar circuit with 14 instructions:
├── ⟨ZZ⟩ @ q[0,1], z[0]
├── z[0] *= -1.0
├── ⟨ZZ⟩ @ q[1,2], z[1]
├── z[1] *= -1.0
├── ⟨ZZ⟩ @ q[2,3], z[2]
├── z[2] *= -1.0
├── ⟨X⟩ @ q[0], z[3]
├── z[3] *= -0.5
├── ⟨X⟩ @ q[1], z[4]
├── z[4] *= -0.5
├── ⟨X⟩ @ q[2], z[5]
├── z[5] *= -0.5
├── ⟨X⟩ @ q[3], z[6]
└── z[6] *= -0.5


>>> total_terms = c.num_zvars()
>>> _ = c.push(Add(total_terms), *range(total_terms))
>>> c
4-qubit, 7-zvar circuit with 15 instructions:
├── ⟨ZZ⟩ @ q[0,1], z[0]
├── z[0] *= -1.0
├── ⟨ZZ⟩ @ q[1,2], z[1]
├── z[1] *= -1.0
├── ⟨ZZ⟩ @ q[2,3], z[2]
├── z[2] *= -1.0
├── ⟨X⟩ @ q[0], z[3]
├── z[3] *= -0.5
├── ⟨X⟩ @ q[1], z[4]
├── z[4] *= -0.5
├── ⟨X⟩ @ q[2], z[5]
├── z[5] *= -0.5
├── ⟨X⟩ @ q[3], z[6]
├── z[6] *= -0.5
└── z[0] += 0.0 + z[1] + z[2] + z[3] + z[4] + z[5] + z[6]

Reference

class mimiqcircuits.Add(N=1, c=0.0)[source]

Bases: Operation

Add several z-register variables between them and optionally a constant. The result is strored in the first z-register variable given.

Examples

>>> from mimiqcircuits import *
>>> Add(1)
z[?0] += 0.0
>>> Add(1,c=1)
z[?0] += 1
>>> Add(3,1)
z[?0] += 1 + z[?1] + z[?2]
>>> Add(4, c=3)
z[?0] += 3 + z[?1] + z[?2] + z[?3]
>>> c = Circuit()
>>> c.push(Add(2), 1, 2)
3-zvar circuit with 1 instruction:
└── z[1] += 0.0 + z[2]
__init__(N=1, c=0.0)[source]
iswrapper()[source]
format_with_targets(qubits, bits, zvars)[source]
class mimiqcircuits.Multiply(N=1, c=1.0)[source]

Bases: Operation

Multiply several z-register variables between them and optionally a constant. The result is strored in the first z-register variable given.

Examples

>>> from mimiqcircuits import *
>>> Multiply(1)
z[?0] *= 1.0
>>> Multiply(3)
z[?0] *= 1.0 * z[?1] * z[?2]
>>> Multiply(3, c=2)
z[?0] *= 2 * z[?1] * z[?2]
>>> Multiply(2,2)
z[?0] *= 2 * z[?1]
>>> c = Circuit()
>>> c.push(Multiply(2), 1, 2)
3-zvar circuit with 1 instruction:
└── z[1] *= 1.0 * z[2]
__init__(N=1, c=1.0)[source]
iswrapper()[source]
format_with_targets(qubits, bits, zvars)[source]
class mimiqcircuits.Pow(exponent)[source]

Bases: Operation

Raises a z-variable to a given power (passed as a parameter of the operation).

Examples

>>> from mimiqcircuits import *
>>> Pow(-1)
z[?] = z[?]^(-1)
>>> c = Circuit()
>>> c.push(Pow(2), 1)
2-zvar circuit with 1 instruction:
└── z[1] = z[1]^2
__init__(exponent)[source]
inverse()[source]
iswrapper()[source]
format_with_targets(qubits, bits, zvars)[source]