Keywords

1 Introduction

With the promise of providing efficient algorithmic solutions to many problems [11, 27, 31], some of which are traditionally believed to be intractable [54], quantum computing is the subject of intense investigation by various research communities within computer science, not least that of programming language theory [24, 43, 51]. Various proposals for idioms capable of tapping into this new computing paradigm have appeared in the literature since the late 1990s. Some of these approaches turn out to be fundamentally new [1, 49, 52], while many others are strongly inspired by classical languages and traditional programming paradigms [44, 48, 53, 63].

One of the major obstacles to the practical adoption of quantum algorithmic solutions is the fact that despite huge efforts by scientists and engineers alike, it seems that reliable quantum hardware, contrary to classical one, does not scale too easily: although quantum architectures with up to a couple hundred qubits have recently seen the light [9, 10, 38], it is not yet clear whether the so-called quantum advantage [45] is a concrete possibility, given the tremendous challenges posed by the quantum decoherence problem [50].

This entails that software which makes use of quantum hardware must be designed with great care: whenever part of a computation has to be run on quantum hardware, the amount of resources it needs, and in particular the amount of qubits it uses, should be kept to a minimum. More generally, a fine control over the low-level aspects of the computation, something that we willingly abstract from when dealing with most forms of classical computation, should be exposed to the programmer in the quantum case. This, in turn, has led to the development and adoption of many domain-specific programming languages and libraries in which the programmer explicitly manipulates qubits and quantum circuits, while still making use of all the features of a high-level classical programming language. This is the case of the Qiskit and Cirq libraries [17], but also of the Quipper language [25, 26].

At the fundamental level, Quipper is a circuit description language embedded in Haskell. Because of this, Quipper inherits all the expressiveness of the high level, higher-order functional programming language that is its host, but for the same reason it also lacks a formal semantics. Nonetheless, over the past few years, a number of calculi, collectively known as the Proto-Quipper language family, have been developed to formalize interesting fragments and extensions of Quipper in a type-safe manner [46, 48]. Extensions include, among others, dynamic lifting [8, 21, 35] and dependent types [20, 22], but resource analysis is still a rather unexplored research direction in the Proto-Quipper community [56].

The goal of this work is to show that type systems indeed enable the possibility of reasoning about the size of the circuits produced by a Proto-Quipper program. Specifically, we show how linear dependent types in the style of Gaboardi and Dal Lago [12, 14, 15, 23] can be adapted to Proto-Quipper, allowing to derive upper bounds on circuit widths that are parametric on the number of input wires to the circuit, be they classical or quantum. This enables a form of static analysis of the resource consumption of circuit families and, consequently, of the quantum algorithms described in the language. Technically, a key ingredient of this analysis, besides linear dependency, is a novel form of effect typing in which the quantitative information coming from linear dependency informs the effect system and allows it to keep circuit widths under control.

The rest of the paper is organized as follows. Section 2 informally explores the problem of estimating the width of circuits produced by Quipper, while also introducing the language. Section 3 provides a more formal definition of the Proto-Quipper language. In particular, it gives an overview of the system of simple types due to Rios and Selinger [46], which however is not meant to reason about the size of circuits. We then move on to the most important technical contribution of this work, namely the linear dependent and effectful type system, which is introduced in Section 4 and proven to guarantee both type safety and a form of total correctness in Section 5. Section 6 is dedicated to an example of a practical application of our type and effect system, that is, a program that builds the Quantum Fourier Transform (QFT) circuit [11, 39] and which is verified to do so without any ancillary qubits.

To conclude this introduction, we wish to emphasize that while it is true that quantum computing can be a difficult and intimidating subject, the class of languages analyzed in this work focuses on circuit construction, which is an entirely classical process, paying little to no concern to the actual quantum semantics of circuit execution. Because of this, and due to space constraints, we refrain from providing a general introduction to quantum computing in this paper. Instead, we refer the interested reader to the excellent works of Nielsen and Chuang [39], Yanofsky and Mannucci [60], and Mingsheng [61].

2 An Overview on Circuit Width Estimation

Quipper allows programmers to describe quantum circuits in a high-level and elegant way, using both gate-by-gate and circuit transformation approaches. Quipper also supports hierarchical and parametric circuits, thus promoting a view in which circuits become first-class citizens. Quipper has been shown to be scalable, in the sense that it has been effectively used to describe complex quantum algorithms that easily translate to circuits involving trillions of gates applied to millions of qubits. The language allows the programmer to optimize the circuit, e.g. by using ancilla qubits for the sake of reducing the circuit depth, or recycling qubits that are no longer needed.

One feature that Quipper lacks is a methodology for statically proving that important parameters — such as the the width — of the underlying circuit are below certain limits, which of course would need to be parametric on the input size of the circuit. If this kind of analysis were available, then it would be possible to derive bounds on the number of qubits needed to solve any instance of a problem, and ultimately to know in advance how big of an instance can be possibly solved given a fixed amount of qubits.

In order to illustrate the kind of scenario we are reasoning about, this section offers some simple examples of Quipper programs, showing in what sense we can think of capturing the quantitative information that we are interested in through types and effect systems and linear dependency. We proceed at a very high level for now, without any ambition of formality.

Let us start with the example of Figure 1. The Quipper function on the left builds the structure on the right, which we call a quantum circuit. For the purposes of this work, it suffices to say that horizontal lines represent qubits, while other symbols represent elementary operations applied to them, e.g. initializations, gate applications, and so on. Time flows from left to right. The specific circuit in Figure 1 consists in an (admittedly contrived) implementation of the quantum not operation. The dumbNot function implements negation using a controlled not gate and an ancillary qubit a, which is initialized and discarded within the body of the function. This qubit does not appear in the interface of the circuit, but it clearly adds to its overall width, which is 2.

Fig. 1.
figure 1

A contrived implementation of the quantum not operation using an ancilla

Consider now the higher-order function in Figure 2. This function takes as input a circuit building function f, an integer n and describes the circuit obtained by applying f’s circuit n times to the input qubit q. It is easy to see that the width of the circuit produced in output by iter dumbNot n is equal to 2, even though, overall, the number of qubits initialized during the computation is equal to n. The point is that each ancilla is created only after the previous one has been discarded, thus enabling a form of qubit recycling.

Fig. 2.
figure 2

A higher-order function which iterates a circuit-building function f on an input qubit q and the result of its application to the dumbNot function from Figure 1

Is it possible to statically analyze the width of the circuit produced in output by iter dumbNot n so as to conclude that it is constant and equal to 2? What techniques can we use? Certainly, the presence of higher order types complicates an already non-trivial problem. The approach we propose in this paper is based on two ingredients. The first is the so-called effect typing [40]. In this context the effect produced by the program is nothing more than the circuit and therefore it is natural to think of an effect system in which the width of such circuit, and only that, is exposed. Therefore, the arrow type \(A\rightarrow B\) should be decorated with an expression indicating the width of the circuit produced by the corresponding function when applied to an argument of type \(A\). Of course, the width of an individual circuit is a natural number, so it would make sense to annotate the arrow with such a number. For technical reasons, however, it will also be necessary to keep track of another natural number, corresponding to the number of wire resources that the function captures from the surrounding environment. This necessity stems from a need to keep track of wires even in the presence of data hiding, and will be explained in further detail in Section 4.

Under these premises, the dumbNot function would receive type \(\textsf{Qubit}\rightarrow _{2,0} \textsf{Qubit}\), meaning that it takes as input a qubit and produces a circuit of width 2 which outputs a qubit. Note that the second annotation is 0, since we do not capture anything from the function’s environment, let alone a wire. Consequently, because iter iterates in sequence and because the ancillary qubit in dumbNot can be reused, the type of iter dumbNot n would also be \(\textsf{Qubit}\rightarrow _{2,0} \textsf{Qubit}\).

