Home Reference Source

src/setups/grid.js


import assert from 'assert'
import {
  BasicMathGate, ClassicalInstructionGate, CNOT, ControlledGate, QFT, Swap
} from '../ops';
import {getInverse} from '../ops/_cycle';
import DecompositionRuleSet from '../cengines/replacer/decompositionruleset';
import {instanceOf, isKindclassOf} from '../libs/util';
import {AutoReplacer, InstructionFilter} from '../cengines';
import TagRemover from '../cengines/tagremover'
import LocalOptimizer from '../cengines/optimize'
import math from '../libs/math/defaultrules'
import decompositions from './decompositions'
import GridMapper from '../cengines/twodmapper';

/**
 * @ignore
 * @desc Remove any MathGates
 */
export function high_level_gates(eng, cmd) {
  const {gate} = cmd
  if (gate.equal(QFT) || getInverse(gate).equal(QFT) || gate.equal(Swap)) {
    return true
  }
  if (gate instanceof BasicMathGate) {
    return false
  }
  return true
}

function one_and_two_qubit_gates(eng, cmd) {
  const allqubits = []
  cmd.allQubits.forEach(qr => qr.forEach(q => allqubits.push(q)))

  // This is required to allow Measure, Allocate, Deallocate, Flush
  if (cmd.gate instanceof ClassicalInstructionGate) {
    return true
  } else if (allqubits.length <= 2) {
    return true
  }
  return false
}

/**
 * @desc
 * Returns an engine list to compile to a 2-D grid of qubits.

 Note:
 If you choose a new gate set for which the compiler does not yet have
 standard rules, it raises an `NoGateDecompositionError` or a
 `RuntimeError: maximum recursion depth exceeded...`. Also note that
 even the gate sets which work might not yet be optimized. So make sure
 to double check and potentially extend the decomposition rules.
 This implemention currently requires that the one qubit gates must
 contain Rz and at least one of {Ry(best), Rx, H} and the two qubit gate
 must contain CNOT (recommended) or CZ.

 Note:
 Classical instructions gates such as e.g. Flush and Measure are
 automatically allowed.

 @example
  getEngineList(2, 3, tuple(Rz, Ry, Rx, H), tuple(CNOT))

 @param {number} num_rows Number of rows in the grid
 @param {number} num_columns Number of columns in the grid.
 @param {string|Array.<BasicGate>} one_qubit_gates "any" allows any one qubit gate, otherwise provide
   a tuple of the allowed gates. If the gates are instances of a class (e.g. X), it allows all gates
   which are equal to it. If the gate is a class (Rz), it allows all instances of this class. Default is "any"
 @param {string|Array.<BasicGate>} two_qubit_gates "any" allows any two qubit gate, otherwise provide
   a tuple of the allowed gates. If the gates are instances of a class (e.g. CNOT), it allows all gates
   which are equal to it. If the gate is a class, it allows all instances of this class.
   Default is (CNOT, Swap).
 @throws {Error} If input is for the gates is not "any" or a tuple.

 @return {Array<BasicEngine>} A list of suitable compiler engines.
 */
export function getEngineList(num_rows, num_columns, one_qubit_gates = 'any', two_qubit_gates = [CNOT, Swap]) {
  if (two_qubit_gates !== 'any' && !Array.isArray(two_qubit_gates)) {
    throw new Error("two_qubit_gates parameter must be 'any' or a tuple. "
        + 'When supplying only one gate, make sure to correctly '
        + "create the tuple (don't miss the comma), "
        + 'e.g. tuple(CNOT)')
  }
  if (one_qubit_gates !== 'any' && !Array.isArray(one_qubit_gates)) {
    throw new Error("one_qubit_gates parameter must be 'any' or a tuple.")
  }
  const rule_set = new DecompositionRuleSet([...math, ...decompositions])
  const allowed_gate_classes = []
  const allowed_gate_instances = []

  if (one_qubit_gates !== 'any') {
    one_qubit_gates.forEach((gate) => {
      if (typeof gate === 'function') {
        allowed_gate_classes.push(gate)
      } else {
        allowed_gate_instances.push([gate, 0])
      }
    })
  }
  if (two_qubit_gates !== 'any') {
    two_qubit_gates.forEach((gate) => {
      if (typeof gate === 'function') {
        //  Controlled gate classes don't yet exists and would require
        //  separate treatment
        assert(!isKindclassOf(gate, ControlledGate))
        allowed_gate_classes.push(gate)
      } else if (gate instanceof ControlledGate) {
        allowed_gate_instances.push([gate.gate, gate.n])
      } else {
        allowed_gate_instances.push([gate, 0])
      }
    })
  }

  function low_level_gates(eng, cmd) {
    const allqubits = []
    cmd.allQubits.forEach(qr => qr.forEach(q => allqubits.push(q)))

    assert(allqubits.length <= 2)
    if (cmd.gate instanceof ClassicalInstructionGate) {
      // This is required to allow Measure, Allocate, Deallocate, Flush
      return true
    } else if (one_qubit_gates === 'any' && allqubits.length === 1) {
      return true
    } else if (two_qubit_gates === 'any' && allqubits.length === 2) {
      return true
    } else if (instanceOf(cmd.gate, allowed_gate_classes)) {
      return true
    } else {
      const cn = cmd.controlQubits.length
      const idx = allowed_gate_instances.findIndex((looper) => {
        try {
          return cmd.gate.equal(looper[0]) && cn === looper[1]
        } catch (e) {
          return false
        }
      })
      if (idx !== -1) {
        return true
      }
    }
    return false
  }
  return [
    new AutoReplacer(rule_set),
    new TagRemover(),
    new InstructionFilter(high_level_gates),
    new LocalOptimizer(5),
    new AutoReplacer(rule_set),
    new TagRemover(),
    new InstructionFilter(one_and_two_qubit_gates),
    new LocalOptimizer(5),
    new GridMapper({num_rows, num_columns}),
    new AutoReplacer(rule_set),
    new TagRemover(),
    new InstructionFilter(low_level_gates),
    new LocalOptimizer(5),
  ]
}