Type system

During semantic analysis, libqasm assigns data types to expressions trees in depth-first order. This type system knows the following types:

  • qubits;
  • bits (a.k.a. booleans);
  • axes (X, Y, or Z);
  • integers (64-bit signed in libqasm, may be less in target);
  • real scalars (IEEE 754 double precision in libqasm, may be different in target);
  • complex scalars (2x IEEE 754 double precision for real and imaginary components in libqasm, may be different in target);
  • real vectors and matrices (IEEE 754 double precision per element in libqasm, may be different in target);
  • complex vectors and matrices (2x IEEE 754 double precision for real and imaginary components for each element in libqasm, may be different in target);
  • strings (libqasm makes no assertions about encoding); and
  • JSON strings.

Types are used in semantic analysis to check whether the operands for instructions, functions, and operators make sense (for example, X 1 makes no sense; you can’t apply an X gate to an integer), and in some cases are used to disambiguate between multiple overloads of the same gate or function.

Historical

The above used to be hardcoded in the grammar. That is, X 1 wouldn’t parse not because of a type system, but because the X keyword had to be followed by a single qubit. This is a bad idea for many reasons, such as readability of the grammar specification, maintainability, inability to redefine X to refer to a qubit using a mapping, and lack of configurability of the instruction set as done in OpenQL. It is, however, easy to write things down this way that are difficult to express with a type system. Combined with the backward compatibility requirement when instruction set configurability became important, this relatively extensive type system had to be introduced.

Constant propagation and coercion

While doing type analysis, libqasm also performs constant propagation where applicable. This means that the expression 1 + 3 is completely equivalent to just 4. Wherever constants are expected, you can still use operators and functions with constant propagation rules to help make the code clearer.

libqasm also evaluates promotion rules during this process. Specifically;

  • If an integer constant is found but a real number is expected, the integer is implicitly converted to a real number.

  • If an integer or real number constant is found but a complex number is expected, the integer or real number is implicitly converted to a complex number.

  • If a constant real matrix is found but a complex matrix is expected, and the real matrix has dimensions matching the ones expected for the complex matrix, the real matrix is converted to a complex matrix.

  • If a constant real row vector with 2n2 entries is found but a complex matrix of size n by n is expected, the elements in the vector are interpreted as real-imaginary pairs in row-major order, and the value is converted accordingly. For example:

    [1, 2, 3, 4, 5, 6, 7, 8]
    # is promoted to
    [
        1 + 2*im, 3 + 4*im
        5 + 6*im, 7 + 8*im
    ]
    # whenever a 2x2 complex matrix is expected.
    

    This rule exists only for backward compatibility for the one-qubit U gate.

Note that all promotion rules only apply to constants. When a target supports dynamic evaluation, it must provide functions to convert between types if the target supports this, and the user must explicitly use these functions.