Fig. 3.
figure 3

The hadamardN function implements a circuit family where circuits have width linear in their input size.

Let us now consider a slightly different situation, in which the width of the produced circuit is not constant, but rather increases proportionally to the circuit’s input size. Figure 3 shows a Quipper function that returns a circuit on n qubits in which the Hadamard gate is applied to each qubit, a common preprocessing step in many quantum algorithms. It is obvious that this function works on inputs of arbitrary size, and therefore we can interpret it as a circuit family, parametrized on the length of the input list of qubits. This quantity, although certainly a natural number, is unknown statically and corresponds precisely to the width of the produced circuit. It is thus natural to wonder whether the kind of effect typing we briefly hinted at in the previous paragraph is capable of dealing with such a function. Certainly, the expressions used to annotate arrows cannot be, like in the previous case, mere constants, as they clearly depend on the size of the input list. Is there a way to reflect this dependency in types? Certainly, one could go towards a fully-fledged notion of dependent types, like the ones proposed in [22], but a simpler approach, in the style of Dal Lago and Gaboardi’s linear dependent types [12, 14, 15, 23] turns out to be enough for this purpose. This is precisely the route that we follow in this paper. In this approach, terms can indeed appear in types, but that is only true for a very restricted class of terms, disjoint from the ordinary ones, called index terms. As an example, the type of the function hadamardN above could become \(\textsf{List}^{i}\,{\textsf{Qubit}} \rightarrow _{i,0} \textsf{List}^{i}\,{\textsf{Qubit}}\), where \(i\) is an index variable. The meaning of the type would thus be that hadamardN takes as input any list of qubits of length \(i\) and produces a circuit of width at most \(i\) which outputs \(i\) qubits. Indices are better explained in Section 4, but in general we can say that they consist of arithmetical expressions over natural numbers and index variables, and can thus express non-trivial dependencies between input sizes and corresponding circuit widths.

3 The Proto-Quipper Language

This section aims at introducing the Proto-Quipper family of calculi to the non-specialist, without any form of resource analysis. At its core, Proto-Quipper is a linear lambda calculus with bespoke constructs to build and manipulate circuits. Circuits are built as a side-effect of a computation, behind the scenes, but they can also appear and be manipulated as data in the language.

Fig. 4.
figure 4

The Proto-Quipper types

The types of Proto-Quipper are given in Figure 4. Speaking at a high level, we can say that Proto-Quipper employs a linear-nonlinear typing discipline. In particular, \(w\in \{\textsf{Bit},\textsf{Qubit}\}\) is a wire type and is linear, while \(\multimap \) is the linear arrow constructor. A subset of types, called parameter types, represent the values of the language that are not linear and that can therefore be copied. Any term of type \(A\) can be lifted into a duplicable parameter of type \({\text {!}}A\) if its type derivation does not require the use of linear resources.

Fig. 5.
figure 5

The Proto-Quipper syntax

The syntax of Proto-Quipper is given in Figure 5. At a very high level, we are dealing with an effectful lambda calculus with bespoke constructs for manipulating circuits. A \(\textsf{return}\) expression turns a value into a trivial computation, while a \(\textsf{let}\) expression is used to sequence computations. Note that \(\textsf{let}\) is associative and that \(\textsf{return}\) acts as its identity. Now, let us informally dissect the domain-specific aspects of this language, starting with the language of values. The constructs of greatest interest are labels and boxed circuits. A label \(\ell \) represents a reference to a free wire of the underlying circuit being built and is attributed a wire type \(w\in \{\textsf{Bit},\textsf{Qubit}\}\). Due to the no-cloning property of quantum states [39], labels have to be treated linearly. Arbitrary structures of labels form a subset of values which we call wire bundles and which are given bundle types. On the other hand, a boxed circuit \((\bar{\ell },\mathcal {C},\bar{k})\) represents a circuit object \(\mathcal {C}\) as a datum within the language, together with its input and output interfaces \(\bar{\ell }\) and \(\bar{k}\). Such a value is given parameter type \(\textsf{Circ}^{}(T,U)\), where bundle types \(T\) and \(U\) are the input and output types of the circuit, respectively. Boxed circuits can be copied, manipulated by primitive functions and, more importantly, applied to the underlying circuit. This last operation, which lies at the core of Proto-Quipper’s circuit-building capabilities, is possible thanks to the \({\textsf{apply}}\) operator. This operator takes as first argument a boxed circuit \((\bar{\ell },\mathcal {C},\bar{k})\) and appends \(\mathcal {C}\) to the underlying circuit \(\mathcal {D}\). How does \({\textsf{apply}}\) know where exactly in \(\mathcal {D}\) to apply \(\mathcal {C}\)? Thanks to a second argument: a bundle of wires \(\bar{t}\) coming from the free output wires of \(\mathcal {D}\), which identify the exact location where \(\mathcal {C}\) is supposed to be attached.

The language is expected to be endowed with constant boxed circuits corresponding to fundamental gates and operations (e.g. Hadamard, CNOT, initialization, etc.), but the programmer can also introduce their own boxed circuits via the \(\textsf{box}\) operator. Intuitively, \(\textsf{box}\) takes as input a circuit-building function and executes it in a sandboxed environment, on dummy arguments, in a way that leaves the underlying circuit unchanged. Said function produces a standalone circuit \(\mathcal {C}\), which is then returned by the \(\textsf{box}\) operator as a boxed circuit \((\bar{\ell },\mathcal {C},\bar{k})\).

Figure 6 shows the Proto-Quipper term corresponding to the Quipper program in Figure 1, as an example of the use of the language. Note that \(\textsf{let}\; \langle x,y\rangle = M \;\textsf{in}\; N\) is syntactic sugar for \(\textsf{let}\; z = M \;\textsf{in}\; \textsf{let}\; \langle x,y\rangle = z \;\textsf{in}\; N\). The \( dumbNot \) function is given type \(\textsf{Qubit}\multimap \textsf{Qubit}\) and builds the circuit shown in Figure 1 when applied to an argument.

Fig. 6.
figure 6

An example Proto-Quipper program. \(\mathsf {INIT_1,CNOT}\) and \(\textsf{DISCARD}\) are primitive boxed circuits implementing the corresponding elementary operations.

On the classical side of things, it is worth mentioning that Proto-Quipper as presented in this section does not support general recursion. A limited form of recursion on lists is instead provided via a primitive \({\textsf{fold}}\) constructor, which takes as argument a (copiable) step function of type \({\text {!}}((B \otimes A) \multimap B)\), an initial value of type \(B\), and constructs a function of type \(\textsf{List}^{}\,{A} \multimap B\). Although this workaround is not enough to recover the full power of general recursion, it appears that it is enough to describe many quantum algorithms. Figure 7 shows an example of the use of \({\textsf{fold}}\) to reverse a list. Note that \(\lambda \langle x,y\rangle _{A \otimes B}.M\) is syntactic sugar for \(\lambda z_{A \otimes B}.\textsf{let}\; \langle x,y\rangle = z \;\textsf{in}\; M\).

Fig. 7.
figure 7

An example of the use of \({\textsf{fold}}\): the function that reverses a list

To conclude this section, we just remark how all of the Quipper programs shown in Section 2 can be encoded in Proto-Quipper. However, Proto-Quipper’s system of simple types in unable to tell us anything about the resource consumption of these programs. Of course, one could run hadamardN on a concrete input and examine the size of the circuit produced at run-time, but this amounts to testing, not verifying the program, and lacks the qualities of staticity and parametricity that we seek.

