Skip to content

jlapeyre/Qurt.jl

Repository files navigation

Qurt

Build Status Aqua QA JET QA Coverage

Purpose of this repo

This package provides facilities for representing and manipulating quantum circuits in Julia. A subset of the functionality of Qiskit is implemented. This package is meant to be highly compatible with qiskit, in the sense that one can call the other to do circuit tranformations. But it is not a reimplementation of Qiskit circuits in Julia.

Here are some notes on design considerations. The notes are somewhat out of date, as my understanding has evolved since I have been implementing Qurt.jl.

Installing Julia

One of the easiest ways for a Python user to install Julia is

> pip install jill
> jill install

The Julia community is pushing the juliaup installer, written in Rust, because it doesn't require an installed runtime (Python). You may try juliaup if jill.py doesn't work for you (There is another installer jill.sh, but I don't recommend it as a first choice.)

Before you do anything else, start Julia and add two packages like this (this is one way to use the package manager)

julia> import Pkg
julia> Pkg.add("Revise")
julia> Pkg.add("BenchmarkTools")

Revise.jl is an absolutely essential tool for developing. Do using Revise before loading code that you are working on.

How to install

The Julia general registry is similar to the pypi that you access via pip. The general registry is somewhat more gate-kept. Qurt.jl and a few of its dependencies are not in the general registry. However they are in a small registry that you can install like this. Start julia at the cli and press ] to enter package manager mode.

julia> # hit `]`
(pkg)> registry add https://github.com/jlapeyre/LapeyreRegistry
(pkg)> # hit backspace to return to the julia prompt

Installing Qurt if you don't want to edit (or develop) it

Assuming you have installed this small registry. Then you can install Qurt like this

julia> # hit `]`
(pkg)> add Qurt

Installing Qurt if you do want to edit it

If you want to edit or play with Qurt, do install the registry mentioned above. Then clone this repo. From the top level do the following

julia> import Pkg; Pkg.activate(".")

Or do this

julia> # hit `]`
(pkg)> activate .
(Qurt)>

Or do this

shell> julia --project="."

Once you've installed Qurt you should be able to load it like this

julia> using Qurt

How to use

The test suite is a good source of examples. At the time I am writing this sentence the test suite passes. You can run this with Pkg.test().

The online documentation is a somewhat organized (by Documenter.jl) dump of docstrings.

Here is a notebook. The emphasis is on exposing the design.

Development environment

This is optional, but easy to do. I put my development environment in this repo. These are lightweight to instantiate. In Julia, downloaded packages go in one place, your depot. Environments don't copy packages from there. They instead maintain Manifest.toml which points to the packages in the depot.

Clone this repo. In a terminal, change from the top level to the ./devel directory and start Julia with the project defined in that directory activated.

shell> cd ./devel
shell> julia --project="."
julia> using Pkg
julia> Pkg.instantiate() # Create Manifest.toml which creates a tree of dependencies and their locations.

Note that there are other ways to activate a project: Within Julia you can do using Pkg; Pkg.activate("."). By default, the version of Qurt in the repository (which points to github) will be used by the project in ./devel. To use the cloned version, which you can edit, do this, assuming your current directory is ./devel.

julia> using Pkg
julia> Pkg.develop(path=abspath("..")) # abspath makes Manifest.toml relocatable, a slight convenience

You can load Qurt like this.

julia> using Revise # edits are tracked live for all subsequently loaded packages
julia> using Qurt

Python and Qiskit

Python is a weak (i.e. optional dependency) at the moment. We assume that you have a Python environment with Qiskit and other desired packages installed. I keep a Python virtual environment under the top level directory of Qurt. This is not committed to the repository. This might work as follows, but you can also use your development installation of qiskit.

shell> mkdir ./.venvs
shell> python -m venv .venvs/env-3.11 # or whatever version you prefer
shell> source ./.venvs/env-3.11/bin/activate
shell> pip install qiskit-terra # or other qiskit components
shell> cd ./devel
shell> julia --project="."
julia> include("usepkgs.jl") # Load both `Qurt` and optional Python dependency

See comments and docs in ./devel/usepkgs.jl and other files in that directory for more information. For convenience, you can load more symbols into the Main Julia namespace like this

julia> include("import.jl")

