Quick Start Guide

Framework

Building on the recent success of Julia, JuMP and mixed-integer programming (MIP) solvers, QuantumCircuitOpt (or QCOpt), is an open-source toolkit for optimal quantum circuit design. As illustrated in the figure below, QCOpt is written in Julia, a relatively new and fast dynamic programming language used for technical computing with support for extensible type system and meta-programming. At a high level, QCOpt provides an abstraction layer to achieve two primary goals:

  1. To capture user-specified inputs, such as a desired quantum computation and the available hardware gates, and build a JuMP model of an MIP formulation, and
  2. To extract, analyze and post-process the solution from the JuMP model to provide exact and approximate circuit decompositions, up to a global phase and machine precision.
../assets/QCOpt_framework.png ../assets/QCOpt_framework.png

Video tutorials

  • November 2022: Presentation link from the Third Quantum Computing Software Workshop, held in conjunction with the International Conference on Super Computing (SC22). This video will introduce the importance of nonlinear programming formulations for optimal quantum circuit design. More technical details can be found in this paper.

  • July 2022: Presentation at the JuliaCon 2022 introduces the package in greater depth and how to use it's various features.

Youtube-link
  • November 2021: Presentation from the 2nd Quantum Computing Software Workshop, held in conjunction with the International Conference on Super Computing (SC21), will introduce the technicalities underlying the package, which can also be found in this paper.
Youtube-link

Getting started

To get started, install QCOpt and JuMP, a modeling language layer for optimization. QCOpt also needs a MIP solver such as Gurobi or IBM's CPLEX. If you prefer an open-source MIP solver, install HiGHS from the Julia package manager, though be warned that the run times of QCOpt can be substantially slower using any of the open-source MIP solvers.

User inputs

QCOpt takes two types of user-defined input specifications. The first type of input contains all the necessary circuit specifications. This is given by a dictionary in Julia, which is a collection of key-value pairs, where every key is of the type String, which admits values of various types. Below is the list of allowable keys for the dictionary, given in column 1, and it's respective values with descriptions, given in column 2. This input dictionary is represented as params in all the example circuit decompositions.

Mandatory circuit specificationsDescription
num_qubitsNumber of qubits of the circuit (≥ 2).
maximum_depthMaximum allowable depth for decomposition of the circuit (≥ 2).
elementary_gatesVector of all one and two qubit elementary gates. The menagerie of quantum gates currently supported in QCOpt can be found in gates.jl.
target_gateTarget unitary gate which you wish to decompose using the above-mentioned elementary_gates.
objectiveChoose one of the following: (a) minimize_depth, which minimizes the total number of one- and two-qubit gates. For this option, include Identity matrix in the above-mentioned elementary_gates, (b) minimize_cnot, which minimizes the number of CNOT gates in the decomposition. (default: minimize_depth)
decomposition_typeChoose one of the following: (a) exact_optimal, which finds an exact, provably optimal, decomposition if it exists, (b) exact_feasible, which finds any feasible exact decomposition, but not necessarily an optimal one if it exists, (c) optimal_global_phase, which finds an optimal (best) circuit decomposition if it exists, up to a global phase (d) approximate, which finds an approximate decomposition if an exact one does not exist; otherwise it will return an exact decomposition (default: exact_optimal)

If the above-specified elementary_gates contain gates with continuous angle parameters, then the following mandarotry input angle discretizations have to be specified in addition to the above inputs:

Mandatory angle discretizationsDescription
RX_discretizationVector of discretization angles (in radians) for RXGate. Input this only if this gate is part of the above-mentioned elementary_gates.
RY_discretizationVector of discretization angles (in radians) for RYGate. Input this only if this gate is part of the above-mentioned elementary_gates.
RZ_discretizationVector of discretization angles (in radians) for RZGate. Input this only if this gate is part of the above-mentioned elementary_gates.
Phase_discretizationVector of discretization angles (in radians) for PhaseGate. Input this only if this gate is part of the above-mentioned elementary_gates.
U3_θ_discretizationVector of discretization angles (in radians) for θ parameter in U3Gate. Input this only if this gate is part of the above-mentioned elementary_gates.
U3_ϕ_discretizationVector of discretization angles (in radians) for ϕ parameter in U3Gate. Input this only if this gate is part of the above-mentioned elementary_gates.
U3_λ_discretizationVector of discretization angles (in radians) for λ parameter in U3Gate. Input this only if this gate is part of the above-mentioned elementary_gates.
CRX_discretizationVector of discretization angles (in radians) for CRXGate. Input this only if this gate is part of the above-mentioned elementary_gates.
CRY_discretizationVector of discretization angles (in radians) for CRYGate. Input this only if this gate is part of the above-mentioned elementary_gates.
CRZ_discretizationVector of discretization angles (in radians) for CRZGate. Input this only if this gate is part of the above-mentioned elementary_gates.
CU3_θ_discretizationVector of discretization angles (in radians) for θ parameter in CU3Gate. Input this only if this gate is part of the above-mentioned elementary_gates.
CU3_ϕ_discretizationVector of discretization angles (in radians) for ϕ parameter in CU3Gate. Input this only if this gate is part of the above-mentioned elementary_gates.
CU3_λ_discretizationVector of discretization angles (in radians) for λ parameter in CU3Gate. Input this only if this gate is part of the above-mentioned elementary_gates.

In addition, here is a list of optional circuit specifications, which can be added to the above set of inputs, to accelerate the performance of the QCOpt package:

Optional circuit specificationsDescription
initial_gateIntitial-condition gate to the decomposition (gate at 0th depth) (default: Identity).
set_cnot_lower_boundThis option sets a lower bound on the total number of CNot or CX gates which an optimal decomposition can admit.
set_cnot_upper_boundThis option sets an upper bound on the total number of CNot/CX gates which an optimal decomposition can admit. Note that both set_cnot_lower_bound and set_cnot_upper_bound can also be set to an identitcal value to fix the number of CNot/CX gates in the optimal decomposition.
input_circuitInput circuit representing an ensemble of elementary gates which decomposes the given target gate. This input circuit, which serves as a warm-start, can accelerate the MIP solver's search for the incumbent solution. (default: empty circuit).

Optimization model inputs

The second set of inputs for QCOpt contains all the optional specifications for the underlying optimization models. This is given by a dictionary in Julia, which is a collection of key-value pairs, where every key is of the type Symbol, which admits values of various types. Below is the list of allowable keys for this dictionary, given in column 1, and it's respective values with descriptions, given in column 2. This input dictionary is an optional one, as it's default values are already set in types.jl correspnding to an optimal performance of the QCOpt package. Further, this dictionary is an optional argument while executing functions, build_QCModel and run_QCModel only.

Optional model inputsDescription
model_typeThe type of implemented MIP model to optimize in QCOpt (default: compact_formulation).
commute_gate_constraintsThis option activates the valid constraints to eliminate pairs of commuting gates in the elementary (native) gates set (default: true)
involutory_gate_constraintsThis option activates the valid constraints to eliminate pairs of involutory gates in the elementary (native) gates set (default: true)
redundant_gate_pair_constraintsThis option activates the valid constraints to eliminate redundant pairs of gates in the elementary (native) gates set (default: true)
identity_gate_symmetry_constraintsThis option activates the valid constraints to eliminate symmetry in the Identity gate in the decomposition (default: true)
idempotent_gate_constraintsThis option activates the valid constraints to eliminate idempotent gates in the elementary (native) gates set (default: false)
convex_hull_gate_constraintsThis option activates the valid constraints to apply convex hull of complex entries in the elementary (native) gates set (default: false)
fix_unitary_variablesThis option evaluates all the fixed-valued indices of unitary matrix varaibles (U_var) at every depth, and appropriately builds the optimization model (default: true)
visualize_solutionThis option activates the visualization of the optimal circuit decomposition (default: true)
relax_integralityThis option transforms integer variables into continuous variables (default: false).
optimizer_logThis option enables or disables console logging for the optimizer (default: true).
objective_slack_penaltyThis option sets the penalty for minimizing the slack term in the objective, when decomposition_type is set to approximate (default: 1E3).
time_limitThis option allows sets the maximum time limit for the optimizer in seconds (default: 10,800).