4 Incepting Linear Dependency and Effect Typing

We are now ready to expand on the informal definition of the Proto-Quipper language given in Section 3, to reach a formal definition of Proto-Quipper-R: a linearly and dependently typed language whose type system supports the derivation of upper bounds on the width of the circuits produced by programs.

Fig. 8.
figure 8

Proto-Quipper-R syntax and types

4.1 Types and Syntax of Proto-Quipper-R

The types and syntax of Proto-Quipper-R are given in Figure 8. As we mentioned, one of the key ingredients of our type system are the index terms with which we annotate standard Proto-Quipper types. These indices provide quantitative information about the elements of the resulting types, in a manner reminiscent of refinement types [18, 47]. In our case, we are primarily concerned with circuit width, which means that the natural starting point of our extension of Proto-Quipper is precisely the circuit type: \(\textsf{Circ}^{I}(T,U)\) has elements the boxed circuits of input type \(T\), output type \(U\), and width bounded by \(I\). Term \(I\) is precisely what we call an index, that is, an arithmetical expression denoting a natural number. Looking at the grammar for indices, their interpretation is fairly straightforward, with a few notes: \(n\) is a natural number, \(i\) is an index variable, \(I - J\) denotes Natural subtraction, such that \(I - J=0\) whenever \(I\le J\), and lastly \(\textsf{max}_{i < I}\,J\) is the maximum for \(i\) going from 0 (included) to \(I\) (excluded) of \(J\), where \(i\) can occur in \(J\). Note that \(I= 0\) implies \(\textsf{max}_{i < I}\,J = 0\). While the index in a circuit type denotes an upper bound, the index in a type of the form \(\textsf{List}^{I}\,{A}\) denotes the exact length of the lists of that type. While this refinement might seem quite restrictive in a generic scenario, it allows us to include lists of labels among wire bundles, something that was not possible with simple lists. This is due to the fact that sized lists are effectively isomorphic to finite tensors, and therefore a sized list of labels represent a wire bundle of known size, whereas the same is not true for a simple list of labels. Lastly, as we anticipated in Section 2, an arrow type \(A \multimap _{I,J} B\) is annotated with Two indices: \(I\) is an upper bound to the width of the circuit built by the function once it is applied to an argument of type \(A\), while \(J\) describes the exact number of wires captured in the function’s closure. The utility of this last annotation will be clearer in Section 4.3.

The languages for terms and values are almost the same as in Proto-Quipper, with the minor difference that the \({\textsf{fold}}\) operator now binds the index variable name \(i\) within its first argument. This variable appears locally in the type of the step function, in such a way as to allow each iteration of the fold to contribute to the overall circuit width in a different way.

4.2 A Formal Language for Circuits

The type system of Proto-Quipper-R is designed to allow for reasoning about the width of circuits. Therefore, before we formally introduce the type system in Section 4.3, we ought to introduce circuits themselves in a formal way. So far, we have only spoken of circuits at a very high and intuitive level, and we have represented them only graphically. Looking at the circuits in Section 2, what do they have in common? At the fundamental level, they are made up of elementary operations applied to specific wires. Of course, the order of these operations matters, as does the order of wires that they are applied to. In the existing literature on Proto-Quipper, circuits are usually interpreted as morphisms in a symmetric monoidal category [46], but this approach makes it particularly hard to reason about their intensional properties, such as width. For this reason, we opt for a concrete model of wires and circuits, rather than an abstract one.

Luckily, we already have a datatype modeling ordered structures of wires, that is, the wire bundles that we introduced in the previous sections. We use them as the basis upon which we build circuits.

Fig. 9.
figure 9

CRL syntax and types

That being said, Figure 9 introduces the Circuit Representation Language (CRL) which we use as the target for circuit building in Proto-Quipper-R. Wire bundles are exactly as in Figure 8 and represent arbitrary structures of wires, while circuits themselves are defined very simply as sequences of elementary operations applied to said structures. We call \(Q\) a label context and define it as a mapping from label names to wire types. We use label contexts as a means to keep track of the set of labels available in a computation, alongside their respective types. Circuit \(id_{Q}\) represents the identity circuit taking as input the labels in \(Q\) and returning them unchanged, while \(\mathcal {C};g(\bar{\ell }) \rightarrow \bar{k}\) represents the application of the elementary operation \(g\) to the wires identified by \(\bar{\ell }\) among the outputs of \(\mathcal {C}\). Operation \(g\) outputs the wire bundle \(\bar{k}\), whose labels become part of the outputs of the overall circuit. Note that an “elementary operation” is usually the application of a gate, but it could also be a measurement, or the initialization or discarding of a wire. Although semantically very different, from the perspective of circuit building these operations are just elementary building blocks in the construction of a more complex structure, and it makes no sense to distinguish between them syntactically. Circuits are amenable to a form of concatenation. We write the concatenation of \(\mathcal {C}\) and \(\mathcal {D}\) as \(\mathcal {C}::\mathcal {D}\) and define it in the natural way, that is, as \(\mathcal {C}\) followed by all the operations occurring in \(\mathcal {D}\).

Circuit Typing Naturally, not all circuits built from the CRL syntax make sense. For example \(id_{(\ell :\textsf{Qubit})};H(k) \rightarrow k\) and \(id_{(\ell :\textsf{Qubit})}; CNOT (\langle \ell ,\ell \rangle ) \rightarrow \langle k,t\rangle \) are both syntactically correct, but the first applies a gate to a non-existing wire, while the second violates the no-cloning theorem by duplicating \(\ell \). To rule out such ill-formed circuits, we employ a rudimentary type system for circuits which allows us to derive judgments of the form \(\mathcal {C} : Q \rightarrow L\), which informally read “circuit \(\mathcal {C}\) is well-typed with input label context \(Q\) and output label context \(L\)”.

Fig. 10.
figure 10

The CRL type system

The typing rules for CRL are given in Figure 10. We call \(Q \vdash _w \bar{\ell } : T\) a wire judgment, and we use it to give a structured type to an otherwise unordered label context, by means of a wire bundle. Most rules are straightforward, except those for lists, which rely on a judgment of the form \( \vDash I = J\). This is to be intended as a semantic judgment asserting that \(I\) and \(J\) are closed and equal when interpreted as natural numbers. Within the rule, this reflects the idea that there are many ways to syntactically represent the length of a list. For example, \(\textsf{nil}\) can be given type \(\textsf{List}^{0}\,{T}\), but also \(\textsf{List}^{1 - 1}\,{T}\) or \(\textsf{List}^{0 \times 5}\,{T}\). This kind of flexibility might seem unwarranted for such a simple language, but it is useful to effectively interface CRL and the more complex Proto-Quipper-R. Speaking of the actual circuit judgments, the seq rule tells us that the the application of an elementary operation \(g\) is well typed whenever \(g\) only acts on labels occurring in the outputs of \(\mathcal {C}\) (those in \(\bar{\ell }\), that is in \(H\)), produces in output labels that do not clash with the remaining outputs of \(\mathcal {C}\) (since \(L,K\) denotes the disjoint union of the two label contexts) and is of the right type. This last requirement is expressed as \(g\in \mathscr {G}(T,U)\), where \(\mathscr {G}(T,U)\) is the subset of elementary operations that can be applied to an input of type \(T\) to obtain an output of type \(U\). For example, the Hadamard gate, which acts on a single qubit, is in \(\mathscr {G}(\textsf{Qubit},\textsf{Qubit})\).

Circuit Width Among the many properties of circuits, we are interested in width, so we conclude this section by giving a formal status to this quantity.

Definition 1 (Circuit Width)

