.. role:: raw-latex(raw) :format: latex .. Types and Casting ================= .. _identifiers: Identifiers ----------- Identifiers must begin with a letter [A-Za-z], an underscore or an element from the Unicode character categories Lu/Ll/Lt/Lm/Lo/Nl :cite:`wikipediaUnicode`. The set of permissible continuation characters consists of all members of the aforementioned character sets with the addition of decimal numerals [0-9]. Identifiers may not override a reserved identifier. .. _variables: Variables --------- Variables must be named according to the rules for identifiers (See :ref:`identifiers`). Variables may be assigned values within a program. Variables representing any classical type can be initialized on declaration. Any classical variable or Boolean that is not explicitly initialized is undefined. Classical types can be mutually cast to one another using the typename. See :ref:`castingSpecifics` for more details on casting. Declaration and initialization must be done one variable at a time for both quantum and classical types. Comma seperated declaration/initialization (``int x, y, z``) is NOT allowed for any type. For example, to declare a set of qubits one must do .. code-block:: qubit q0; qubit q1; qubit q2; and to declare a set of classical variables .. code-block:: int[32] a; float[32] b = 5.5; bit[3] c; bool my_bool = false; We use the notation ``s:m:f`` to denote the width and precision of fixed point numbers, where ``s`` is the number of sign bits, ``m`` is the number of integer bits, and ``f`` is the number of fractional bits. It is necessary to specify low-level classical representations since OpenQASM operates at the intersection of gates/analog control and digital feedback and we need to be able to explicitly transform types to cross these boundaries. Classical types are scoped to the braces within which they are declared. Quantum types ------------- Qubits ~~~~~~ There is a quantum bit (``qubit``) type that is interpreted as a reference to a two-level subsystem of a quantum state. The statement ``qubit name;`` declares a reference to a quantum bit. These qubits are referred to as "virtual qubits" (in distinction to "physical qubits" on actual hardware; see below). The statement ``qubit[size] name;`` declares a quantum register with ``size`` qubits. Sizes must always be :ref:`compile-time constant ` positive integers. Quantum registers are static arrays of qubits that cannot be dynamically resized. The label ``name[j]`` refers to a qubit of this register, where :math:`j\in \{0,1,\dots,\mathrm{size}(\mathrm{name})-1\}` is an integer. .. note:: To be compliant with the base OpenQASM 3.0 specification, an implementation is only required to allow this "quantum-register indexing" with a :ref:`compile-time constant value ` (those with ``const`` types). Implementations are permitted to treat indexing into a quantum register with a value of non-\ ``const`` type as an error. Consult your compiler and hardware documentation for details. .. code-block:: // Valid statements include "stdgates.inc"; qubit[5] q1; const uint SIZE = 4; uint runtime_u = 2; qubit[SIZE] q2; // Declare a 4-qubit register. x q1[0]; z q2[SIZE - 2]; // The index operand is of type `const uint`. // Validity is implementation-defined. x q1[runtime_u]; // Indexing with a value with a non-`const` type (`uint`, in this case) is // not guaranteed to be supported. The keyword ``qreg`` is included for backwards compatibility and will be removed in the future. Qubits are initially in an undefined state. A quantum ``reset`` operation is one way to initialize qubit states. All qubits are global variables. Qubits cannot be declared within gates or subroutines. This simplifies OpenQASM significantly since there is no need for quantum memory management. However, it also means that users or compiler have to explicitly manage the quantum memory. Physical Qubits ~~~~~~~~~~~~~~~ While program qubits can be named, hardware qubits are referenced only by the syntax ``$[NUM]``. For an ``n`` qubit system, we have physical qubit references given by ``$0``, ``$1``, ..., ``$n-1``. These qubit types are used in lower parts of the compilation stack when emitting physical circuits. Physical qubits must not be declared and they are, as all the qubits, global variables. .. code-block:: // Declare a qubit qubit gamma; // Declare a qubit with a Unicode name qubit γ; // Declare a qubit register with 20 qubits qubit[20] qubit_array; // CNOT gate between physical qubits 0 and 1 CX $0, $1; Classical scalar types ---------------------- Classical bits and registers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There is a classical bit type that takes values 0 or 1. Classical registers are static arrays of bits. The classical registers model part of the controller state that is exposed within the OpenQASM program. The statement ``bit name;`` declares a classical bit, and or ``bit[size] name;`` declares a register of ``size`` bits. The label ``name[j]`` refers to a bit of this register, where :math:`j\in \{0,1,\dots,\mathrm{size}(\mathrm{name})-1\}` is an integer. Bit registers may also be declared as ``creg name[size]``. This is included for backwards compatibility and may be removed in the future. For convenience, classical registers can be assigned a text string containing zeros and ones of the same length as the size of the register. It is interpreted to assign each bit of the register to corresponding value 0 or 1 in the string, where the least-significant bit is on the right. .. code-block:: // Declare a register of 20 bits bit[20] bit_array; // Declare and assign a register of bits with decimal value of 15 bit[8] name = "00001111"; Integers ~~~~~~~~ There are n-bit signed and unsigned integers. The statements ``int[size] name;`` and ``uint[size] name;`` declare signed 1:n-1:0 and unsigned 0:n:0 integers of the given size. The sizes and the surrounding brackets can be omitted (*e.g.* ``int name;``) to use a precision that is specified by the particular target architecture. Bit-level operations cannot be used on types without a specified width, and unspecified-width types are different to *all* specified-width types for the purposes of casting. Because register indices are integers, they can be cast from classical registers containing measurement outcomes and may only be known at run time. An n-bit classical register containing bits can also be reinterpreted as an integer, and these types can be mutually cast to one another using the type name, e.g. ``int[16](c)``. As noted, this conversion will be done assuming little-endian bit ordering. The example below demonstrates how to declare, assign and cast integer types amongst one another. .. code-block:: // Declare a 32-bit unsigned integer uint[32] my_uint = 10; // Declare a 16 bit signed integer int[16] my_int; my_int = int[16](my_uint); // Declare a machine-sized integer int my_machine_int; Floating point numbers ~~~~~~~~~~~~~~~~~~~~~~ IEEE 754 floating point registers may be declared with ``float[size] name;``, where ``float[64]`` would indicate a standard double-precision float. Note that some hardware vendors may not support manipulating these values at run-time. Similar to integers, floating-point registers can be declared with an unspecified size. The resulting precision is then set by the particular target architecture, and the unspecified-width type is different to all specified-width types for the purposes of casting. .. code-block:: // Declare a single-precision 32-bit float float[32] my_float = π; // Declare a machine-precision float. float my_machine_float = 2.3; .. _void-type: Void type ~~~~~~~~~ Subroutines and externs that do not return a value implicitly return ``void``. The ``void`` type is unrealizable and uninstantiable, and thus cannot be attached to an identifier or used as a cast operator. The keyword ``void`` is reserved for potential future use. .. _angle-type: Angles ~~~~~~ OpenQASM 3 includes a new type to represent classical angles: ``angle``. This type is intended to make manipulations of angles more efficient at runtime, when the hardware executing the program does not have built-in support for floating-point operations. The manipulations on ``angle`` values are designed to be significantly less expensive when done using integer hardware than the equivalent software emulation of floating-point operations, by using the equivalence of angles modulo :math:`2\pi` to remove the need for large dynamic range. In brief, the type ``angle[size]`` is manipulated very similarly to a single unsigned integer, where the value ``1`` represents an angle of :math:`2\pi/2^{\text{size}}`, and the largest representable value is this subtracted from :math:`2\pi`. Addition with other angles, and multiplication and division by unsigned integers is defined by standard unsigned-integer arithmetic, with more details found in :ref:`the section on classical instructions `. The statement ``angle[size] name;`` statement declares a new angle called ``name`` with ``size`` bits in its representation. Angles can be assigned values using the constant ``π`` or ``pi``, such as:: // Declare a 20-bit angle with the value of "π/2" angle[20] my_angle = π / 2; // Declare a machine-sized angle angle my_machine_angle; The bit representation of the type ``angle[size]`` is such that if ``angle_as_uint`` is the integer whose representation as a ``uint[size]`` has the same bit pattern, the value of the angle (using exact mathematical operations on the field of real numbers) would be .. math:: 2\pi \times \frac{\text{angle_as_uint}}{2^{\text{size}}} This "mathematical" value is the value used in casts from floating-point values (if available), whereas casts to and from ``bit[size]`` types reinterpret the bits directly. This means that, unless ``a`` is sufficiently small:: float[32] a; angle[32](bit[32](uint[32](a))) != angle[32](a) Explicitly, the most significant bit (bit index ``size - 1``) correpsonds to :math:`\pi`, and the least significant bit (bit index ``0``) corresponds to :math:`2^{-\text{size} + 1}\pi`. For example, with the most-significant bit on the left in the bitstrings:: angle[4] my_pi = π; // "1000" angle[6] my_pi_over_two = π/2; // "010000" angle[8] my_angle = 7 * (π / 8); // "01110000" Angles outside the interval :math:`[0, 2\pi)` are represented by their values modulo :math:`2\pi`. Up to this modulo operation, the closest ``angle[size]`` representation of an exact mathematical value is different from the true value by at most :math:`\epsilon\leq \pi/2^{\text{size}}`. Complex numbers ~~~~~~~~~~~~~~~ Complex numbers may be declared as ``complex[float[size]] name``, where ``size`` is the size of the IEEE-754 floating-point number used to store the real and imaginary components. Each component behaves as a ``float[size]`` type. The designator ``[size]`` can be omitted to use the default hardware ``float``, and ``complex`` with no arguments is a synonym for ``complex[float]``. Imaginary literals are written as a decimal-integer or floating-point literal followed by the letters ``im``. There may be zero or more spaces between the numeric component and the ``im`` component. The type of this token is ``complex`` (its value has zero real component), and the component type is as normal given the floating-point literal, or the machine-size ``float`` if the numeric component is an integer. The real and imaginary components of a complex number can be extracted using the builtin functions ``real()`` and ``imag()`` respectively. The output types of these functions is the component type specified in the type declaration. For example, given a declaration ``complex[float[64]] c;`` the output type of ``imag(c)`` would be ``float[64]``. The ``real()`` and ``imag()`` functions can be used in compile-time constant expressions when called on compile-time constant values. .. code-block:: complex[float[64]] c; c = 2.5 + 3.5im; complex[float] d = 2.0+sin(π/2) + (3.1 * 5.5 im); float d_real = real(d); // equal to 3.0 .. note:: Real-world hardware may not support run-time manipulation of ``complex`` values. Consult your hardware's documentation to determine whether these language features will be available at run time. .. warning:: The OpenQASM 3.0 specification only directly permits complex numbers with floating-point component types. Individually language implementations may choose to make other component types available, but this version of the specification prescribes no portable semantics in these cases. It is possible that a later version of the OpenQASM specification will define semantics for non-\ ``float`` component types. Boolean types ~~~~~~~~~~~~~ There is a Boolean type ``bool name;`` that takes values ``true`` or ``false``. Qubit measurement results can be converted from a classical ``bit`` type to a Boolean using ``bool(c)``, where 1 will be true and 0 will be false. .. code-block:: bit my_bit = 0; bool my_bool; // Assign a cast bit to a boolean my_bool = bool(my_bit); .. _const-expression: Compile-time constants ---------------------- A typed declaration of a scalar type may be modified by the ``const`` keyword, such as ``const int a = 1;``. This defines a compile-time constant. Values of type ``const T`` may be used in all locations where a value of type ``T`` is valid. ``const``-typed values are required when specifying the widths of types (e.g. in ``float[SIZE] f;``, ``SIZE`` must have a ``const`` unsigned integer type). All scalar literals are ``const`` types. .. code-block:: // Valid statements const uint SIZE = 32; // Declares a compile-time unsigned integer. qubit[SIZE] q1; // Declares a 32-qubit register called `q1`. int[SIZE] i1; // Declares a signed integer called `i1` with 32 bits. // Invalid statements uint runtime_size = 32; qubit[runtime_size] q2; // Invalid; runtime_size is not a `const` type. int[runtime_size] i2; // Invalid for the same reason. .. _const-expression-initialization: Identifiers whose type is ``const T`` must be initialized, and may not be assigned to in subsequent statements. The type of the result of the initialization expression for a ``const`` declaration must be ``const S``, where ``S`` is a type that is either ``T`` or can be :ref:`implicitly promoted ` to ``T``. .. code-block:: // Valid statements const uint u1 = 4; const int[8] i1 = 8; float[64] runtime_f1 = 2.0; const uint u2 = u1; // `u1` is of type `const uint`. const float[32] f2 = u1; // `const uint` is implicitly promoted to `const float[32]`. // Invalid statements const int[64] i2 = f2; // `const float[32]` cannot be implicitly promoted to `const int[64]`. const float[64] f3 = runtime_f1; // `runtime_f1` is not `const`. .. _const-expression-operator: Operator expressions, e.g. ``a + b`` (addition), ``a[b]`` (bit-level indexing) and ``a == b`` (equality), and :ref:`certain built-in functions ` acting only on ``const`` operands will be evaluated at compile time. The resulting values are of type ``const T``, where the type ``T`` is the type of the result when acting on non-\ ``const`` operands. .. code-block:: // Valid statements const uint[8] SIZE = 5; const uint[16] u1 = 2 * SIZE; // Compile-time value 10. const float[64] f1 = 5.0 * SIZE; // Compile-time value 25.0. const bit b1 = u1[1]; // Compile-time value `"1"`. const bit[SIZE - 1] b2 = u1[0:3]; // Compile-time value `"1010"`. .. _const-expression-cast: The resultant type of a cast to type ``T`` is ``const T`` if the input value has a type ``const S``, where values of type ``S`` can be cast to type ``T``. If ``S`` cannot be cast to ``T``, the expression is invalid. The cast operator does not contain the keyword ``const``. .. code-block:: // Valid statements const float[64] f1 = 2.5; uint[8] runtime_u = 7; const int[8] i1 = int[8](f1); // `i1` has compile-time value 2. const uint u1 = 2 * uint(f1); // `u1` has compile-time value 4. // Invalid statements const bit[2] b1 = bit[2](f1); // `float[64]` cannot be cast to `bit[2]`. const int[16] i2 = int[16](runtime_u); // Casting runtime values is not `const`. .. _const-expression-nonconst: The resultant type of any expression involving a value that is not ``const`` is not ``const``. The output type of a call to a subroutine defined by a ``def``, or a call to a subroutine linked by an ``extern`` statement is not ``const``. In these cases, values of type ``const T`` are converted to type ``T`` (which has no runtime cost and no effect on the value), then evaluation continues as usual. .. code-block:: // Valid statements int[8] runtime_i1 = 4; def f(int[8] a) -> int[8] { return a; } // Invalid statements const int[8] i2 = 2 * runtime_i1; // Initialization expression has type `int[8]`, not `const int[8]`. const int[8] i3 = f(runtime_i1); // User-defined function calls do not propagate `const` values. Built-in constants ~~~~~~~~~~~~~~~~~~ Six identifiers are automatically defined in the global scope at the beginning of all OpenQASM 3 programs. There are two identifiers for each of the mathematical constants :math:`\pi`, :math:`\tau = 2\pi` and Euler's number :math:`e`. Each of these values has one ASCII-only identifier and one single-Unicode-character identifier. .. container:: :name: tab:real-constants .. table:: [tab:real-constants] Built-in real constants in OpenQASM3 of type ``float[64]``. +-------------------------------+--------+--------------+---------------------+ | Constant | ASCII | Unicode | Approximate Base 10 | +===============================+========+==============+=====================+ | :math:`\pi` | pi | π | 3.1415926535... | +-------------------------------+--------+--------------+---------------------+ | :math:`\tau = 2\pi` | tau | τ | 6.283185... | +-------------------------------+--------+--------------+---------------------+ | Euler’s number :math:`e` | euler | ℇ | 2.7182818284... | +-------------------------------+--------+--------------+---------------------+ .. _const-expression-functions: Built-in constant expression functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following identifiers are compile-time functions that take ``const`` inputs and have a ``const`` output. The normal implicit casting rules apply to the inputs of these functions. .. note:: These functions may not be available for use on runtime values; consult your compiler and hardware documentation for details. .. container:: :name: tab:built-in-math .. table:: Built-in mathematical functions in OpenQASM3. +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | Function | Input Range/Type, [...] | Output Range/Type | Notes | +==========+=====================================+======================================+========================================+ | arccos | ``float`` on :math:`[-1, 1]` | ``float`` on :math:`[0, \pi]` | Inverse cosine. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | arcsin | ``float`` on :math:`[-1, 1]` | ``float`` on :math:`[-\pi/2, \pi/2]` | Inverse sine. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | arctan | ``float`` | ``float`` on :math:`[-\pi/2, \pi/2]` | Inverse tangent. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | ceiling | ``float`` | ``float`` | Round to the nearest representable | | | | | integer equal or greater in value. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | cos | (``float`` or ``angle``) | ``float`` | Cosine. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | exp | ``float`` | ``float`` | Exponential :math:`e^x`. | | | | | | | | ``complex`` | ``complex`` | | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | floor | ``float`` | ``float`` | Round to the nearest representable | | | | | integer equal or lesser in value. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | log | ``float`` | ``float`` | Logarithm base :math:`e`. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | mod | ``int``, ``int`` | ``int`` | Modulus. The remainder from the | | | | | integer division of the first argument | | | ``float``, ``float`` | ``float`` | by the second argument. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | popcount | ``bit[_]`` | ``uint`` | Number of set (1) bits. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | pow | ``int``, ``uint`` | ``int`` | :math:`\texttt{pow(a, b)} = a^b`. | | | | | | | | ``float``, ``float`` | ``float`` | For floating-point and complex values, | | | | | the principal value is returned. | | | ``complex``, ``complex`` | ``complex`` | | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | rotl | ``bit[n] value``, ``int distance`` | ``bit[n]`` | Rotate the bits in the representation | | | | | of ``value`` by ``distance`` places | | | ``uint[n] value``, ``int distance`` | ``uint[n]`` | to the left (towards higher | | | | | indices). This is similar to a bit | | | | | shift operation, except the vacated | | | | | bits are filled from the overflow, | | | | | rather than being set to zero. The | | | | | width of the output is set equal to | | | | | the width of the input. | | | | | | | | | | ``rotl(a, n) == rotr(a, -n)``. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | rotr | ``bit[n] value``, ``int distance`` | ``bit[n]`` | Rotate the bits in the representation | | | | | of ``value`` by ``distance`` places to | | | ``uint[n] value``, ``int distance`` | ``uint[n]`` | the right (towards lower indices). | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | sin | (``float`` or ``angle``) | ``float`` | Sine. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | sqrt | ``float`` | ``float`` | Square root. This always returns the | | | | | principal root. | | | ``complex`` | ``complex`` | | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ | tan | (``float`` or ``angle``) | ``float`` | Tangent. | +----------+-------------------------------------+--------------------------------------+----------------------------------------+ For each built-in function, the chosen overload is the first one to appear in the list above where all given operands can be implicitly cast to the valid input types. The output type is not considered when choosing an overload. It is an error if there is no valid overload for a given sequence of operands. .. code-block:: // Valid statements. const float[64] f1 = 2.5; const int[8] i1 = 4; const uint[4] u1 = 3; const bit[8] b1 = "0010_1010"; const complex[float[64]] c1 = 1.0 + 2.0im; const float[64] f2 = 2.0 * exp(f1); const float[64] f3 = exp(i1); // The ``float -> float`` overload of ``exp`` is chosen in both of these // cases; in the first, there is an exact type match, in the second the // ``int[8]`` input can be implicitly promoted to ``float``. const int[8] i2 = pow(i1, u1); // Value 64, expression has type `const int`. The first overload of `pow` // is chosen, because `i1` can be implicitly promoted to `const int` and // `u1` to `const uint`. const float[64] f4 = pow(i1, -2); // Value 0.0625, expression has type `const float`. The second, // `(float, float) -> float`, overload is chosen, because `-2` (type // `const int`) cannot be implicitly promoted to `const uint`, but both // input types can be implicitly promoted to `float`. The `complex` overload // is not attempted, because it has lower priority. const bit[8] b2 = rotl(b1, 3); // Value "0101_0001", expression has type `const bit[8]`. // Invalid statements. const complex[float[64]] c2 = mod(c1, 2); // No valid overload is possible; the first given operand has type // `const complex[float[64]]`, which cannot be implicitly promoted to // `int` or `float`. Literals -------- There are five types of literals in OpenQASM 3, integer, float, boolean, bit string, and timing. These literals have ``const`` types. Integer literals can be written in decimal without a prefix, or as a hex, octal, or binary number, as denoted by a leading ``0x/0X``, ``0o``, or ``0b/0B`` prefix. Non-consecutive underscores ``_`` may be inserted between the first and last digit of the literal to improve readability for large values. .. code-block:: int i1 = 1; // decimal int i2 = 0xff; // hex int i3 = 0xffff_ffff // hex with _ for readability int i4 = 0XBEEF; // uppercase HEX int i5 = 0o73; // octal int i6 = 0b1101; // binary int i7 = 0B0110_1001; // uppercase B binary with _ for readability int i8 = 1_000_000 // 1 million with _ for readability Float literals contain either - one or more digits followed by a ``.`` and zero or more digits, - a ``.`` followed by one or more digits. In addition, scientific notation can be used with a signed or unsigned integer exponent. .. code-block:: float f1 = 1.0; float f2 = .1; // leading dot float f3 = 0.; // trailing dot float f4 = 2e10; // scientific float f5 = 2e+1; // scientific with positive signed exponent float f6 = 2.0E-1; // uppercase scientific with signed exponent The two boolean literals are ``true`` and ``false``. Bit string literals are denoted by double quotes ``"`` surrounding a number of zero and one digits, and may include non-consecutive underscores to improve readability for large strings. .. code-block:: bit[8] b1 = "00010001"; bit[8] b2 = "0001_0001"; // underscore for readability Timing literals are float or integer literals with a unit of time. ``ns, μs, us, ms, and s`` are used for SI time units. ``dt`` is a backend-dependent unit equivalent to one waveform sample. .. code-block:: duration one_second = 1000ms; duration thousand_cycles = 1000dt; .. _types-arrays: Arrays ------ Statically-sized arrays of values can be created and initialized, and individual elements can be accessed, using the following general syntax: .. code-block:: array[int[32], 5] myArray = {0, 1, 2, 3, 4}; array[float[32], 3, 2] multiDim = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}}; int[32] firstElem = myArray[0]; // 0 int[32] lastElem = myArray[4]; // 4 int[32] alsoLastElem = myArray[-1]; // 4 float[32] firstLastElem = multiDim[0, 1]; // 1.2 float[32] lastLastElem = multiDim[2, 1]; // 3.2 float[32] alsoLastLastElem = multiDim[-1, -1]; // 3.2 myArray[4] = 10; // myArray == {0, 1, 2, 3, 10} multiDim[0, 0] = 0.0; // multiDim == {{0.0, 1.2}, {2.1, 2.2}, {3.1, 3.2}} multiDim[-1, 1] = 0.0; // multiDim == {{0.0, 1.2}, {2.1, 2.2}, {3.1, 0.0}} The first argument to the ``array`` type constructor is the base type of the array. The supported classical types include various sizes of ``bit``, ``int``, ``uint``, ``float``, ``complex``, and ``angle``, as well as ``bool`` and ``duration``. Note that ``stretch`` is not a valid array base type. Arrays *cannot* be declared inside the body of a function or gate. All arrays *must* be declared within the global scope of the program. Indexing of arrays is n-based *i.e.*, negative indices are allowed. The index ``-1`` means the last element of the array, ``-2`` is the second to last, and so on, with ``-n`` being the first element of an n-element array. Multi-dimensional arrays (as in the example above) are allowed, with a maximum of 7 total dimensions. The subscript operator ``[]`` is used for element access, and for multi-dimensional arrays subarray accesses can be specified using a comma-delimited list of indices (*e.g.* ``myArr[1, 2, 3]``), with the outer dimension specified first. For interoperability, the standard ways of declaring quantum registers and bit registers are equivalent to the array syntax version (*i.e.* ``qubit[5] q1;`` is the same as ``array[qubit, 5] q1;``). Assignment to elements of arrays, as in the examples above, acts as expected, with the left-hand side of the assignment operating as a reference, thereby updating the values inside the original array. For multi-dimensional arrays, the shape and type of the assigned value must match that of the reference. .. code-block:: array[int[8], 3] aa; array[int[8], 4, 3] bb; bb[0] = aa; // all of aa is copied to first element of bb bb[0, 1] = aa[2] // last element of aa is copied to one element of bb bb[0] = 1 // error - shape mismatch Arrays may be passed to subroutines and externs. For more details, see :any:`arrays-in-subroutines`. Types related to timing ----------------------- Duration ~~~~~~~~ We introduce a ``duration`` type to express timing. Durations can be assigned with expressions including timing literals. ``durationof()`` is an intrinsic function used to reference the duration of a calibrated gate. .. code-block:: duration one_second = 1000ms; duration thousand_cycles = 1000dt; duration two_seconds = one_second + 1s; duration c = durationof({x $3;}); ``duration`` is further discussed in :any:`duration-and-stretch` Stretch ~~~~~~~ We further introduce a ``stretch`` type which is a sub-type of ``duration``. ``stretch`` types have variable non-negative duration that is permitted to grow as necessary to satisfy constraints. Stretch variables are resolved at compile time into target-appropriate durations that satisfy a user’s specified design intent. ``stretch`` is further discussed in :any:`duration-and-stretch` Aliasing -------- The ``let`` keyword allows quantum bits and registers to be referred to by another name as long as the alias is in scope. .. code-block:: qubit[5] q; // myreg[0] refers to the qubit q[1] let myreg = q[1:4]; Index sets and slicing ---------------------- Register concatenation and slicing ---------------------------------- Two or more registers of the same type (i.e. classical or quantum) can be concatenated to form a register of the same type whose size is the sum of the sizes of the individual registers. The concatenated register is a reference to the bits or qubits of the original registers. The statement ``a ++ b`` denotes the concatenation of registers ``a`` and ``b``. A register cannot be concatenated with any part of itself. Classical and quantum registers can be indexed in a way that selects a subset of (qu)bits, i.e. by an index set. A register so indexed is interpreted as a register of the same type but with a different size. The register slice is a reference to the original register. A register cannot be indexed by an empty index set. Similarly, classical arrays can be indexed using index sets. See :any:`array-slicing`. An index set can be specified by a single integer (signed or unsigned), a comma-separated list of integers contained in braces ``{a,b,c,…}``, or a range. Ranges are written as ``a:b`` or ``a:c:b`` where ``a``, ``b``, and ``c`` are integers (signed or unsigned). The range corresponds to the set :math:`\{a, a+c, a+2c, \dots, a+mc\}` where :math:`m` is the largest integer such that :math:`a+mc\leq b` if :math:`c>0` and :math:`a+mc\geq b` if :math:`c<0`. If :math:`a=b` then the range corresponds to :math:`\{a\}`. Otherwise, the range is the empty set. If :math:`c` is not given, it is assumed to be one, and :math:`c` cannot be zero. Note the index sets can be defined by variables whose values may only be known at run time. .. code-block:: qubit[2] one; qubit[10] two; // Aliased register of twelve qubits let concatenated = one ++ two; // First qubit in aliased qubit array let first = concatenated[0]; // Last qubit in aliased qubit array let last = concatenated[-1]; // Qubits zero, three and five let qubit_selection = two[{0, 3, 5}]; // First seven qubits in aliased qubit array let sliced = concatenated[0:6]; // Every second qubit let every_second = concatenated[0:2:12]; // Using negative ranges to take the last 3 elements let last_three = two[-4:-1]; // Concatenate two alias in another one let both = sliced ++ last_three; Classical value bit slicing --------------------------- A subset of classical values (int, uint, and angle) may be accessed at the bit level using index sets similar to register slicing. The bit slicing operation always returns a bit array of size equal to the size of the index set. .. code-block:: int[32] myInt = 15; // 0xF or 0b1111 bit[1] lastBit = myInt[0]; // 1 bit[1] signBit = myInt[31]; // 0 bit[1] alsoSignBit = myInt[-1]; // 0 bit[16] evenBits = myInt[0:2:31]; // 3 bit[16] upperBits = myInt[-16:-1]; bit[16] upperReversed = myInt[-1:-16]; myInt[4:7] = "1010"; // myInt == 0xAF Bit-level access is still possible with elements of arrays. It is suggested that multi-dimensional access be done using the comma-delimited version of the subscript operator to reduce confusion. With this convention nearly all instances of multiple subscripts ``[][]`` will be bit-level accesses of array elements. .. code-block:: array[int[32], 5] intArr = {0, 1, 2, 3, 4}; // Access bit 0 of element 0 of intArr and set it to 1 intArr[0][0] = 1; // lowest 5 bits of intArr[4] copied to b bit[5] b = intArr[4][0:4]; .. _array-slicing: Array concatenation and slicing ------------------------------- Two or more classical arrays of the same fundamental type can be concatenated to form an array of the same type whose size is the sum of the sizes of the individual arrays. Unlike with qubit registers, this operation copies the contents of the input arrays to form the new (larger) array. This means that arrays *can* be concatenated with themselves. However, the array concatenation operator is forbidden to be used directly in the argument list of a subroutine or extern call. If a concatenated array is to be passed to a subroutine then it should be explicitly declared and assigned the concatenation. .. code-block:: array[int[8], 2] first = {0, 1}; array[int[8], 3] second = {2, 3, 4}; array[int[8], 5] concat = first ++ second; array[int[8], 4] selfConcat = first ++ first; array[int[8], 2] secondSlice = second[1:2]; // {3, 4} // slicing with assignment second[1:2] = first[0:1]; // second == {2, 0, 1} array[int[8], 4] third = {5, 6, 7, 8}; // combined slicing and concatenation selfConcat[0:3] = first[0:1] ++ third[1:2]; // selfConcat == {0, 1, 6, 7} subroutine_call(first ++ third) // forbidden subroutine_call(selfConcat) // allowed Arrays can be sliced just like quantum registers using a range ``a:b:c`` and can be indexed using an integer but cannot be indexed by a a comma-separated list of integers contained in braces ``{a,b,c,…}``. Slicing uses the subscript operator ``[]``, but produces an array (or reference in the case of assignment) with the same number of dimensions as the given identifier. Array slicing is syntactic sugar for concisely expressing for loops over multi-dimensional arrays. For sliced assignments, as with non-sliced assignments, the shapes and types of the slices must match. .. code-block:: int[8] scalar; array[int[8], 2] oneD; array[int[8], 3, 2] twoD; // 3x2 array[int[8], 3, 2] anotherTwoD; // 3x2 array[int[8], 4, 3, 2] threeD; // 4x3x2 array[int[8], 2, 3, 4] anotherThreeD; // 2x3x4 threeD[0, 0, 0] = scalar; // allowed threeD[0, 0] = oneD; // allowed threeD[0] = twoD; // allowed threeD[0] = oneD; // error - shape mismatch threeD[0, 0] = scalar // error - shape mismatch threeD = anotherThreeD // error - shape mismatch twoD[1:2] = anotherTwoD[0:1]; // allowed twoD[1:2, 0] = anotherTwoD[0:1, 1]; // allowed .. _castingSpecifics: .. _implicit-promotion-rules: Casting specifics ----------------- The classical types are divided into the 'standard' classical types (bool, int, uint, float, and complex) that exist in languages like C, and the 'special' classical types (bit, angle, duration, and stretch) that do not. The standard types follow rules that mimic those of C99 for `promotion and conversion `_ in mixed expressions and assignments. If values with two different types are used as the operands of a binary operation, the lesser of the two types is cast to the greater of the two. All ``complex`` are greater than all ``float``, and all ``complex`` and all ``float`` are greater than all ``int`` or ``uint``. Within each level of ``complex`` and ``float``, types with greater width are greater than types with lower width. For more information, see the `usual arithmetic conversions in C `_. The rules for rank of integer conversions mimic those of C99. For more, see `integer promotions `_, and `integer conversions `_. Standard and special classical types may only mix in expressions with operators defined for those mixed types, otherwise explicit casts must be provided, unless otherwise noted (such as for assigning float values or expressions to angles). Additionally, angle values will be implicitly promoted or converted in the same manner as unsigned integers when mixed with or assigned to angle values with differing precision. In general, for any cast between standard types that results in loss of precision, if the source value is larger than can be represented in the target type, the exact behavior is implementation specific and must be documented by the vendor. Allowed casts ~~~~~~~~~~~~~ .. role:: rbg .. role:: gbg .. role:: center +--------------+--------------------------------------------------------------------------------------------------------+ | | :center:`Casting To` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | Casting From | bool | int | uint | float | angle | bit | duration | qubit | +==============+============+============+============+=============+============+============+============+============+ | **bool** | :center:`-`| :gbg:`Yes` | :gbg:`Yes` | :gbg:`Yes` | :rbg:`No` | :gbg:`Yes` | :rbg:`No` | :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **int** | :gbg:`Yes` | :center:`-`| :gbg:`Yes` | :gbg:`Yes` | :rbg:`No` | :gbg:`Yes` | :rbg:`No` | :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **uint** | :gbg:`Yes` | :gbg:`Yes` | :center:`-`| :gbg:`Yes` | :rbg:`No` | :gbg:`Yes` | :rbg:`No` | :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **float** | :gbg:`Yes` | :gbg:`Yes` | :gbg:`Yes` | :center:`-` | :gbg:`Yes` | :rbg:`No` | :rbg:`No` | :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **angle** | :gbg:`Yes` | :rbg:`No` | :rbg:`No` | :rbg:`No` | :center:`-`| :gbg:`Yes` | :rbg:`No` | :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **bit** | :gbg:`Yes` | :gbg:`Yes` | :gbg:`Yes` | :rbg:`No` | :gbg:`Yes` | :center:`-`| :rbg:`No` | :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **duration** | :rbg:`No` | :rbg:`No` | :rbg:`No` | :rbg:`No*` | :rbg:`No` | :rbg:`No` | :center:`-`| :rbg:`No` | +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ | **qubit** | :rbg:`No` | :rbg:`No` | :rbg:`No` | :rbg:`No` | :rbg:`No` | :rbg:`No` | :rbg:`No` | :center:`-`| +--------------+------------+------------+------------+-------------+------------+------------+------------+------------+ \*Note: ``duration`` values can be converted to ``float`` using the division operator. See :ref:`divideDuration` Casting from bool ~~~~~~~~~~~~~~~~~ ``bool`` values cast from ``false`` to ``0.0`` and from ``true`` to ``1.0`` or an equivalent representation. ``bool`` values can only be cast to ``bit[1]`` (a single bit), so explicit index syntax must be given if the target ``bit`` has more than 1 bit of precision. Casting from int/uint ~~~~~~~~~~~~~~~~~~~~~ ``int[n]`` and ``uint[n]`` values cast to the standard types mimicking C99 behavior. Casting to ``bool`` values follows the convention ``val != 0``. As noted above, if the value is too large to be represented in the target type the result is implementation-specific. However, casting between ``int[n]`` and ``uint[n]`` is expected to preserve the bit ordering, specifically it should be the case that ``x == int[n](uint[n](x))`` and vice versa. Casting to ``bit[m]`` is only allowed when ``m==n``. If the target ``bit`` has more or less precision, then explicit slicing syntax must be given. As noted, the conversion is done assuming a little-endian 2's complement representation. Casting from float ~~~~~~~~~~~~~~~~~~ ``float[n]`` values cast to the standard types mimicking C99 behavior (*e.g.* discarding the fractional part for integer-type targets). As noted above, if the value is too large to be represented in the target type the result is implementation-specific. Casting a ``float[n]`` value to an ``angle[m]`` involves finding the nearest representable value modulo :math:`\text{float}_n(2\pi)`, where ties between two possible representations are resolved by choosing to have zero in the least-significant bit (*i.e.* round to nearest, ties to even). Casting the floating-point values ``inf``, ``-inf`` and all representations of ``NaN`` to ``angle[m]`` is not defined. For example, given the double-precision floating-point value:: // The closest double-precision representation of 2*pi. const float[64] two_pi = 6.283185307179586 // For double precision, we have // (two_pi * (127./512.)) / two_pi == (127./512.) // exactly. float[64] f = two_pi * (127. / 512.) the result of the cast ``angle[8](f)`` should have the bitwise representation ``"01000000"`` (which represents the exact angle :math:`2\pi\cdot\frac{64}{256} = \frac\pi2`), despite ``"00111111"`` (:math:`2\pi\cdot\frac{63}{256}`) being equally close, because of the round-to-nearest ties-to-even behaviour. Casting from angle ~~~~~~~~~~~~~~~~~~ ``angle[n]`` values cast to ``bool`` using the convention ``val != 0``. Casting to ``bit[m]`` values is only allowed when ``n==m``, otherwise explicit slicing syntax must be provided. When casting to ``bit[m]``, the value is a direct copy of the bit pattern using the same little-endian ordering :ref:`as described above `. When casting between angles of differing precisions (``n!=m``): if the target has more significant bits, then the value is padded with ``m-n`` least significant bits of ``0``; if the target has fewer significant bits, then there are two acceptable behaviors that can be supported by compilers: rounding and truncation. For rounding the value is rounded to the nearest value, with ties going to the value with the even least significant bit. Trunction is likely to have more hardware support. This behavior can be controlled by the use of a ``#pragma``. Casting from bit ~~~~~~~~~~~~~~~~ ``bit[n]`` values cast to ``bool`` using the convention ``val != 0``. Casting to ``int[m]`` or ``uint[m]`` is done assuming a little endian 2's complement representation, and is only allowed when ``n==m``, otherwise explicit slicing syntax must be given. Likewise, ``bit[n]`` can only be cast to ``angle[m]`` when ``n==m``, in which case an exact per-bit copy is done using little-endian bit order. Finally, casting between bits of differing precisions is not allowed, explicit slicing syntax must be given. .. _divideDuration: Converting duration to other types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Casting from or to duration values is not allowed, however, operations on durations that produce values of different types is allowed. For example, dividing a duration by a duration produces a machine-precision ``float``. .. code-block:: duration one_ns = 1ns; duration a = 500ns; float a_in_ns = a / one_ns; // 500.0 duration one_s = 1s; float a_in_s = a / one_s; // 5e-7