Home Reference Source

src/backends/printer.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 a compiler engine which prints commands to stdout prior to sending
them on to the next engines (see CommandPrinter).
*/
import assert from 'assert'
import {BasicEngine} from '../cengines/basics'
import {FlushGate, Measure} from '../ops/gates'
import {LogicalQubitIDTag} from '../meta/tag'
import {BasicQubit} from '../types/qubit'
import { LastEngineError } from '../meta/error'

/**
 * @class CommandPrinter
 * @desc
 * CommandPrinter is a compiler engine which prints commands to stdout prior
 * to sending them on to the next compiler engine.
 */
export default class CommandPrinter extends BasicEngine {
  /**
   * @constructor
  @param {boolean} acceptInput If accept_input is true, the printer queries
  the user to input measurement results if the CommandPrinter is
  the last engine. Otherwise, all measurements yield
  @param {boolean} defaultMeasure Default measurement result (if accept_input is false).
  @param {boolean} inPlace If in_place is true, all output is written on the same line of the terminal.
  */
  constructor(acceptInput = true, defaultMeasure = false, inPlace = false) {
    super()
    this._acceptInput = acceptInput
    this._defaultMeasure = defaultMeasure
    this._inPlace = inPlace
  }

  /**
    Specialized implementation of isAvailable: Returns true if the
    CommandPrinter is the last engine (since it can print any command).

    @param {Command} cmd Command of which to check availability (all Commands can be printed).
    @return {boolean} true, unless the next engine cannot handle the Command (if there is a next engine).
   */
  isAvailable(cmd) {
    try {
      return super.isAvailable(cmd)
    } catch (e) {
      if (e instanceof LastEngineError) {
        return true
      }
    }
    return false
  }

  /**
    Print a command or, if the command is a measurement instruction and
    the CommandPrinter is the last engine in the engine pipeline: Query
    the user for the measurement result (if accept_input = true) / Set
    the result to 0 (if it's false).

    @param {Command} cmd Command to print.
   */
  printCMD(cmd) {
    if (this.isLastEngine && cmd.gate.equal(Measure)) {
      assert(cmd.controlCount === 0)
      console.log(cmd.toString())
      cmd.qubits.forEach((qureg) => {
        qureg.forEach((qubit) => {
          // ignore input
          const m = this._defaultMeasure
          let logicQubitTag
          cmd.tags.forEach((tag) => {
            if (tag instanceof LogicalQubitIDTag) {
              logicQubitTag = tag
            }
          })

          if (logicQubitTag) {
            qubit = new BasicQubit(qubit.engine, logicQubitTag.logical_qubit_id)
          }
          this.main.setMeasurementResult(qubit, m)
        })
      })
    } else if (this._inPlace) {
      console.log(`\0\r\t\x1b[K${cmd.toString()}\r`)
    } else {
      console.log(cmd.toString())
    }
  }

  /**
  Receive a list of commands from the previous engine, print the
commands, and then send them on to the next engine.

    @param {Command[]} commandList List of Commands to print (and potentially send on to the next engine).
   */
  receive(commandList) {
    commandList.forEach((cmd) => {
      if (!(cmd.gate instanceof FlushGate)) {
        this.printCMD(cmd)
      }
      if (!this.isLastEngine) {
        this.send([cmd])
      }
    })
  }
}