[Circuit Width] We define the width of a CRL circuit \(\mathcal {C}\), written \({\text {width}}(\mathcal {C})\), as follows

$$\begin{aligned} {\text {width}}(id_{Q}) &= |Q|,\end{aligned}$$
(1)
$$\begin{aligned} {\text {width}}(\mathcal {C};g(\bar{\ell }) \rightarrow \bar{k}) &= {\text {width}}(\mathcal {C}) + \max (0, {\text {new}}(g) - {\text {discarded}}(\mathcal {C})), \end{aligned}$$
(2)

where \(|Q|\) is the number of labels in \(Q\), \({\text {new}}(g)\) represents the net number of new wires initialized by \(g\), and \({\text {discarded}}(\mathcal {C})\) is the number of wires that have been effectively discarded by the end of \(\mathcal {C}\), obtained as the difference between \(\mathcal {C}\)’s width and the number of its outputs. Note that one expects \({\text {new}}(g)\) to be equal to the difference between the number of labels in \(\bar{k}\) and those in \(\bar{\ell }\). The overarching idea behind this definition is that whenever we require new wires in our computation, we first try to reuse as many previously discarded wires as possible. As long as we can do this (\({\text {new}}(g) \le {\text {discarded}}(\mathcal {C})\)), the initializations do not add to the total width of the circuit. Otherwise (\({\text {new}}(g) > {\text {discarded}}(\mathcal {C})\)) we must actually create new wires, increasing the overall width of the circuit.

Now that we have a formal definition of circuit types and width, we can state a fundamental property of the concatenation of well-typed circuits, which is illustrated in Figure 11 and proven in Theorem 1. We use this result pervasively in proving the correctness of Proto-Quipper-R in section 5.

Theorem 1 (CRL)

[CRL] Given \(\mathcal {C} : Q \rightarrow L,H\) and \(\mathcal {D} : H \rightarrow K\) such that the labels shared by \(\mathcal {C}\) and \(\mathcal {D}\) are all and only those in \(H\), we have

  1. 1.

    \(\mathcal {C}::\mathcal {D} : Q \rightarrow L,K,\)

  2. 2.

    \({\text {width}}(\mathcal {C}::\mathcal {D}) \le {\text {max}}({\text {width}}(\mathcal {C}),{\text {width}}(\mathcal {D}) + |L|).\)

Proof

By induction of the derivation of \(\mathcal {D} : H \rightarrow K\).

Fig. 11.
figure 11

The kind of scenario described by Theorem 1

4.3 Typing Programs

Going back to Proto-Quipper-R, we have already seen how the standard Proto-Quipper types are refined with quantitative information. However, decorating types is not enough for the purposes of width estimation. Recall that, in general, a Proto-Quipper program produces a circuit as a side effect of its evaluation. If we want to reason about the width of said circuit, it is not enough to rely on a regular linear type system, although dependent. Rather, we have to introduce the second ingredient of our analysis and turn to a type-and-effect system [40], revolving around a type judgment of the form

$$\begin{aligned} \varTheta ;\varGamma ;Q \vdash _c M : A ; I, \end{aligned}$$
(3)

which intuitively reads “for all natural values of the index variables in \(\varTheta \), under typing context \(\varGamma \) and label context \(Q\), term \(M\) has type \(A\) and produces a circuit of width at most \(I\)”. Therefore, \(\varTheta \) is a collection of index variables which are universally quantified in the rest of the judgment, while \(\varGamma \) is a typing context for parameter and linear variables alike. When a typing context contains exclusively parameter variables, we write it as \(\varPhi \). In this judgment, \(I\) plays the role of an Effect annotation, describing a relevant aspect of the side effect produced by the evaluation of \(M\) (i.e. the width of the produced circuit). The attentive reader might wonder why this annotation consists only of one index, whereas when we discussed arrow types in previous sections we needed two. The reason is that the second index, which we use to keep track of the number of wires captured by a function, is redundant in a typing judgment where the same quantity can be inferred directly from the environments \(\varGamma \) and \(Q\). A similar typing judgment of the form \(\varTheta ;\varGamma ;Q \vdash _v V : A\) is introduced for values, which are effect-less.

Fig. 12.
figure 12

Proto-Quipper-R type system

The rules for deriving typing judgments are those in Figure 12, where \(\varGamma _1,\varGamma _2\) denotes the union of two contexts with disjoint domains. A well-formedness judgment of the form \(\varTheta \vdash I\) means that all the free index variables occurring in \(I\) are in \(\varTheta \). Well-formedness is lifted to types and typing contexts in the natural way. Among interesting typing rules, we can see how the Circ rule bridges between CRL and Proto-Quipper-R. A boxed circuit \((\bar{\ell },\mathcal {C},\bar{k})\) is well typed with type \(\textsf{Circ}^{I}(T,U)\) when \(\mathcal {C}\) is no wider than the quantity denoted by \(I\), \(\mathcal {C} : Q \rightarrow L\) and \(\bar{\ell },\bar{k}\) contain all and only the labels in \(Q\) and \(L\), respectively, acting as a language-level interface to \(\mathcal {C}\).

The two main constructs that interact with circuits are \({\textsf{apply}}\) and \(\textsf{box}\). The apply rule is the foremost place where effects enter the type derivation: \(V\) represents some boxed circuit of width at most \(I\), so its application to an appropriate wire bundle \(W\) produces exactly a circuit of width at most \(I\). The box rule, on the other hand, works approximately in the opposite direction. If \(V\) is a circuit building function that, once applied to an input of type \(T\), would build a circuit of output type \(U\) and width at most \(I\), then boxing it means turning it into a boxed circuit with the same characteristics. Note that the Box rule requires that the typing context be devoid of linear variables. This reflects the idea that \(V\) is meant to be executed in complete isolation, to build a standalone, replicable circuit, and therefore it should not capture any linear resource (e.g. a label) from the surrounding environment.

