Home Reference Source

src/types/qubit.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.
 */

/**
    This file defines BasicQubit, Qubit, WeakQubit and Qureg.

    A Qureg represents a list of Qubit or WeakQubit objects.
    Qubit represents a (logical-level) qubit with a unique index provided by the
    MainEngine. Qubit objects are automatically deallocated if they go out of
    scope and intented to be used within Qureg objects in user code.

 @example
      import MainEngine
      const eng = new MainEngine()
      const qubit = eng.allocateQubit()

    qubit is a Qureg of size 1 with one Qubit object which is deallocated once
    qubit goes out of scope.

    WeakQubit are used inside the Command object and are not automatically deallocated.
*/
import {arrayEqual} from '../libs/polyfill'

/**
 * @class BasicQubit
 * @desc
 * objects represent qubits. They have an id and a reference to the owning engine.
 */
export class BasicQubit {
  /**
   *  @constructor
   *  Initialize a BasicQubit object.
   *  @param {BasicEngine} engine Owning engine / engine that created the qubit
   *  @param {number} idx Unique index of the qubit referenced by this qubit
   */
  constructor(engine, idx) {
    /**
     * @type {BasicEngine}
     */
    this.engine = engine
    this.id = idx
  }

  /**
    Return string representation of this qubit.
   @return {string}
   */
  toString() {
    return `${this.id}`
  }

  /**
   *
   * @return {string}
   */
  inspect() {
    return this.toString()
  }

  /**
    Access the result of a previous measurement and return false / true (0 / 1)
    @return {boolean}
  */
  toBoolean() {
    return this.engine.main.getMeasurementResult(this)
  }

  /**
   * @return {number}
   */
  toNumber() {
    return this.toBoolean() ? 1 : 0
  }

  /**
   * Compare with other qubit (Returns true if equal id and engine).
   *
   * @param other {BasicQubit|Object} BasicQubit to which to compare this one
   * @return {boolean}
   */
  equal(other) {
    if (this === other) {
      return true
    }
    return other instanceof BasicQubit && this.id === other.id && this.engine === other.engine
  }

  weakCopy() {
    return new BasicQubit(this.engine, this.id)
  }

  static copyArray(array) {
    return array.map(i => i.weakCopy())
  }
}

/**
 * @class Qubit
 * @desc
    Represents a (logical-level) qubit with a unique index provided by the
    MainEngine. Once the qubit goes out of scope (and is garbage-collected),
    it deallocates itself automatically, allowing automatic resource management.

    Thus the qubit is not copyable only returns a reference to the same object.
 */
export class Qubit extends BasicQubit {
  deallocate() {
    // If a user directly calls this function, then the qubit gets id == -1
    // but stays in active_qubits as it is not yet deleted, hence remove
    // it manually (if the garbage collector calls this function, then the
    // WeakRef in active qubits is already gone):
    if (this.id === -1) {
      return
    }

    try {
      const qubits = this.engine.main.activeQubits
      if (qubits.has(this)) {
        qubits.delete(this)
      }
      this.engine.deallocateQubit(this)
    } catch (e) {
      throw e
    } finally {
      this.id = -1
    }
  }

  /**
    Non-copyable (returns reference to self).
    Note:
      To prevent problems with automatic deallocation, qubits are not copyable!
  */
  copy() {
    return this
  }
}

/**
 * Quantum register class.
Simplifies accessing measured values for single-qubit registers (no []-access necessary)
 and enables pretty-printing of general quantum registers).
 @class Qureg
 */
export class Qureg extends Array {
  /**
   * a little different with `Array`: when pass an array as argument, will copy the passed array
   * @constructor
   * @param {...any|number|Array} args
   */
  constructor(...args) {
    const arg0 = args[0]
    if (Array.isArray(arg0)) {
      super(...arg0)
    } else {
      super(...args)
    }
    /**
     * @ignore
     * @private
     */
    this.__proto__ = Qureg.prototype
  }

  /**
   * test if two Quregs are equal
   * @param {Qureg|Object} other
   * @return {boolean}
   */
  equal(other) {
    if (other instanceof Qureg) {
      return arrayEqual(this, other, (x, y) => x.equal(y))
    }
    return false
  }

  /**
   * only supported when `length === 1`, use the qubit value as qureg value
   * @throws {Error} will throw when `length !== 1`
   * @return {boolean}
   */
  toBoolean() {
    if (this.length === 1) {
      return this[0].toBoolean()
    }
    throw new Error('qureg.toBoolean(): Quantum register contains more "\n'
        + '"than 1 qubit. Use qureg[idx].toBoolean() instead.')
  }

  /**
   * number representation
   * @return {number}
   */
  toNumber() {
    return this.toBoolean() ? 1 : 0
  }

  /**
   * add qubits from `other`, return a new Qureg instance
   * @param {Array<Qubit>|Qureg} other
   * @return {Qureg}
   */
  add(other) {
    const array = this.concat(other)
    return new Qureg(array)
  }

  /**
   * string description
   * @return {string}
   */
  toString() {
    if (this.length === 0) return 'Qureg[]'
    const ids = this.slice(1).map(({id}) => id)
    ids.push(null) // Forces a flush on last loop iteration.

    const out_list = []
    let start_id = this[0].id
    let count = 1
    ids.forEach((qubit_id) => {
      if (qubit_id === start_id + count) {
        count += 1
      } else {
        // TODO
        if (count > 1) {
          out_list.push(`${start_id}-${start_id + count - 1}`)
        } else {
          out_list.push(`${start_id}`)
        }
        start_id = qubit_id
        count = 1
      }
    })

    return `Qureg[${out_list.join(', ')}]`
  }

  /**
   * deallocate all qubit, then clear the qureg
   */
  deallocate() {
    this.forEach(qubit => qubit.deallocate())
    this.length = 0
  }

  /**
   * @return {BasicEngine}
   */
  get engine() {
    return this[0].engine
  }

  /**
   * @param {BasicEngine} newEngine
   */
  set engine(newEngine) {
    this.forEach(looper => looper.engine = newEngine)
  }
}