alexandrupaler / fondq

10 stars 1 forks source link

"Simplify flipped CNOTs" exercise in intro colab has wrong solution #1

Open amirebrahimi opened 4 years ago

amirebrahimi commented 4 years ago

This isn't an issue with fondq, but I'm new to colabs, so I'm not sure how to suggest a fix to a colab. If there is a repo where I can submit a PR, then let me know.

In tutorial_01, "Introduction - Colab" from Cirq for NISQ near the end there is an exercise to create a Point Optimizer for CNOTs sandwiched with two H gates. The solution is only a redundant Hadamard optimizer. Perhaps the exercise changed after or vice versa. Anyhow, I wrote a HCXHOptimizer that you can throw in the Solution panel:

class HCXHOptimizer(cirq.PointOptimizer):
    """Replaces a CNOT sandwiched by H gates with a flipped CNOT."""

    # The circuit to improve
    # The index of the moment with the operation to focus on
    # The operation to focus improvements upon
    def optimization_at(self, circuit, index, op):
        if isinstance(op, cirq.GateOperation) and (op.gate == cirq.H):

            # Finds the index of the next moment that touches the given qubits.
            next_op_index = circuit.next_moment_operating_on(op.qubits, index + 1)
            qubit = op.qubits[0]

            # If the next index exists
            if next_op_index is not None:

                # Get the operation at the existing index
                next_op = circuit.operation_at(qubit, next_op_index)

                if isinstance(next_op, cirq.GateOperation) and  (next_op.gate == cirq.CNOT):
                    other_qubit = next_op.qubits[1]
                    cnot_index = next_op_index
                    cnot_op = next_op

                    next_op_index = circuit.next_moment_operating_on(op.qubits, next_op_index + 1)
                    next_op = circuit.operation_at(qubit, next_op_index)

                    if isinstance(next_op, cirq.GateOperation) and (next_op.gate == cirq.H):
                      other_op_index = circuit.prev_moment_operating_on(cnot_op.qubits, cnot_index)
                      other_op = circuit.operation_at(other_qubit, other_op_index)

                      if isinstance(other_op, cirq.GateOperation) and (other_op.gate == cirq.H):
                        other_op_index = circuit.next_moment_operating_on(cnot_op.qubits, cnot_index + 1)
                        other_op = circuit.operation_at(other_qubit, other_op_index)

                        if isinstance(other_op, cirq.GateOperation) and (other_op.gate == cirq.H):                      
                          new_op = cirq.CNOT(other_qubit, qubit)

                          # see https://cirq.readthedocs.io/en/stable/generated/cirq.PointOptimizationSummary.html?highlight=pointoptimizationsummary
                          return cirq.PointOptimizationSummary(
                              clear_span = next_op_index - index + 1, # Range of moments to affect. 
                              clear_qubits = cnot_op.qubits, # The set of qubits that should be cleared with each affected moment
                              new_operations = [new_op] # The operations to replace
                          )
        return None

# The optimizer
opt = HCXHOptimizer()

a = cirq.NamedQubit("a")
b = cirq.NamedQubit("b")

# Unoptimized circuit
circuit = cirq.Circuit(cirq.H(a), cirq.H(b), cirq.CNOT(a, b), cirq.H(a), cirq.H(b))
print('Before\n{}\n'. format(circuit))

# Optimized circuit
opt.optimize_circuit(circuit)
print('After HCX opt\n{}\n'.format(circuit))

# We can remove the empty moments
remempty = cirq.DropEmptyMoments()
remempty.optimize_circuit(circuit)
print('After Drop opt\n{}\n'.format(circuit))
alexandrupaler commented 4 years ago

Thank you very much. I will take care of incorporating this into fondq.