qat.vsolve.ansatz.AnsatzFactory

class qat.vsolve.ansatz.AnsatzFactory

This class regroups the implementation all the different Ansätze available in the QLM.

static qaoa_circuit(observable, depth, strategy='default', cnots=True, **to_circ_args)

Generates a QAOA Ansatz from an observable and an Ansatz depth

Example

from qat.core import Observable, Term
from qat.vsolve.ansatz import AnsatzFactory

line_obs = Observable(10)
for i in range(9):
    line_obs.add_term(Term(1., "ZZ", [i, i+1]))
ansatz_with_cnots = AnsatzFactory.qaoa_circuit(line_obs, 3)
print("The Ansatz has {} gates".format(len(ansatz_with_cnots.ops)))
ansatz_with_rzz = AnsatzFactory.qaoa_circuit(line_obs, 3, cnots=False)
print("The Ansatz has {} gates".format(len(ansatz_with_rzz.ops)))
The Ansatz has 121 gates
The Ansatz has 67 gates

The synthesis strategy may influence the depth of the circuit:

def depth(circuit):
    ''' Computes the depth of a circuit '''
    slices = [set()]
    for op in circuit:
        qbits = op.qbits
        insert_in = None
        for index, slic in enumerate(reversed(slices)):
            if all(qb not in slic for qb in qbits):
                continue
            insert_in = index
            break
        if insert_in is None:
            for qb in qbits:
                slices[0].add(qb)
        elif insert_in == 0:
            slices.append(set(qbits))
        else:
            for qb in qbits:
                slices[len(slices) - insert_in].add(qb)
    return len(slices)

from qat.core import Observable, Term
from qat.vsolve.ansatz import AnsatzFactory

line_obs = Observable(10)
for i in range(9):
    line_obs.add_term(Term(1., "ZZ", [i, i+1]))
ansatz_default = AnsatzFactory.qaoa_circuit(line_obs, 3, strategy="default")
print("The Ansatz has depth {}".format(depth(ansatz_default)))
ansatz_coloring = AnsatzFactory.qaoa_circuit(line_obs, 3, strategy="coloring")
print("The Ansatz has depth {}".format(depth(ansatz_coloring)))
The Ansatz has depth 43
The Ansatz has depth 22

When considering QAOA instances with large Clauses (i.e clauses with more than 2 variables), the “gray_synth” strategy can often remove lots of CNOTS:

def cnot_count(circuit):
    ''' count cnots in a circuit '''
    return sum(1 if name == "CNOT" else 0
               for name, args, qbits in circuit.iterate_simple())

from qat.core import Observable, Term
from qat.vsolve.ansatz import AnsatzFactory

n = 5
line_obs = Observable(n)
for i in range(n - 2):
    line_obs.add_term(Term(1., "ZZZ", [i, i+1, i+2]))

ansatz_default = AnsatzFactory.qaoa_circuit(line_obs, 3, strategy="default")
ansatz_gray_synth = AnsatzFactory.qaoa_circuit(line_obs, 3, strategy="gray_synth")
print("Cnot count in default:", cnot_count(ansatz_default))
print("Cnot count in gray synth:", cnot_count(ansatz_gray_synth))
Cnot count in default: 36
Cnot count in gray synth: 24

Synthesis strategies:

  • default: uses the default term ordering provided by the input observable

  • coloring: orders terms using a graph coloring technique in order to reduce circuit depth

  • gray_synth: uses Amy et al GraySynth algorithm to synthesize the entangling layer. This might help in reducing the overall CNOT count.

Parameters
  • observable (Observable) – some diagonal observable

  • depth (int) – the depth of the Ansatz

  • strategy (str) – the strategy to adopt to generate the circuit.

  • cnots (optional, bool) – if set to True, the generator will onlt use CNOT gates as entangling gates. Default to True. This argument is ignored for some strategies.

  • **to_circ_args – arguments passed to the to_circ method