The value of any classic variable can be set by the assignment operator =. The right-hand value must be of the same type as the variable. In contrast to arithmetic operators and built-in functions, no implicit typecasting is performed.
qcl> complex z; qcl> z=pi; // no typecast ! type mismatch: invalid assignment qcl> z=conj(pi); // implicit typecast |
qcl> qureg q[2]; qcl> quref p=q[0]; qcl> p=q[1]; ! invalid type: assignment to quantum variable |
The routine types procedure, operator and qufunct can be called.
qcl> list CNot; // The controlled-not operator takes : global symbol CNot: // a qureg and a quconst as arguments extern qufunct CNot(qureg q,quconst c); qcl> qureg q[2]; qcl> quconst p[2]; qcl> CNot(q[0],q[1]); // cast from qureg to quconst qcl> CNot(p[0],p[1]); // no cast from quconst to qureg ! parameter mismatch: quconst used as non-const argument to CNot |
Calls to the operator types operator and qufunct can be inverted with the adjungation prefix `!'. The operator is then normally executed, except that all suboperators within the operator definition are not immediately invoked, but stored in an internal list together with their evaluated parameters.
When the execution is finished, the suboperators are called in reverse order with their adjungation flags inverted.
qcl> <<dft qcl> qureg q[2]; // allocate 2 qubits qcl> set log 1; // turn on operator logging qcl> dft(q); // perform discrete Fourier transform @ Rot(real theta=1.570796,qureg q=|..0.>) @ CPhase(real phi=1.570796,qureg q=|..10>) @ Rot(real theta=1.570796,qureg q=|...0>) @ Swap(qureg a=|...0>,qureg b=|..0.>) [2/4] 0.5 |0000> + -0.5 |0010> + -0.5 |0001> + 0.5 |0011> qcl> !dft(q); // inverse Fourier transform @ !Swap(qureg a=|...0>,qureg b=|..0.>) @ !Rot(real theta=1.570796,qureg q=|...0>) @ !CPhase(real phi=1.570796,qureg q=|..10>) @ !Rot(real theta=1.570796,qureg q=|..0.>) [2/4] 1 |0000> |
qcl> real n; qcl> input "Enter Number of iterations:",n; ? Enter Number of iterations: 1000 |
qcl> boolean b; qcl> input b; ? boolean b [t(rue)/f(alse)] ? yes ? boolean b [t(rue)/f(alse)] ? true |
The print command takes a comma separated list of expressions and prints them to the console. Each output is prepended by a colon and terminated with newline. Multiple expressions are delimited by space. In the case of quantum expressions, the position of the corresponding qubits in the machine-state is printed.
qcl> int i=3; real x=pi; complex z=(0,1); boolean b; qureg q[8]; qcl> print i,x,z,b,q; : 3 3.141593 (0.000000,1.000000) false |........76543210> |
The commands shell and exit open and close subshells during interactive use. Please refer to section 2.1.3.3 and section 2.2.4.4 for a detailed description.
qcl> <<dft qcl> list flip,pi; : global symbol flip: qufunct flip(qureg q) { int i; for i = 0 to #q/2-1 { Swap(q[i],q[(#q-i)-1]); } } : global symbol pi = 3.141593: const pi = 3.141593; |
The operators Fanout and Swap play a major
role in QC as the moral equivalent to the elementary
mov operation in conventional microprocessors.
(2.8) | |||
(2.9) |
extern qufunct Fanout(qureg a,qureg b); extern qufunct Swap(qureg a,qureg b); |
For convenience, QCL provides some syntactic sugar for calls to Fanout and Swap, which can be used instead of the standard syntax:
As pointed out in section 1.1.1, any quantum computation must be composition of initialisations, unitary operators and measurements. A typical probabilistic quantum algorithm usually runs an evaluation loop like this:
{ reset; // R: |Psi> -> |0> myoperator(q); // U: |0> -> |Psi'> measure q,m; // M: |Psi'> -> |m> } until ok(m); // picked the right m ? |
The reset command resets the machine-state to , which is also the initial state when qcl is started. The quantum heap and the binding of quantum variables are unaffected.
The outcome of the measurement is determined by a random number generator, which - by default - is initialised with the current system time. For reproducable behaviour of the simulation, a seed value can be given with the option -seed.
Since reset and measure operations are irreversible, they must not occur within operator definitions.
QCL provides several commands to directly access the simulated machine state. Since this would be impossible when using a real quantum computer, they should be regarded as a non-standard extention to the QCL language.
qcl> qureg q[2]; qcl> Mix(q); qcl> dump; : STATE: 2 / 4 qubits allocated, 2 / 4 qubits free 0.5 |0000> + 0.5 |0010> + 0.5 |0001> + 0.5 |0011> qcl> dump q; : SPECTRUM q: |..10> 0.25 |00> + 0.25 |01> + 0.25 |10> + 0.25 |11> |
qcl> set dump-format "x"; qcl> dump; : STATE: 2 / 4 qubits allocated, 2 / 4 qubits free 0.5 |0x0> + 0.5 |0x2> + 0.5 |0x1> + 0.5 |0x3> |
The current machine-state can be loaded and saved with the load and save command. State files have the extention .qst. If no filename is given, the default file qclstate.qst is used.
All flow control statements operate on blocks of code. A block is a list of statements enclosed in braces:
The if and if-else statements allow for the conditional execution of blocks, depending on the value of a boolean expression.
for-loops take a counter of type integer2.5which is incremented from to . The loop body is executed for each value of .
Inside the body, the counter is treated as a constant. The increment is or if unspecified. If the loop isn't executed at all.
qcl> int i; qcl> for i=10 to 2 step -2 { print i^2; } : 100 : 64 : 36 : 16 : 4 qcl> for i=1 to 10 { i=i^2; } // i is constant in body ! unknown symbol: Unknown variable i |
QCL supports two types of conditional loops:
A while-loop is iterated as long as a the condition is satisfied. When evaluates to false, the loop terminates.
An until-loop is executed at least once and iterated until the condition is satisfied.
User defined routines often require their parameters to match certain conditions (e.g. sizes of quantum register arguments). Abnormal termination of subroutines can be forced with the exit statement.
$ qcl -n -i swap.qcl qcl> qureg q[2]; qcl> qureg p[1]; qcl> Swap(q,p); ! in qufunct Swap: user error: Swap: unmatching register sizes |