Home Reference Source

src/ops/uniformly_controlled_rotation.js

import math from 'mathjs'
import deepEqual from 'deep-eql'
import {BasicGate, ANGLE_PRECISION, ANGLE_TOLERANCE} from './basics'
import {NotMergeable} from '../meta/error'

/**
 * @param {number[]} angles
 * @return {number[]}
 */
function roundAngles(angles) {
  const rounded_angles = []
  angles.forEach((angle) => {
    let newAngle = math.round(angle % (4 * math.pi), ANGLE_PRECISION)
    if (newAngle > 4 * math.pi - ANGLE_TOLERANCE) {
      newAngle = 0
    }
    if (Object.is(newAngle, -0)) {
      newAngle = 0
    }
    rounded_angles.push(newAngle)
  })
  return rounded_angles
}

/**
 * Uniformly controlled Ry gate as introduced in arXiv:quant-ph/0312218.

 This is an n-qubit gate. There are n-1 control qubits and one target qubit.
 This gate applies Ry(angles(k)) to the target qubit if the n-1 control
 qubits are in the classical state k. As there are 2^(n-1) classical
 states for the control qubits, this gate requires 2^(n-1) (potentially
 different) angle parameters.

 Example:
 .. code-block:: python

 controls = eng.allocate_qureg(2)
 target = eng.allocate_qubit()
 UniformlyControlledRy(angles=[0.1, 0.2, 0.3, 0.4]) | (controls, target)

 Note:
 The first quantum register contains the control qubits. When converting
 the classical state k of the control qubits to an integer, we define
 controls[0] to be the least significant (qu)bit. controls can also
 be an empty list in which case the gate corresponds to an Ry.

 Args:
 angles(list[float]): Rotation angles. Ry(angles[k]) is applied
 conditioned on the control qubits being in state
 k.
 * @class UniformlyControlledRy
 */
export class UniformlyControlledRy extends BasicGate {
  /**
   * @constructor
   * @param {number[]} angles
   */
  constructor(angles) {
    super()
    this.angles = roundAngles(angles)
  }

  getInverse() {
    return new UniformlyControlledRy(this.angles.map(angle => -angle))
  }

  getMerged(other) {
    if (other instanceof UniformlyControlledRy) {
      const angles = other.angles.map((a1, idx) => a1 + this.angles[idx])
      return new UniformlyControlledRy(angles)
    } else {
      throw new NotMergeable()
    }
  }

  toString() {
    const str = this.angles.map(a => a.toString()).join(', ')
    return `UniformlyControlledRy([${str}])`
  }

  equal(other) {
    if (other instanceof UniformlyControlledRy) {
      return deepEqual(this.angles, other.angles)
    }
    return false
  }
}

/**
 * Uniformly controlled Rz gate as introduced in arXiv:quant-ph/0312218.

 This is an n-qubit gate. There are n-1 control qubits and one target qubit.
 This gate applies Rz(angles(k)) to the target qubit if the n-1 control
 qubits are in the classical state k. As there are 2^(n-1) classical
 states for the control qubits, this gate requires 2^(n-1) (potentially
 different) angle parameters.

 Example:
 .. code-block:: python

 controls = eng.allocate_qureg(2)
 target = eng.allocate_qubit()
 UniformlyControlledRz(angles=[0.1, 0.2, 0.3, 0.4]) | (controls, target)

 Note:
 The first quantum register are the contains qubits. When converting
 the classical state k of the control qubits to an integer, we define
 controls[0] to be the least significant (qu)bit. controls can also
 be an empty list in which case the gate corresponds to an Rz.

 Args:
 angles(list[float]): Rotation angles. Rz(angles[k]) is applied
 conditioned on the control qubits being in state
 k.
 @class UniformlyControlledRz
 */
export class UniformlyControlledRz extends BasicGate {
  /**
   * @constructor
   * @param {number[]} angles
   */
  constructor(angles) {
    super()
    this.angles = roundAngles(angles)
  }

  getInverse() {
    return new UniformlyControlledRz(this.angles.map(angle => -angle))
  }

  getMerged(other) {
    if (other instanceof UniformlyControlledRz) {
      const angles = other.angles.map((a1, idx) => a1 + this.angles[idx])
      return new UniformlyControlledRz(angles)
    } else {
      throw new NotMergeable()
    }
  }

  toString() {
    const str = this.angles.map(a => a.toString()).join(', ')
    return `UniformlyControlledRz([${str}])`
  }

  equal(other) {
    if (other instanceof UniformlyControlledRz) {
      return deepEqual(this.angles, other.angles)
    }
    return false
  }
}