Closed AdriaanRol closed 3 years ago
Below is a quote of an old post of mine on slack channel '#openql-and-friends' that pertains to this subject:
As some of you know, I'm working on an OpenQL backend for the new Central Controller. This CC differs from the CClight in that it has no notion of quantum operations and internally has a distributed architecture (with a separate processor per connected instrument). In order to be able to support conditional instructions, we need to be able to relate measurements (currently performed on an UHFQC) to actions (performed on VSM or maybe AWG). Because we have no a priori knowledge of the mapping of qubits on either, we devised an end user programmable (i.e. through the backend) Condition Engine. This Condition Engine can do many more things than the implicit conditional gates of the CClight (e.g. support expressions on 2 qubits, and support a concept of state), but these are currently difficult/impossible to express in OpenQL. Of course we can (and probably will) support the current conditional gates, but I think it would be a great improvement if the condition could be passed as a parameter (preferably a logic expression). We also support program flow conditional on measurement results ("comprehensive feedback"). This can only be expressed by the user in 'classical QASM', which is a bit awkward to support for us because it is very close to the CClight internal instruction set, but much less so to ours. As an example, we support a 'loop' instruction which combines decrementing a register and a conditional branch. As another example, in our instructions measurement results live in a register space different from the classic processor registers (namely in distributed shared memory controlled by the real time part of the processor). I think all of those translations would be easier if the classical part of QASM would be a higher level language. All in all I think a discussion on classical logic would be appropriate (maybe that is already going on for libqasm2.0, but I'm not connected to that)
meeting homework:
documentation on the CC can be found in the Central Controller dropbox. If you require access please let me know. Places to have a look are "doc/design/CC", "doc/presentations" and "doc/requirements/CC".
meeting homework:
indeed the instruction set of the CC does not operate at the qubit and quantum gate level like the CC-light, but instead it deals with digital inputs (from a UHFQC) and outputs (to an AWG). Moreover, the architecture is distributed: one sequence processor controls one instrument only, and sequence processors can exchange data (measurements) using Distributed Shared Memory. The current CC OpenQL backend mostly produces digital output (the seq_out instruction), the translation process from a quantum gate to a digital output happens in the backend (contrary to the CC-light where the ISA performs this), and involves the mapping of qubits to instruments. The CC "Q1" processor supports classical instructions like add, or, xor, see file "Q1_SEQ Instructionset.xlsx" in dropbox:/doc/design/CC. Additionally, the Distributed Shared Memory fabric supports a programmable feedback data path, see "cc_firmware_implementation-20191203.pdf" section 4.5.3.1 and 4.5.3.1 and also "CC-soft+firm-ware-design.docx" section 2.3
Feel free to ask more questions after reading through the documentation,
Meeting homework:
Feedback requirements are discussed in Sections A.11 and further of dropbox:doc/requirements/CC/QuSurf_CC_UseCases.docx
The feedback implementation of the CC was pointed to above: "Additionally, the Distributed Shared Memory fabric supports a programmable feedback data path, see "cc_firmware_implementation-20191203.pdf" section 4.5.3.1 and 4.5.3.1 and also "CC-soft+firm-ware-design.docx" section 2.3"
Update:
dropbox:doc/requirements/CC/CC_conditionalGates.docx discusses the relation between use cases for conditional gates and the code to implement those on the CC
We want to support binary conditional gates in OpenQL to map to the Central Controller. E.g.
{ measure q0, r0 } # measure q0: r0 = meas q0
{ bitcomplement r0, r1 } # bit-complement: r1 = ~r0
{ IF r0 X q0 | IF r1 Y q1 } # have X q0 and Y q1 in parallel, conditionally on r0 and ~r0
Conditional gate execution means:
The Central Controller supports conditional expressions in hardware (using a so-called feed-back multiplexer, MUX for short). These expressions have the power to shortcut the measurement and the conditional execution without the need of intermediate storage or operations: implementing the above example on the CC, the result of the measurement would be stored internally and used internally; there would be no need to pass them through r0 and r1:
It is limited what form of expression can be evaluated in a single cycle, in two, etc. It is possible to collect a result, store it in shared memory, and based on that do another expression in the next (or later) cycle.
It needs 3 program descriptions that are generated by the CC back-end:
As far as I (@jvansomeren) can judge, the MUX and SM together can be used to implement classical conditional expressions (and, or, not, compare, xor): the MUX for the expressions and the SM for the registers. The support of classical integer expressions would require a simple ALU (add, subtract, compare) with a set of integer registers (REG) and a path to/from the SM and other bits for conditionals. This implementation is assumed below in the examples.
The processing sequence and layering in OpenQL regarding conditional gates is:
Representation in cQASM and/or the OpenQL API This describes the language support for binary conditional gates.
Decomposing gates to match the primitive gates that are supported by the CC. Any high-level descriptions are broken down to those that are supported:
Scheduling the circuit
Instruction generation by the back-end
Some proposals for representation were already done, some of which were already implemented:
cQASM2.0 proposes syntax for binary-controlled gate execution of the form:
c-GATE condition-register -> gate-operands
e.g.
c-x r0 -> q1 means a conditional execution of X q1 depending on r0
c-Rx r0 -> q1,pi/3 means a conditional X rotation over pi/3 radians of q1 depending on r0
In this:
The "c-" prefix just indicates binary-control of any gate.
So:
c-Rx r0 -> q1,pi/3 (cQASM2.0)
c-Rx r0, q1, pi/3 (libqasm)
IF r0 Rx q1, pi/3 (as in example above)
COND r0 Rx q1, pi/3 (as used in earlier mail)
are just syntactically equivalent variants of the same.
libqasm recognizes binary-controlled gates but the arrow -> of cQASM2.0 must be written as a comma:
c-x r0, q1 c-Rx r0, q1, pi/3 c-CNOT r0, q1, q2
and libqasm only knows default gates, i.e. it is not configured from a config file
cqasm_reader doesn't support binary controlled gates
There are two issues:
In cQASM we have several options how to represent the conditional expression in a gate. These seem all appealing but the last one turns out to be easiest, engineering-wise, and so easiest to support/implement:
only and exactly an expression that is supported by CC can be represented in the conditional gate, not a general expression, i.e. the programmer knows it exactly:
{ measure q0 } # measure q0; implicitly keeps result in last[q0]
{ IF last[q0] X q0 | IF ~last[q0] Y q1 } # have X q0 and Y q1 in parallel, conditionally on last measurement
The programmer guarantees that the expression fulfills all CC constraints.
This solution requires all kinds of special casing,
while reading, keeping the result, scheduling, etc., and it probably turns out
that generalized support would take as much time as this special casing.
It is also not portable to later CC versions with different constraints.
So although this seems to make life easy, it is not.
any conditional expression can be represented in the conditional gate, i.e. a more generalized one that the one allowed by CC, or also only a classical register, i.e. less than supported by CC:
{ measure q0 } # measure q0; implicitly keeps result in last[q0]
{ SM[0] = ( ~last[q0] ) } # keep this result inverted in shared memory
{ measure q1 } # measure q1; implicitly keeps result in last[q1]
{ measure q1 } # measure q1; implicitly keeps result in last[q1], previous now in previous[q0]
{ IF ( ~last[q0] | SM[0] | prev[q1] ) Y q1 } # execute Y q1, conditionally on complicated expression
Decomposition:
- OpenQL decomposes this expression in the part done by CC (left in place in the gate)
and the part(s) done by classical logical instructions (additional instructions).
This is a CC-dependent decomposition pass.
- These parts are connected by newly allocated classical registers.
- Because measurement is a gate and because of the special support for the last and previous measurements, specials are needed to access those results.
Pre-scheduling and RC-scheduling:
- dependences on previous gates/qubits/classical registers of the CC-part and non-CC-part(s)
- timing of CC part is separate from timing of the other parts
- any constraints on CC-MUX etc.?
Because of the special casing for last and previous measurement results inside the total conditional expression, this becomes dependent on what CC supports; that is not nice.
only a single register access
{ measure q0, r0} # measure q0: r0 = meas 10
{ SM[0] = ( ~r0 ) } # keep this result inverted in shared memory
{ measure q1, r1 } # measure q1; keep in r1
{ measure q1, r2 } # measure q1; keep in r2
{ SM[2] = SM[0] | SM[1] }
{ IF ( SM[2] | r1 ) Y q1 } # execute Y q1, conditionally on primitive expression
Data-flow analysis:
- OpenQL uses the data dependence graph to trace back the data flow to the condition.
The operations in this data flow graph are the measurements and the classical logical instructions.
In this data flow graph, the CC-part is isolated and extracted into the gate;
the original operations are deleted or transformed.
This is a CC-dependent decomposition pass.
Pre-scheduling and RC-scheduling:
- as with 2.
This is the most general solution.
Because the last and previous measurement must be found, if any, those must be present in the same circuit, and not separate, reachable over a (conditional) branch.
In the end, it is also probably the easiest to implement, exactly because of the absence of special casing.
The CC-dependent decomposition pass has full control.
OpenQL needs to be updated:
Some remarks per section...
CC support:
OpenQL layers:
What's already there:
Conditional expressions:
bitcomplement r0, r1
is counterintuitive to me: it actually assigns a value to r1, while that's not what the user intends, neither is it what will happen on the CC. To me, { IF r0 X q0 | IF ~r0 Y q1 }
is way more readable;
OpenQL is currently a combination of API calls. Expressing classical construct is very counter intuitive. This can be improved.