Using the QIR Tracer with a Q# project #865
Description
After working on #864, I was able to compile and analyze the database-search sample project using the QIR Tracer runtime on a Mac laptop.
Since QIR's Tracer is still not incorporated into Q#'s build framework or the internal qir-tool
, there are a few more manual steps need to achieve this. These are my notes.
Pre-reqs
Same set=up as #864: checked out the repos qsharp-runtime and Quantum (i.e. samples) under a ~/Repos
folder. Then installed:
- powershell 7+ (brew install --cask powershell): needed to run all build scripts
- cmake 3.20+ (brew install cmake): all our native projects are based on cmake for x-platform support
- clang (xcode-select --install): needed to compile native code
- dotnet: needed by the Q# compiler and qir-cli tool.
Note: there might be other dependencies that were already installed on my environment. Also,
bootstrap.ps1
installs Rust automatically (used for the experimental simulator), and might install other things.
Build QIR Runtime components
Run bootstrap.ps1 under root to build Native simulators and basic Qir runtime
cd ~/Repos/qsharp-rutnime
pwhs ./bootstrap.ps1
Build rest of QIR:
cd ~/Repos/qsharp-runtime/src/Qir
pwsh ./build_all.ps1
Note: The database-search project calls
Measure
for the output, which currently triggers an Exception on Tracer.
I modified the code and recompile to simply returnZero
.
1. Generate QIR from Q# project
We can't use the OOTB QIR generation for the Tracer library. Tracer requires its own set of intrinsic operations and all regular intrinsic operations need to be decomposed accordingly. At a high level, all operations we wish to track need to make a call to either single_qubit_op
or multi_qubit_op
, everything needs to be decomposed into these two.
A Q# file with such decompositions can be found at qsharp-runtime/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs
.
To incorporate this into your project, download and copy it to your project's src
folder, e.g.:
cd ~/Repos/Quantum/samples/algorithms/database-search
cp ~/Repos/qsharp-runtime/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs .
However, trying to compile this with dotnet build /property:QirGeneration=true
triggers errors as the intrinsics are duplicated.
To fix this we need to remove the built-in intrinsics library when calling the Q# compiler.
There are a couple of ways to do this, what I did is I modify the resource file generated for the
QirGeneration=true
the parameter (found in obj/qsharp/config/qsc.rsp
) and remove the Microsoft.Quantum.QSharp.Core.dll
from it, then call directly the Q# compiler using this file:
dotnet ~/.nuget/packages/microsoft.quantum.sdk/0.19.2109165653/DefaultItems/../tools/qsc/qsc.dll build --format MsBuild -v Normal --response-files obj/qsharp/config/qsc.rsp
Note the existing
tracer-target.qs
is missing a couple of intrinsics:
R1
Reset
ResetAll
MResetZ
which I manually added to compile successfully.
This generates two versions of the QIR code:
qir/DatabaseSearchSample.ll
: with the text versionobj/qsharp/DatabaseSearchSample.bc
: with the binary version
Compile QIR
It's very simple:
c++ -c qir/DatabaseSearchSample.ll -o output/qir.o
Generate & compile driver
The generated QIR code does not have a built-in main
function. A driver
program that initializes the Tracer and invokes the operation is needed. The
driver also needs a mapping from operation ids to names.
I chose to modify the driver auto-generated by the qir-tool
. The new file can be found in this gist:
tracer-driver.cpp
Download the file into the database-search
folder and compile it:
wget https://gist.githubusercontent.com/anpaz/893fec94a6d58b0bfc8c12267c7f0a7a/raw/e00872d0d7904b8174f17c807e1d1efccb73098c/tracer-driver.cpp -O tracer-driver.cpp
c++ -c tracer-driver.cpp -o output/tracer-driver.o -std=gnu++17 -I ~/Repos/qsharp-runtime/src/Qir/Runtime/public
Another required step is to make the runtime QIR libraries discoverable for the linker and at runtime.
To achieve this, create a lib
folder, and copy all the dlls from the Qir bin output folder. Then set
mkdir lib
find ~/Repos/qsharp-runtime/src/Qir/Runtime/bin/Debug/bin -name "*.dylib" -exec cp "{}" lib \;
export LIBRARY_PATH=$LIBRARY_PATH:lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:lib
Now we can link everything to generate an executble a.out
:
c++ output/qir.o output/tracer-driver.o \
-lMicrosoft.Quantum.Qir.Runtime \
-lMicrosoft.Quantum.Qir.QSharp.Foundation \
-lMicrosoft.Quantum.Qir.QSharp.Core \
-lMicrosoft.Quantum.Qir.Tracer
This creates an a.out
file. To trace the Q# operation, run the program, the output should be something like this:
$ ./a.out
Quantum search for marked element in database.
Database size: 64.
Classical success probability: 0.015625
Queries per search: 7
Quantum success probability: 0.59138015005737521
Attempt 10. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 20. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 30. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 40. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 50. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 60. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 70. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 80. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 90. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
Attempt 100. Success: Zero, Probability: 1.0 Speedup: 9.14300000000000068 Found database index [Zero, Zero, Zero, Zero, Zero, Zero]
layer_id,name,X,MCX,MCZ,H,Rx,100
0,,0,0,0,600,0,0
1,,0,100,0,0,0,0
2,,0,0,0,0,100,0
3,,0,100,0,0,0,0
4,,100,0,0,600,0,0
5,,600,0,0,0,0,0
6,,0,0,100,0,0,0
7,,700,0,0,0,0,0
8,,0,0,0,600,0,0
9,,0,100,0,0,0,0
10,,0,0,0,0,100,0
11,,0,100,0,0,0,0
12,,100,0,0,600,0,0
13,,600,0,0,0,0,0
14,,0,0,100,0,0,0
15,,700,0,0,0,0,0
16,,0,0,0,600,0,0
17,,0,100,0,0,0,0
18,,0,0,0,0,100,0
19,,0,100,0,0,0,0
20,,100,0,0,600,0,0
21,,600,0,0,0,0,0
22,,0,0,100,0,0,0
23,,700,0,0,0,0,0
24,,0,0,0,600,0,0
25,,0,100,0,0,0,0
26,,0,0,0,0,0,700