Sample circuit synthesis

Using the above-described mandatory and optional user inputs, here is a sample circuit decomposition to minimize the total depth for implementing a 2-qubit controlled-Z gate (CZGate), with entangling CNOT gate and the one-qubit, universal rotation gate (U3Gate) with three discretized Euler angles (θ,ϕ,λ):

import QuantumCircuitOpt as QCOpt
using JuMP
using Gurobi

# Target: CZGate
function target_gate()
    return Array{Complex{Float64},2}([1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 -1]) 
end

# Circuit specifications (mandatory)
params = Dict{String, Any}(
"num_qubits" => 2, 
"maximum_depth" => 4,    
"elementary_gates" => ["U3_1", "U3_2", "CNot_1_2", "Identity"],
"target_gate" => target_gate(),
"objective" => "minimize_depth",
"decomposition_type" => "exact_optimal",
       
"U3_θ_discretization" => -π:π/2:π,
"U3_ϕ_discretization" => -π:π/2:π,
"U3_λ_discretization" => -π:π/2:π,
)

# Optimization model inputs (optional)
model_options = Dict{Symbol, Any}(:model_type => "compact_formulation",
                                  :visualize_solution => true)

qcm_optimizer = JuMP.optimizer_with_attributes(Gurobi.Optimizer, "presolve" => 1)
QCOpt.run_QCModel(params, qcm_optimizer; options = model_options)

If you prefer to decompose a target gate of your choice, update the target_gate() function and the set of elementary_gates accordingly in the above sample code. For more such circuit decompositions, with various types of elementary gates, refer to examples folder.

Warning

Note that QCOpt tries to find the global minima of a specified objective function for a given set of input one- and two-qubit gates, target gate and the total depth of the decomposition. This combinatiorial optimization problem is known to be NP-hard to compute in the size of num_qubits, maximum_depth and elementary_gates.

Tip

Run times of QCOpt's mathematical optimization models are significantly faster using Gurobi as the underlying mixed-integer programming (MIP) solver. Note that this solver's individual-usage license is available free for academic purposes.

Extracting results

The run commands (for example, run_QCModel) in QCOpt return detailed results in the form of a dictionary. This dictionary can be saved for further processing as follows,

results = QCOpt.run_QCModel(params, qcm_optimizer)

For example, for decomposing the above controlled-Z gate, the QCOpt's runtime and the optimal objective value (minimum depth) can be accessed using,

results["solve_time"]
results["objective"]

Also, results["solution"] contains detailed information about the solution produced by the optimization model, which can be utilized for further analysis.

Visualizing results

QCOpt currently supports the visualization of optimal circuit decompositions obtained from the results dictionary (from above), which can be executed using,

data = QCOpt.get_data(params)
QCOpt.visualize_solution(results, data)

For example, for the above controlled-Z gate decomposition, the processed output of QCOpt is as follows:

=============================================================================
QuantumCircuitOpt version: v0.5.1

Quantum Circuit Model Data
  Number of qubits: 2
  Total number of elementary gates (after presolve): 72
  Maximum depth of decomposition: 4
  Elementary gates: ["U3_1", "U3_2", "CNot_1_2", "Identity"]
    U3_θ discretization: [-180.0, -90.0, 0.0, 90.0, 180.0]
    U3_ϕ discretization: [-180.0, -90.0, 0.0, 90.0, 180.0]
    U3_λ discretization: [-180.0, -90.0, 0.0, 90.0, 180.0]
  Type of decomposition: exact_optimal
  MIP optimizer: Gurobi

Optimal Circuit Decomposition
  U3_2(-90.0,0.0,0.0) * CNot_1_2 * U3_2(90.0,0.0,0.0) = Target gate
  Minimum optimal depth: 3
  Optimizer run time: 3.01 sec.
=============================================================================