Using the Python extension and Qiskit, you can do this

using Qurt.Elements: H, CX, Measure
qc = Circuit(2, 2)
@build qc H(1) CX(1, 2) Measure(1, 2; 3, 4)
draw(qc)
     ┌───┐     ┌─┐
q_0: ┤ H ├──■──┤M├───
     └───┘┌─┴─┐└╥┘┌─┐
q_1: ─────┤ X ├─╫─┤M├
          └───┘ ║ └╥┘
c: 2/═══════════╩══╩═
                0  1

Other Julia packages

You can add and remove packages from this development environment like this

julia> using Pkg; # in case it's not already loaded
julia> Pkg.add("PackageName")
julia> Pkg.rm("OtherPackageName")

As mentioned above these environments are lightweight because they don't copy from your package cache (called a "depot"), but rather point to the cache. In addition, they are relocatable. For example, changing the directory name won't invalidate them, although any explicit hardcoded links that you did with Pkg.develop(path="path/to/package") need to be repeated unless they used an absolute path.

Examples

Tallying occurrences of things on nodes is pretty fast.

julia> using Qurt
julia> using Qurt.Circuits
julia> using Qurt.Elements
julia> using Qurt.Interface

julia> num_gates = 10^6
1000000

julia> qc = Circuit(10)
circuit {nq=10, ncl=0, nv=20, ne=10} Qurt.Nodes.NodeStructOfVec Int64

julia> foreach(((gate, wire),) -> add_node!(qc, gate, (wire,)),
                 zip(rand(X:SX, num_gates), rand(1:10, num_gates)));

julia> @btime count_ops($qc)
  7.693 ms (24 allocations: 1.44 KiB)
7-element Dictionaries.Dictionary{Element, Int64}
  Input │ 10
 Output │ 10
      Y │ 200119
      X │ 200011
      H │ 200810
      Z │ 199387
     SX │ 199673

What the DAG looks like:

julia> using Qurt
julia> using Qurt.Circuits
julia> using Qurt.IOQDAGs
julia> using Qurt.Elements

julia> qc = Circuit(2, 2)
circuit {nq=2, ncl=2, nv=8, ne=4} Int64 NodeStructOfVec

julia> qc = Circuit(2, 2); print_edges(qc)
    1 => 3  Input (1,) => Output (1,)
    2 => 4  Input (2,) => Output (2,)
    5 => 7  ClInput (3,) => ClOutput (3,)
    6 => 8  ClInput (4,) => ClOutput (4,)

julia> add_node!(qc, H, (1,)); print_edges(qc)
    1 => 9  Input (1,) => H (1,)
    2 => 4  Input (2,) => Output (2,)
    5 => 7  ClInput (3,) => ClOutput (3,)
    6 => 8  ClInput (4,) => ClOutput (4,)
    9 => 3  H (1,) => Output (1,)

julia> add_node!(qc, CX, (1, 2)); print_edges(qc)
    1 => 9  Input (1,) => H (1,)
    2 => 10  Input (2,) => CX (1, 2)
    5 => 7  ClInput (3,) => ClOutput (3,)
    6 => 8  ClInput (4,) => ClOutput (4,)
    9 => 10  H (1,) => CX (1, 2)
    10 => 3  CX (1, 2) => Output (1,)
    10 => 4  CX (1, 2) => Output (2,)

julia> add_node!(qc, Measure, (1, 2), (3, 4)); print_edges(qc)
    1 => 9  Input (1,) => H (1,)
    2 => 10  Input (2,) => CX (1, 2)
    5 => 11  ClInput (3,) => Measure ((1, 2), (3, 4))
    6 => 11  ClInput (4,) => Measure ((1, 2), (3, 4))
    9 => 10  H (1,) => CX (1, 2)
(2) 10 => 11  CX (1, 2) => Measure ((1, 2), (3, 4))
    11 => 3  Measure ((1, 2), (3, 4)) => Output (1,)
    11 => 4  Measure ((1, 2), (3, 4)) => Output (2,)
    11 => 7  Measure ((1, 2), (3, 4)) => ClOutput (3,)
    11 => 8  Measure ((1, 2), (3, 4)) => ClOutput (4,)

About

Qiskit compatible circuit manipulation in Julia

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors