Home Reference Source

src/meta/control.js

/*
 * Copyright (c) 2018 Isaac Phoenix ([email protected]).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
Contains the tools to make an entire section of operations controlled.

    @example

with Control(eng, qubit1):
H | qubit2
X | qubit3
*/

// Adds control qubits to all commands that have no compute / uncompute tags.
import {ClassicalInstructionGate} from '../ops/basics'
import {BasicQubit} from '../types/qubit'
import {BasicEngine} from '../cengines/basics'
import {dropEngineAfter, insertEngine} from './util'
import {UncomputeTag, ComputeTag} from './tag'
import {instanceOf} from '../libs/util'

/**
 * @class ControlEngine
 */
export class ControlEngine extends BasicEngine {
  /**
   * @constructor
    @param {Array.<BasicQubit>} qubits qubits conditional on which the following operations are executed.
     */
  constructor(qubits) {
    super()
    this.qubits = qubits
  }

  /**
    Return true if command cmd has a compute/uncompute tag.
    @param {Command} cmd
  */
  hasComputeUnComputeTag(cmd) {
    const tagClass = [UncomputeTag, ComputeTag]
    return cmd.tags.some(looper => instanceOf(looper, tagClass))
  }

  handleCommand(cmd) {
    if (!this.hasComputeUnComputeTag(cmd) && !(cmd.gate instanceof ClassicalInstructionGate)) {
      cmd.addControlQubits(this.qubits)
    }
    this.send([cmd])
  }

  receive(commandList) {
    commandList.forEach(cmd => this.handleCommand(cmd))
  }
}

/**
Condition an entire code block on the value of qubits being 1.

@example

with Control(eng, ctrlqubits)
do_something(otherqubits)
 Enter a controlled section.

 @param {BasicEngine} engine Engine which handles the commands (usually MainEngine)
 @param {Array.<BasicQubit>} qubits Qubits to condition on
 @param {function} func
 Enter the section using a with-statement
 @example
 Control(eng, ctrlqubits, () => ...)
 */
export function Control(engine, qubits, func) {
  if (qubits instanceof BasicQubit) {
    qubits = [qubits]
  }
  const qs = qubits

  const enter = () => {
    if (qs.length > 0) {
      const ce = new ControlEngine(qs)
      insertEngine(engine, ce)
    }
  }

  const exit = () => {
    if (qs.length > 0) {
      dropEngineAfter(engine)
    }
  }

  if (typeof func === 'function') {
    enter()
    try {
      func()
    } catch (e) {
      throw e
    } finally {
      exit()
    }
  }
}