Wire Count Notice that many rules rely on an operator written \(\#(\cdot )\), which we call the wire count operator. Intuitively, this operator returns the number of wire resources (in our case, bits or qubits) represented by a type or context. To understand how this is important, consider the return rule. The \(\textsf{return}\) operator turns a value \(V\) into a trivial computation that evaluates immediately to \(V\), and therefore it would be tempting to give it an effect annotation of 0. However, \(V\) is not necessarily a closed value. In fact, it might very well contain many bits and qubits, coming both from the typing context \(\varGamma \) and the label context \(Q\). Although nothing happens to these bits and qubits, they still corresponds to wires in the underlying circuit, and these wires have a width which must be accounted for in the judgment for the otherwise trivial computation. The return rule therefore produces an effect annotation of the form \(\#(\varGamma ;Q)\), which is shorthand for \(\#(\varGamma ) + \#(Q)\) and corresponds exactly to this quantity. A formal definition of the wire count operator on types is given in the following definition, which is lifted to contexts in the natural way.

Definition 2 (Wire Count)

[Wire Count] We define the wire count of a type \(A\), written \(\#(A)\), as a function \(\#(\cdot ): TYPE \rightarrow INDEX \) such that

figure a

This definition is fairly straightforward, except for the arrow case. By itself, an arrow type does not give us any information about the amount of qubits or bits captured in the corresponding closure. This is precisely where the second index \(J\), which keeps track exactly of this quantity, comes into play. This annotation is introduced by the Abs rule and allows our analysis to circumvent data hiding.

The let rule is another rule in which wire counts are essential. The two terms \(M\) and \(N\) in \(\textsf{let}\; x = M \;\textsf{in}\; N\) build the circuits \(\mathcal {C}_M\) and \(\mathcal {C}_N\), whose widths are bounded by \(I\) and \(J\), respectively. Once again, it might be tempting to conclude that the overall circuit built by the let construct has width bounded by \(\textsf{max}(I,J)\), but this fails to take into account the fact that while \(M\) is building \(\mathcal {C}_M\) starting from the wires contained in \(\varGamma _1\) and \(Q_1\), we must keep aside the wires contained in \(\varGamma _2\) and \(Q_2\), which will be used by \(N\) to build \(\mathcal {C}_N\). These wires must flow alongside \(\mathcal {C}_M\) and their width, i.e. \(\#(\varGamma _2;Q_2)\), adds up to the total width of the left-hand side of the \(\textsf{let}\) construct, leading to an overall width upper bound of \(\textsf{max}(I+\#(\varGamma _2;Q_2),J)\). This situation is better illustrated in Figure 13.

Fig. 13.
figure 13

The shape of a circuit built by a \(\textsf{let}\) construct

The last rule that makes substantial use of wire counts is fold, arguably the most complex rule of the system. The main ingredient of the fold rule is the bound index variable \(i\), which occurs in the accumulator type \(B\) and is used to keep track of the number of steps performed by the fold. Let \((\cdot )\{I/i\}\) denote the capture-avoiding substitution of the index term \(I\) for the index variable \(i\) inside an index, type, context, value or term, not unlike \((\cdot )[V/x]\) denotes the capture-avoiding substitution of the value \(V\) for the variable \(x\). Intuitively, if the accumulator has initially type \(B\{0/i\}\) and each application of the step function increases \(i\) by one, then when we fold over a list of length \(I\) we get an output of type \(B\{I/i\}\). Index \(E\) is the upper bound to the width of the overall circuit built by the fold: if the input list is empty, then the width of the circuit is just the number of wires contained in the initial accumulator, that is, \(\#(\varGamma ;Q)\). If the input list is non-empty, on the other hand, things get slightly more complicated. At each step \(i\), the step function builds a circuit \(\mathcal {C}_i\) of width bounded by \(J\), where \(J\) might depend on \(i\). This circuit takes as input all the wires in the accumulator, as well as the wires contained in the first element of the input list, which are \(\#(A)\). The wires contained in remaining \(I- 1 - i\) elements have to flow alongside \(\mathcal {C}_i\), giving a width upper bound of \(J+ (I- 1 - i) \times \#(A)\) at each step \(i\). The overall width upper bound is then the maximum for \(i\) going from 0 to \(I-1\) of this quantity, i.e. precisely \(\textsf{max}_{i < I}\,J+ (I- 1 - i) \times \#(A)\). Once again, a graphical representation of this scenario is given in Figure 14.

Fig. 14.
figure 14

The shape of a circuit built by a fold applied to an input list of type \(\textsf{List}^{I}\,{A}\)

Subtyping Notice that Proto-Quipper-R’s type system includes two subsumption rules, which are effectively the same rule for terms and values, respectively: csub and vsub. We mentioned that our type system resembles a refinement type system, and all such systems induce a subtyping relation between types, where \(A\) is a subtype of \(B\) whenever the former is “at least as refined” as the latter. In our case, a subtyping judgment such as \(\varTheta \vdash _s A <: B\) means that for all natural values of the index variables in \(\varTheta \), \(A\) is a subtype of \(B\).

Fig. 15.
figure 15

Proto-Quipper-R subtyping rules

We derive this kind of judgments by the rules in Figure 15. Note that \(\varTheta \vdash _s A <:> B\) is shorthand for “\(\varTheta \vdash _s A <: B\) and \(\varTheta \vdash _s B <: A\)”. Subtyping relies in turn on a judgment of the form \(\varTheta \vDash I \le J\), which is a generalization of the semantic judgment that we used in the CRL type system in Section 4.2. Such a judgment asserts that for all natural values of the index variables in \(\varTheta \), \(I\) is lesser or equal than \(J\). Consequently, \(\varTheta \vDash I = J\) is shorthand for “\(\varTheta \vDash I \le J\) and \(\varTheta \vDash J \le I\)”. We purposefully leave the decision procedure of this kind of judgments unspecified, with the prospect that, from a more practical perspective, they could be delegated to an SMT solver [7].

4.4 Operational Semantics

Operationally speaking, it does not make sense, in the Proto-Quipper languages, to speak of the semantics of a term in isolation: a term is always evaluated in the context of an underlying circuit that supplies all of the term’s free labels. We therefore define the operational semantics of Proto-Quipper-R as a big-step evaluation relation \(\Downarrow \) on configurations, i.e. circuits paired with either terms or values. Intuitively, \((\mathcal {C},M)\Downarrow (\mathcal {D},V)\) means that \(M\) evaluates to \(V\) and updates \(\mathcal {C}\) to \(\mathcal {D}\) as a side effect.

Fig. 16.
figure 16

Proto-Quipper-R big-step operational semantics

The rules for evaluating configurations are given in Figure 16, where \(\mathcal {C},\mathcal {D}\) and \(\mathcal {E}\) are circuits, \(M\) and \(N\) are terms, while \(V,W,X,Y\) and \(Z\) are values. Most evaluation rules are straightforward, with the exception perhaps of apply, box and fold-step. Being the fundamental block of circuit-building, the semantics of \({\textsf{apply}}\) lies almost entirely in the way it updates the underlying circuit. The concatenation of the underlying circuit \(\mathcal {C}\) and the applicand \(\mathcal {D}\) is delegated entirely to the \({\text {append}}\) function, which is given in Definition 4. Before we examine the \({\text {append}}\) function, however, consider than when we deal with circuit objects we are not really interested in the concrete labels that occur in them, but rather in the structure that they convey. For this reason, we introduce the following notion of circuit equivalence.

Definition 3 (Circuit Equivalence)

[Circuit Equivalence] We say that two boxed circuits \((\bar{\ell },\mathcal {C},\bar{k})\) and \((\bar{t},\mathcal {D},\bar{q})\) are equivalent, and we write \((\bar{\ell },\mathcal {C},\bar{k}) \cong (\bar{t},\mathcal {D},\bar{q})\), when there exists a renaming \(\rho \) of labels such that \(\rho (\bar{\ell }) = \bar{t}\), \(\rho (\bar{k}) = \bar{q}\) and \(\rho (\mathcal {C}) = \mathcal {D}\).

We can now move on to the definition of \({\text {append}}\), where the notion of circuit equivalence is used to instantiate the generic input interface of a boxed circuit with the actual labels that it is going to be appended to, and to ensure that there are no name clashes between the appended circuit and the underlying circuit.

Definition 4 (\({\text {append}}\))

[\({\text {append}}\)] We define the append of \((\bar{\ell },\mathcal {D},\bar{k})\) to \(\mathcal {C}\) on \(\bar{t}\), written \({\text {append}}(\mathcal {C},\bar{t},(\bar{\ell },\mathcal {D},\bar{k}))\), as the function that performs the following steps:

  1. 1.

    Finds \((\bar{t},\mathcal {D}',\bar{q}) \cong (\bar{\ell },\mathcal {D},\bar{k})\) such that the labels shared by \(\mathcal {C}\) and \(\mathcal {D}'\) are all and only those in \(\bar{t}\),

  2. 2.

    Computes \(\mathcal {E}= \mathcal {C}::\mathcal {D}'\),

  3. 3.

    Returns \((\mathcal {E},\bar{q})\).

On the other hand, the semantics of a term of the form \(\textsf{box}_{T} ({\textsf{lift}}M)\) relies on the \({\text {freshlabels}}\) function. What \({\text {freshlabels}}\) does is take as input a bundle type \(T\) and instantiate fresh \(Q,\bar{\ell }\) such that \(Q \vdash _w \bar{\ell } : T\). The wire bundle \(\bar{\ell }\) is then used as a dummy argument to \(V\), the circuit-building function resulting from the evaluation of \(M\). This function application is evaluated in the context of the identity circuit \(id_{Q}\) and eventually produces a circuit \(\mathcal {D}\), together with its output labels \(\bar{k}\). Finally, \(\bar{\ell }\) and \(\bar{k}\) become respectively the input and output interfaces of the boxed circuit \((\bar{\ell },\mathcal {D},\bar{k})\), which is the result of the evaluation of \(\textsf{box}_{T} ({\textsf{lift}}M)\).

Note, at this point, that \(T\) controls how many labels are initialized by the \({\text {freshlabels}}\) function. Because \(T\) can contain indices (e.g. it could be that \(T\equiv \textsf{List}^{3}\,{\textsf{Qubit}}\)), it follows that in Proto-Quipper-R indices are not only relevant to typing, but they also have operational value. For this reason, the semantics of Proto-Quipper-R is well-defined only on terms closed both in the sense of regular variables and index variables, since a circuit-building function of input type, say, \(\textsf{List}^{i}\,{\textsf{Qubit}}\) does not correspond to any individual circuit, and therefore it makes no sense to box it. This aspect of the semantics is also apparent in the fold-step rule, where the index variable \(i\) occurring free in \(M\) is instantiated to 0 before evaluating \(M\) to obtain the step function \(Y\). Then, before evaluating the next fold, \(i\) is replaced with \(i + 1\) in \(M\), increasing the index for the next iteration.

5 Type Safety and Correctness

Because the operational semantics of Proto-Quipper-R is based on configurations, we ought to adopt a notion of well-typedness which is also based on configurations. The following definition of well-typed configuration is thus central to our type-safety analysis.

Definition 5 (Well-typed Configuration)

[Well-typed Configuration] We say that configuration \((\mathcal {C},M)\) is well-typed with input \(Q\), type \(A\), width \(I\) and output \(L\), and we write \(Q \vdash (\mathcal {C},M) : A ; I; L\), whenever \(\mathcal {C} : Q \rightarrow L,H\) for some \(H\) such that \(\emptyset ;\emptyset ;H \vdash _c M : A ; I\). We write \(Q \vdash (\mathcal {C},V) : A ; L\) whenever \(\mathcal {C} : Q \rightarrow L,H\) for some \(H\) such that \(\emptyset ;\emptyset ;H \vdash _v V : A\).

The three results that we want to show in this section are that any well-typed term configuration \(Q \vdash (\mathcal {C},M) : A ; I; L\) evaluates to some configuration \((\mathcal {D},V)\), that \(Q \vdash (\mathcal {D},V) : A ; L\) and that \(\mathcal {D}\) is obtained from \(\mathcal {C}\) by extending it with a sub-circuit of width at most \(I\). These claims correspond to the Subject reduction and total correctness properties that we will prove at the end of this section. However, both these results rely on a central lemma and on the mutual notions of realization and reducibility, which we first give formally.

Definition 6 (Realization)

[Realization] We define \(V \Vdash _{Q} A\), which reads \(V\) realizes \(A\) under \(Q\), as the smallest relation such that

  • \(* \Vdash _{\emptyset } \mathbbm {1},\)

  • \(\ell \Vdash _{\ell :w} w,\)

  • \(V \Vdash _{Q} A \multimap _{I,J} B\) iff \( \vDash J = |Q|\) and \(\forall W: W \Vdash _{L} A \implies V\,W \Vdash _{Q,L}^{I} B,\)

  • \({\textsf{lift}}M \Vdash _{\emptyset } {\text {!}}A\) iff \(M \Vdash _{\emptyset }^{0} A\),

  • \(\langle V,W\rangle \Vdash _{Q,L} A \otimes B\) iff \(V \Vdash _{Q} A\) and \(W \Vdash _{L} B\),

  • \(\textsf{nil} \Vdash _{\emptyset } \textsf{List}^{I}\,{A}\) iff \( \vDash I = 0,\)

  • \(\textsf{cons}\; {V}\; {W} \Vdash _{Q,L} \textsf{List}^{I}\,{A}\) iff \( \vDash I = J + 1\) and \(V \Vdash _{Q} A\) and \( W \Vdash _{L} \textsf{List}^{J}\,{A},\)

  • \((\bar{\ell },\mathcal {C},\bar{k}) \Vdash _{\emptyset } \textsf{Circ}^{I}(T,U)\) iff \(\mathcal {C} : Q \rightarrow L\) and \( Q \vdash _w \bar{\ell } : T\) and \( L \vdash _w \bar{k} : U\) and \( \vDash {\text {width}}(\mathcal {C}) \le I\).

Definition 7 (Reducibility)

[Reducibility] We say that \(M\) is reducible under \(Q\) with type \(A\) and width \(I\), and we write \(M \Vdash _{Q}^{I} A\), if, for all \(\mathcal {C}\) such that \(\mathcal {C} : L \rightarrow Q,H\), there exist \(\mathcal {D},V\) such that

  1. 1.

    \((\mathcal {C},M) \Downarrow (\mathcal {C}::\mathcal {D},V),\)

  2. 2.

    \( \vDash {\text {width}}(\mathcal {D}) \le I,\)

  3. 3.

    \(\mathcal {D} : Q \rightarrow K\) for some \(K\) such that \(V \Vdash _{K} A\).

Both relations, and in particular reducibility, are given in the form of unary logical relations [55]. The intuition is pretty straightforward: a term is reducible with width \(I\) if it evaluates correctly when paired with any circuit \(\mathcal {C}\) which provides its free labels and if it extends \(\mathcal {C}\) with a sub-circuit \(\mathcal {D}\) whose width is bounded by \(I\). Realization, on the other hand, is less immediate. For most cases, realizing type \(A\) loosely corresponds to being closed and well-typed with type \(A\), but a value realizes an arrow type \(A \multimap _{I,J} B\) when its application to a value realizing \(A\) is reducible with type \(B\) and width \(I\).

By themselves, realization and reducibility are defined only on terms and values closed in the sense both of regular and index variables. To extend these notions to open terms and values, we adopt the standard approach of reasoning explicitly about the substitutions that would render them closed. A closing value substitution \(\gamma \) is a function that turns an open term \(M\) into a closed term \(\gamma (M)\) by substituting a value for each free variable occurring in \(M\). We say that \(\gamma \) implements a typing context \(\varGamma \) using label context \(Q\), and we write \(\gamma \vDash _{Q} \varGamma \), when it replaces every variable \(x_i\) in the domain of \(\varGamma \) with a value \(V_i\) such that \(V_i \Vdash _{Q_i} \varGamma (x_i)\) and \( Q=\biguplus _{x_i\in {\text {dom}}(\varGamma )} Q_i\). A closing index substitution \(\theta \) is similar, only it substitutes closed indices for index variables and can be applied to indices, types, contexts, values and terms alike. We say that \(\theta \) implements an index context \(\varTheta \), and we write \(\theta \vDash \varTheta \), when it replaces every index variable in \(\varTheta \) with a closed index term. This allows us to give the following fundamental lemma, which will be used while proving all other claims.

Lemma 1 (Core Correctness)

[Core Correctness] Let \(\varPi \) be a type derivation. For all \(\theta \vDash \varTheta \) and \(\gamma \vDash _{Q} \theta (\varGamma )\), we have that

$$\begin{aligned} \varPi \triangleright \varTheta ;\varGamma ;L \vdash _c M : A ; I &\implies \gamma (\theta (M)) \Vdash _{Q,L}^{\theta (I)} \theta (A),\\ \varPi \triangleright \varTheta ;\varGamma ;L \vdash _v V : A &\implies \gamma (\theta (V)) \Vdash _{Q,L} \theta (A). \end{aligned}$$

Proof

By induction on the size of \(\varPi \), making use of Theorem 1.

Lemma 1 tells us that any well-typed term (resp. value) is reducible (resp. realizes its type) when we instantiate its free variables according to its contexts. Now that we have Lemma 1, we can proceed to proving the aforementioned results of subject reduction and total correctness. We start with the former, which unsurprisingly requires the following substitution lemmata.

Lemma 2 (Index Substitution)

[Index Substitution] Let \(\varPi \) be a type derivation and let \(I\) be an index such that \(\varTheta \vdash I\). We have that

$$\begin{aligned} \varPi \triangleright \varTheta ,i;\varGamma ;Q \vdash _c M : A ; J &\implies \varTheta ;\varGamma \{I/i\};Q \vdash _c M\{I/i\} : A\{I/i\} ; J\{I/i\},\\ \varPi \triangleright \varTheta ,i;\varGamma ;Q \vdash _v V : A &\implies \varTheta ;\varGamma \{I/i\};Q \vdash _v V\{I/i\} : A\{I/i\}. \end{aligned}$$

Proof

By induction on the size of \(\varPi \).

Lemma 3 (Value Substitution)

[Value Substitution] Let \(\varPi \) be a type derivation and let \(V\) be a value such that \(\varTheta ;\varPhi ,\varGamma _1;Q_1 \vdash _v V : A\). We have that

$$\begin{aligned} \varPi \triangleright \varTheta ;\varPhi ,\varGamma _2,x:A;Q_2 \vdash _c M : B ; I &\implies \varTheta ;\varPhi ,\varGamma _1,\varGamma _2;Q_1,Q_2 \vdash _c M[V/x] : B ; I,\\ \varPi \triangleright \varTheta ;\varPhi ,\varGamma _2,x:A;Q_2 \vdash _v W : B &\implies \varTheta ;\varPhi ,\varGamma _1,\varGamma _2;Q_1,Q_2 \vdash _v W[V/x] : B. \end{aligned}$$

Proof

By induction on the size of \(\varPi \).

Theorem 2 (Subject Reduction)

[Subject Reduction] If \(Q \vdash (\mathcal {C},M) : A ; I; L\) and \((\mathcal {C},M) \Downarrow (\mathcal {D},V)\), then \(Q \vdash (\mathcal {D},V) : A ; L\).

Proof

By induction on the derivation of \((\mathcal {C},M) \Downarrow (\mathcal {D},V)\) and case analysis on the last rule used in its derivation. Lemma 3 is essential to the app,dest and let cases, while Lemma 2 is used in the fold-step case. Lemma 1 is essential to the box case, as it is the only case in which the side effect of the evaluation (the circuit built by the function being boxed), whose preservation is the a matter of correctness, becomes a value (the resulting boxed circuit).

Of course, type soundness is not enough: we also want the resource analysis carried out by our type system to be correct, as stated in the following theorem.

Theorem 3 (Total Correctness)

[Total Correctness] If \(Q \vdash (\mathcal {C},M) : A ; I; L\), then there exist \(\mathcal {D},V\) such that \((\mathcal {C},M) \Downarrow (\mathcal {C}::\mathcal {D},V)\) and \( \vDash {\text {width}}(\mathcal {D}) \le I\).

Proof

By definition, \(Q \vdash (\mathcal {C},M) : A ; I; L\) entails that \(\mathcal {C} : Q \rightarrow L,H\) and \(\emptyset ;\emptyset ;H \vdash _c M : A ; I\). Since an empty context is trivially implemented by an empty closing substitution, by Lemma 1 we get \(M \Vdash _{H}^{I} A\), which by definition entails that there exist \(\mathcal {D},V\) such that \((\mathcal {C},M)\Downarrow (\mathcal {C}::\mathcal {D},V)\) and \( \vDash {\text {width}}(\mathcal {D}) \le I\).

6 A Practical Example

This section provides an example of how Proto-Quipper-R can be used to verify the resource usage of realistic quantum algorithms. In particular, we use our language to implement the QFT algorithm [11, 39] and verify that the circuits it produces have width no greater than the size of their input, i.e. that the QFT algorithm does not overall use additional ancillary qubits.

Fig. 17.
figure 17

A Proto-Quipper-R implementation of the Quantum Fourier Transform circuit family. The usual syntactic sugar is employed.

The Proto-Quipper-R implementation of the QFT algorithm is given in Figure 17. As we walk through the various parts of the program, be aware that we will focus on the resource aspects of the algorithm, ignoring much of its actual meaning. Starting bottom-up, we assume that we have an encoding of naturals in the language and that we can perform arithmetic on them. We also assume some primitive gates and gate families: \(\textsf{H}\) is the boxed circuit corresponding to the Hadamard gate and has type \(\textsf{Circ}^{1}(\textsf{Qubit},\textsf{Qubit})\), whereas the \(\textsf{makeRGate}\) function has type \(\textsf{Nat} \multimap _{0,0} \textsf{Circ}^{2}(\textsf{Qubit} \otimes \textsf{Qubit},\textsf{Qubit} \otimes \textsf{Qubit})\) and produces instances of the parametric controlled \(R_n\) gate. On the other hand, \( qlen \) and \( rev \) stand for regular language terms which implement respectively the linear list length and reverse functions. They have type \( qlen :\textsf{List}^{i}\,{\textsf{Qubit}} \multimap _{i,0} (\textsf{Nat} \otimes \textsf{List}^{i}\,{\textsf{Qubit}})\) and \( rev :\textsf{List}^{i}\,{\textsf{Qubit}} \multimap _{i,0} \textsf{List}^{i}\,{\textsf{Qubit}}\) in our type system.

We now turn our attention to the actual QFT algorithm. Function \( qftStep \) builds a single step of the QFT circuit. The width of the circuit produced at step \(j\) is dominated by the folding of the \( rotate \,n\) function, which applies controlled rotations between appropriate pairs of qubits and has type

$$\begin{aligned} {(\textsf{Qubit} \otimes \textsf{List}^{e}\,{\textsf{Qubit}}) \otimes \textsf{Qubit} \multimap _{e + 2,0} \textsf{Qubit} \otimes \textsf{List}^{e + 1}\,{\textsf{Qubit}}}, \end{aligned}$$
(4)

meaning that \( rotate \,n\) rearranges the structure of its inputs, but overall does not introduce any new wire. We fold this function starting from an accumulator \(\langle q,\textsf{nil}\rangle \), meaning that we can give \({\textsf{fold}}_{j}\, {({\textsf{lift}}( rotate \,n))}\; {\langle q,\textsf{nil}\rangle }\) type as follows:

(5)

where \(\textsf{Q}\) is shorthand for \(\textsf{Qubit}\) and where we implicitly use the fact that \(i,j \vDash \textsf{max}(1,\textsf{max}_{e < j}\,e + 2 + (j - 1 - e) \times 1) = j + 1\) to simplify the arrow’s width annotation using vsub and the arrow subtyping rule. Next, we fold over \( revQs \), which has the same elements as \( qs \) and thus has length \(j\), and we obtain that the fold produces a circuit whose width is bounded by \(j + 1\). Therefore, \( qftStep \) has type

$$\begin{aligned} {\text {!}}((\textsf{List}^{j}\,{\textsf{Qubit}} \otimes \textsf{Qubit}) \multimap _{j + 1,0} \textsf{List}^{j + 1}\,{\textsf{Qubit}}), \end{aligned}$$
(6)

which entails that when we pass it as an argument to the topmost \({\textsf{fold}}\) together with \(\textsf{nil}\) we can conclude that the type of the \( qft \) function is

(7)

where we once again implicitly simplify the arrow type using the fact that \(i \vDash \textsf{max}(0,\textsf{max}_{j < i}\,j + 1 + (i - 1 - j) \times 1) = i\). This concludes our analysis and the resulting type tells us that \( qft \) produces a circuit of width at most \(i\) on inputs of size \(i\), without overall using any additional wires. If we instantiate \(i\) to 3, for example, we can apply \( qft \) to a list of 3 qubits to obtain the circuit shown in Figure 18, whose width is exactly 3.

Fig. 18.
figure 18

The circuit of input size 3 produced by \( qft \,(\textsf{cons}\; {q_1}\; {\textsf{cons}\; {q_2}\; {\textsf{cons}\; {q_3}\; {\textsf{nil}}}})\)

To conclude this section, note that for ease of exposition \( qft \) actually produces the reversed QFT circuit. This is not a problem, since the two circuits are equivalent resource-wise and the actual QFT circuit can be recovered by boxing the result of \( qft \) and reversing it via a primitive operator. Besides, note that Quipper’s internal implementation of the QFT is also reversed [16].

7 Related Work

The metatheory of quantum circuit description languages, and in particular of Quipper-style languages, has been the subject of quite some work in recent years, starting with Ross’s thesis on Proto-Quipper-S [48] and going forward with Selinger and Rios’s Proto-Quipper-M [46]. In the last five years, some proposals have also appeared for more expressive type systems or for language extensions that can handle non-standard language features, such as the so-called dynamic lifting [8, 21, 35], available in the Quipper language, or dependent types [22]. Although some embryonic contributions in the direction of analyzing the size of circuits produced using Quipper have been given [56], no contribution tackles the problem of deriving resource bounds parametric on the size of the input. In this, the ability to have types which depend on the input, certainly a feature of Proto-Quipper-D [22], is not useful for the analysis of intensional attributes of the underlying circuit, simply because such attributes are not visible in types.

If we broaden the horizon to quantum programming languages other than Quipper, we come across, for example, the recent works of Avanzini et al. [5] and Liu et al. [36] on adapting the classic weakest precondition technique to the cost analysis of quantum programs, which however focus on programs in an imperative language. The work of Dal Lago et al. [13] on a quantum language which characterizes complexity classes for quantum polynomial time should certainly be remembered: even though the language allows the use of higher-order functions, the manipulation of quantum data occurs directly and not through circuits. Similar considerations hold for the recent work of Hainry et al. [29] and Yamakami’s algebra of functions [59] in the style of Bellantoni and Cook [6], both characterizing quantum polynomial time.

If we broaden our scope further and become interested in the analysis of the cost of classical or probabilistic programs, we face a vast literature, with contributions employing a variety of techniques on heterogeneous languages and calculi: from functional programs [2, 32, 33] and term rewriting systems [3, 4, 41] to probabilistic [34] and object-oriented programs [19, 28]. In this context, the resource under analysis is often assumed to be computation time, which is relatively easy to analyze given its strictly monotonic nature. Circuit width, although monotonically non-decreasing, evolves in a way that depends on a non-monotonic quantity, i.e. the number of wires discarded by a circuit. As a result, width has the flavor of space and its analysis is less straightforward.

It is also worth mentioning that linear dependent types can be seen as a specialized version of refinement types [18], which have been used extensively in the literature to automatically verify interesting properties of programs [37, 62]. In particular, the work of Vazou et al. on Liquid Haskell [57, 58] has been of particular inspiration, on account of Quipper being embedded precisely in Haskell. The liquid type system [47] of Liquid Haskell relies on SMT solvers to discharge proof obligations and has been used fruitfully to reason about both the correctness and the resource consumption (mainly time complexity) of concrete, practical programs [30].

8 Generalization to Other Resource Types

This work focuses on estimating the width of the circuits produced by Quipper programs. This choice is dictated by the fact that the width of a circuit corresponds to the maximum number of distinct wires, and therefore individual qubits, required to execute it. Nowadays, this is considered as one of the most precious resources in quantum computing, and as such must be kept under control. However, this does not mean that our system could not be adapted to the estimation of other parameters. This section outlines how this may be possible.

First, estimating strictly monotonic resources, such as the total number of gates in a circuit, is possible and in fact simpler than estimating width. A single index term \(I\) that measures the number of gates in the circuit built by a computation would be enough to carry out this analysis. This index would be appropriately increased any time an \({\textsf{apply}}\) instruction is executed, while sequencing two terms via \(\textsf{let}\) would simply add together the respective indices.

If we were instead interested in the depth of a circuit, then we would need a slightly different approach. Although in principle it would be possible to still rely on a single index \(I\), this would give rise to a very coarse approximation, effectively collapsing the analysis of depth to a gate count analysis. A more precise approximation could instead be obtained by keeping track of depth Locally. More specifically, it would be sufficient to decorate each occurrence of a wire type \(w\) with an index term \(I\) so that if a label \(\ell \) were typed with \(w^I\), it would mean that the sub-circuit rooted in \(\ell \) has a depth at most equal to \(I\).

Finally, it should be mentioned that the resources considered, i.e. the depth, width, and gate count of a circuit, can be further refined so as to take into account only some kinds of wires and gates. For instance, one could want to keep track of the maximum number of qubits needed, ignoring the number of classical bits, or at least distinguishing the two parameters, which of course have distinct levels of criticality in current quantum hardware.

9 Conclusion and Future Work

In this paper we introduced a linear dependent type system based on index refinements and effect typing for the paradigmatic calculus Proto-Quipper, with the purpose of using it to derive upper bounds on the width of the circuits produced by programs. We proved not only the classic type safety properties, but also that the upper bounds derived via the system are correct. We also showed how our system can verify a realistic quantum algorithm and elaborated on some ideas on how our technique could be adapted to other crucial resources types, like gate count and circuit depth. Ours is the first type system designed specifically for the purpose of resource analysis to target circuit description languages such as Quipper. Technically, the main novelties are the smooth combination of effect typing and index refinements, but also the proof of correctness, in which reducibility and effects are shown to play well together.

Among topics for further work, we can identify three main research directions. First and foremost, it would be valuable to investigate the ideas presented in this paper from a more practical perspective, that is, to provide a prototype implementation of the language with its type-checking procedure. The necessity to count the wires present in the context (e.g. when typing abstractions) makes it difficult to embed Proto-Quipper-R into existing languages, even those that, in principle, seem like ideal hosts, like Liquid Haskell [57] or Granule [42]. Because of this, we think that it would be better to produce a standalone implementation of Proto-Quipper-R that interfaces directly with SMT solvers to discharge the semantic judgments that are used pervasively in the typing rules.

Staying instead on the theoretical side of things, on one hand we have the prospect of denotational semantics: most incarnations of Proto-Quipper are endowed with categorical semantics that model both circuits and the terms of the language that build them [21, 22, 35, 46]. We already mentioned how the intensional nature of the quantity under analysis renders the formulation of an abstract categorical semantics for Proto-Quipper-R and its circuits a nontrivial task, but we believe that one such semantics would help Proto-Quipper-R fit better in the Proto-Quipper landscape.

On the other hand, in Section 8 we briefly discussed how our system could be modified to handle the analysis of different resource types. It would be interesting to test this path and to investigate the possibility of actually generalizing our resource analysis, that is, of making it parametric on the kind of resource being analyzed. This would allow for the same program in the same language to be amenable to different forms of verification, in a very flexible fashion.