0
$\begingroup$

Question: how to dynamically assign qubits to variables?

e.g., say I want to represent a matrix of qubits:

$matrix := \{\vec{v_1}, \vec{v_2}, ..., \vec{v_r}\}$

where each vector consists of $dim$ many coefficients of each $bitlength$ many qubits.

(To the best of my knowledge) Q# only allows to initiate one-dimensional Qubit arrays:

using ( allQubits = Qubit[rank * dim * bitlength] ) {
    // Some Code
}

Which leaves us with the task to assign a partition of that qubit array to variables.

I see three ways of doing that, that first seems very messy, the other two I did not get to work:

  1. Remembering indices in the qubit array (this will be messy)
  2. Getting a representation of a three dimensional qubits array: $Qubit[rank][dim][bitlength]$
  3. Getting a custom type of QubitVectors and getting an array of those.

Any solution is welcome, does not have to follow any of these approaches.


Lets start with a $Qubit[rank][dim][bitl]$ array. Using the Partitioned function, one can split the allQubits array into smaller parts, e.g., the qubits that represent a single vector.

// Splits a given qubit array into chunks of length size
operation splitQubitArray( allQubits : Qubit[], chunkSize : Int ) : Qubit[][]
{   
    Fact(Length(allQubits) % chunkSize == 0, "Qubit array can not be splitted into equally sized chunks");
    Fact(Length(allQubits) / chunkSize > 0, "Qubit array length is smaller than chunk size.");
    let numChunks = Length(allQubits) / chunkSize;
    mutable arraySplit = new Int[numChunks];
    for (i in 0..(numChunks - 1))
    {
        set arraySplit w/= i <- chunkSize;
    }
    let res = Partitioned(arraySplit, allQubits);
    Fact(Length(res[numChunks]) == 0, "Qubit array not split equally.");
    return res;
}

// Split AllQubits into rank many parts.
// Each part contains dim * bitl many qubits.
mutable allVecQubits = splitQubitArray( allQubits, dim * bitl );

Now we have a two dimensional array, where each entry of the first dimension represents a vector. One can split these further to get out a single vector variable, but I could not figure out how to put this into a iterable array...

mutable vec_1 = splitQubitArray(allVecQubits[0], rank);
mutable vec_2 = splitQubitArray(allVecQubits[1], rank);
...

Option 3, the type model: we define a type that contains a vector.

newtype customVec = (Data : Qubit[][]);

This leaves me with the problem that I am not able to initialize an array of that type...

mutable qbVectorArray = customVec[0]; // this does not work
mutable qbVectorArray = new customVec[0]; // this does not work
mutable qbVectorArray = customVec(new Qubit[][]); // this does not work

As a note on the side: I tried to consule the documentation in copy-and-update expressions but could not figure out how to do this, if the custom type contains Qubits. Furthermore the webpage containing the "update-and-reassign statements" for Q# Arrays which is linked in the type model page seems to be unavailable. An example or guide on how to deal with custom types with Qubits would be very much appreciated.

$\endgroup$
2
$\begingroup$

One important thing to note is that arrays of Qubit aren't special in Q#, such that a solution that allows for splitting an array of type 'T[] into 'T[][][] will also work for arrays of qubits such as what you get from a using statement.

From that perspective, your solution is almost there already; you just need to generalize splitQubitArray into a function that takes 'T instead, then you can call that with 'T being Qubit[] instead of Qubit. Turning your operation into a function and adding a type parameter, we get the following:

open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Diagnostics;

function Chunked<'T>(array : 'T[], chunkSize : Int) : 'T[][] {
    Fact(Length(array) % chunkSize == 0, "Array can not be splitted into equally sized chunks");
    Fact(Length(array) / chunkSize > 0, "Array length is smaller than chunk size.");
    let numChunks = Length(array) / chunkSize;
    let res = Partitioned(ConstantArray(numChunks, chunkSize), array);
    Fact(Length(res[numChunks]) == 0, "Qubit array not split equally.");
    // Note that since Parititoned always returns
    // one more array than the length of the array
    // of chunk sizes, chop off the last element of its result.
    return Most(res);
}

This can then be used twice to turn an array of type 'T[] into an array of type 'T[][][] for any element type 'T[]. For example, we can chunk an array of type Int[] into a rank-three array of integers:

open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Convert;

function Example() : Unit {
    let rank3 = Chunked(Chunked(RangeAsIntArray(1.. 5 * 2 * 3), 2), 3);
    Message($"Rank-3 array: {rank3}");
    Message($"Length(rank3) = {Length(rank3)}");
    Message($"Length(rank3[0]) = {Length(rank3[0])}");
    Message($"Length(rank3[0][0]) = {Length(rank3[0][0])}");
}

This gives us as our output:

Rank-3 array: [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]],[[13,14],[15,16],[17,18]],[[19,20],[21,22],[23,24]],[[25,26],[27,28],[29,30]]]
Length(rank3) = 5
Length(rank3[0]) = 3
Length(rank3[0][0]) = 2

In your example above, then, you could use this new function to make a rank-3 array of qubits:

let qubitsAsRank3Array = Chunked(Chunked(allVecQubits), dim), bitlength);
let vec1 = qubitsAsRank3Array[0];
let vec1 = qubitsAsRank3Array[1];
// ...

This example can be run online at: https://mybinder.org/v2/gist/cgranade/e4a837dcb37a4e60f8e971cf1372a2ef/master

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.