Library for Q# implementing various qRAM proposals
This library implements a variety of different proposals for memory for quantum computers, also commonly called qRAM.
Want to learn more about what qRAM is? Check out the primer on memory for quantum computers in our docs!
There are many different proposals for qRAM in quantum computing that each have different tradeoffs, and currently come up a lot in quantum machine learning applications. We want to better understand the costs and benefits of different qRAM implementations in quantum machine learning as well as quantum computing more generally. This library will help achieve these goals by giving us a concrete way to measure the resources each approach takes; choosing to do this in Q# allows us to leverage the built-in resource estimator to quickly iterate profiling the qRAM implementations and optimizing the circuits.
Do I need a qRAM?
Sometimes. You'll need a qRAM, or some more general means of quantum state preparation in quantum machine learning (QML) algorithms that require you to load in classical data, or query an oracle that returns classical data. I've heard a number of stories of people working on QML being actively discouraged from doing so because ``QML won't work without a qRAM''. That's just not true, because many QML algorithms do not need a qRAM. Now, whether or not they yield any quantum advantage is a separate question, and won't be discussed here. The key point is that some QML algorithms need a qRAM, and they will potentially run into trouble as per the next question.
Can we design an efficient qRAM?
Maybe. In the primer we'll take a look at proposals that will in principle run in polynomial depth, and others that scale far worse. There are some very interesting qubit-time tradeoffs one can explore, in particular if the data being stored has some sort of underlying structure. Regardless, even if we can design an efficient circuit, we'd also like something that is efficient in a fault-tolerant setting, and this is potentially very expensive.
Can I build one?
Maybe. No one has actually done so, but there are a handful of hardware proposals that will be discussed in more detail in the hardware section of the primer on memory for quantum computers.
Example of a Bucket Brigade qRAM circuit: TODO: Q# notebook screenshots/gif TODO: Source in VS Code/gif
Built with:
This library implements a variety of different approaches for qRAM and qROM, including:
Bucket Brigade qRAM: A read/write style memory where specific qubits are set aside to hold the data in the memory. The information can be queried so that the data returned is either bit encoded or phase encoded.
qROM: A read-only style memory that creates a fixed operation that given an address and a target, encodes the data at that address on the target. qROMs are like quantum lookup tables.
SELECT-SWAP qROM: A read-only style memory similar to the basic qROM but that has multiplexing optimizations that can help you adjust your program resources.
It is important to us that we come up with an extensible framework for implementing as many qRAM/qROM implementation as possible so we can have a uniform way to evaluate and compare these proposals. We also include a sample for doing resource estimation (and not actually simulating) so that you can get an idea of what the resources are needed to run your memory.
To validate the qRAM/qROM implementations in this library, this library includes unit tests for small memories that can be simulated classically.
This library is highly portable, and can easily be added to any Q# project with just a package include in your project file! Check out the instructions below for adding the qRAM library to your project.
operation QromQuerySample(queryAddress : Int) : Int {
// Generate a (Int, Bool[]) array of data.
let data = GenerateMemoryData();
// Create the QRAM.
let memory = QromOracle(data::DataSet);
// Measure and return the data value stored at `queryAddress`.
return QueryAndMeasureQROM(memory, queryAddress);
}
See Qrom sample for the rest of this implementation!
operation GroverSearch(addressSize : Int, markedElements : Int[]) : Int {
// First, set up a qRAM with marked elements set to 1.
let nMarkedElements = Length(markedElements);
mutable groverMemoryContents = Mapped<Int,MemoryCell>(MemoryCell(_, [false]), RangeAsIntArray(0..2^addressSize - 1));
// Set the data value to true for each marked address.
for (markedElement in markedElements) {
set groverMemoryContents w/= markedElement <- MemoryCell(markedElement, [true]);
}
using ((groverQubits, targetQubit, flatMemoryRegister) =
(Qubit[addressSize], Qubit[1], Qubit[2^addressSize])
) {
// Create a structured register to make indexing through the memory easier.
let memoryRegister = PartitionMemoryRegister(
flatMemoryRegister,
GeneratedMemoryBank(groverMemoryContents)
);
// Prepare the memory register with the initial data.
let memory = BucketBrigadeQRAMOracle(groverMemoryContents, memoryRegister);
// Initialize a uniform superposition over all possible inputs.
PrepareUniform(groverQubits);
// Grover iterations - the reflection about the marked element is implemented
// as a QRAM phase query. Only the memory cells storing a 1 will produce a phase.
for (idxIteration in 0..NIterations(nMarkedElements, addressSize) - 1) {
memory::QueryPhase(AddressRegister(groverQubits), memoryRegister, targetQubit);
ReflectAboutUniform(groverQubits);
// It's necessary to remove phase since QueryPhase only sets phase
// on the specific address instead of inverting like traditional Grover's.
ApplyToEach(Z, targetQubit);
ResetAll(targetQubit);
}
ResetAll(flatMemoryRegister);
// Measure and return the answer.
ResetAll(targetQubit);
return MeasureInteger(LittleEndian(groverQubits));
}
}
See the Grover sample for the rest of this implementation!
TODO: #37
Anywhere you can use Q#, you can use this library!
For complete and up-to-date ways to install the Quantum Development Kit (including Q# tooling) see the official Q# docs.
For convenience, in this repo we include the following ways to make it easier to use this project.
Remote-Containers: Reopen in Container
and your editor will re-launch in a local docker container that will be properly configured to use the project.TODO: See #27
The tests for this library all live in the tests
directory.
To run the tests, navigate to that directory and run dotnet test
and the .NET project will find and run all the tests in this directory.
If you are adding new features or functionality, make sure to add some tests to either the existing files, or make a new one.
For more info on writing tests for Q#, check out the official Q# docs.
TODO: more detail on nuget.org stuff once published there.
For more information on adding packages to Q# projects (the instructions are the same as for C# packages) check out the official docs.
The basic idea in this case is build locally a version of the nuget packages and then put it in a folder locally that is a known source to nuget.
> rm C:\Users\skais\nuget-packages\QSharpCommunity.Libraries.Qram.X.X.X.nupkg
> rm C:\Users\skais\.nuget\packages\QSharpCommunity.Libraries.Qram\
> cd src
> dotnet pack
X
in the name are the place holder for the version number you are building (should be generated by the previous step).> cp .\bin\Debug\QSharpCommunity.Libraries.Qram.X.X.X.nupkg 'C:\Users\skais\nuget-packages\'
Please see our contributing guidelines and our code of conduct before working on a contribution, thanks!
MIT © qsharp-community