Extensions¶
Here we detail concrete implementations of various XACC interfaces as well as any input parameters they expose.
Compilers¶
xasm¶
The XASM Compiler is the default compiler in XACC. It is the closest language to the underlying XACC IR data model. The XASM compiler provides a quantum assembly like language with support for custom Instructions and Composite Instruction generators as part of the language. Instructions are provided to the language via the usual XACC service registry. Most common digital gates are provided by default, and it is straightforward to add new Instructions.
quilc¶
XACC provides a Compiler implementation that delegates to the Rigetti-developed
quilc compiler. This is acheieved through the rigetti/quilc
Docker image and
the internal XACC REST client API. The Quilc Compiler implementation makes direct
REST POSTs and GETs to the users local Docker Engine. Therefore,
in order to use this Compiler, you must pull down this image.
$ docker pull rigetti/quilc
With that image pulled, you can now use the Quilc compiler via the usual XACC API calls.
auto compiler = xacc::getCompiler("quilc");
auto ir = compiler->compile(R"##(H 0
CNOT 0 1
)##");
std::cout << ir->getComposites()[0]->toString() << "\n";
or in Python
compiler = xacc.getCompiler('quilc')
ir = compiler.compile('''__qpu__ void ansatz(qbit q, double x) {
X 0
CNOT 1 0
RY(x) 1
}''')
Note that you can structure your input to the compiler as a typical XACC kernel source string or as a raw Quil string.
Optimizers¶
XACC provides implementations for the Optimizer
that delegate to NLOpt and MLPack. Here we demonstrate
the various ways to configure these optimizers for a number of different solver types.
In addition to the enumerated parameters below, all Optimizers
expose an initial-parameters
key
that must be a list or vector of doubles with size equal to the number of parameters. By default, [0.,0.,...,0.,0.]
is used.
MLPack¶
|
Optimizer Parameter |
Parameter Description |
default |
type |
---|---|---|---|---|
adam |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-beta1 |
Exponential decay rate for the first moment estimates. |
.7 |
double |
|
mlpack-beta2 |
Exponential decay rate for the weighted infinity norm estimates. |
.999 |
double |
|
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
mlpack-eps |
Value used to initialize the mean squared gradient parameter. |
1e-8 |
double |
|
l-bfgs |
None |
|||
adagrad |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
mlpack-eps |
Value used to initialize the mean squared gradient parameter. |
1e-8 |
double |
|
adadelta |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
mlpack-eps |
Value used to initialize the mean squared gradient parameter. |
1e-8 |
double |
|
mlpack-rho |
Smoothing constant. |
.95 |
double |
|
cmaes |
mlpack-cmaes-lambda |
The population size. |
0 |
int |
mlpack-cmaes-upper-bound |
Upper bound of decision variables. |
duoble |
||
mlpack-cmaes-lower-bound |
Lower bound of decision variables. |
-10.0 |
double |
|
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
gd |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
momentum-sgd |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
mlpack-momentum |
Maximum absolute tolerance to terminate algorithm. |
.05 |
double |
|
momentum-nesterov |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
mlpack-momentum |
Maximum absolute tolerance to terminate algorithm. |
.05 |
double |
|
sgd |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
rms-prop |
mlpack-step-size |
Step size for each iteration. |
.5 |
double |
mlpack-max-iter |
Maximum number of iterations allowed |
500000 |
int |
|
mlpack-tolerance |
Maximum absolute tolerance to terminate algorithm. |
1e-4 |
double |
|
mlpack-alpha |
Smoothing constant |
.99 |
double |
|
mlpack-eps |
Value used to initialize the mean squared gradient parameter. |
1e-8 |
double |
Various examples of using the mlpack optimizer:
// sgd with defaults
auto optimizer = xacc::getOptimizer("mlpack", {std::make_pair("mlpack-optimizer", "sgd")});
// default adam
optimizer = xacc::getOptimizer("mlpack")
// adagrad with 30 max iters and .01 step size
auto optimizer = xacc::getOptimizer("mlpack", {std::make_pair("mlpack-optimizer", "adagrad"),
std::make_pair("mlpack-step-size", .01),
std::make_pair("mlpack-max-iter", 30)});
or in Python
optimizer = xacc.getOptimizer('mlpack', {'mlpack-optimizer':'sgd'})
// default adam
optimizer = xacc.getOptimizer("mlpack")
// adagrad with 30 max iters and .01 step size
optimizer = xacc.getOptimizer("mlpack", {'mlpack-optimizer':'adagrad',
'mlpack-step-size':.01,
'mlpack-max-iter':30})
NLOpt¶
|
Optimizer Parameter |
Parameter Description |
default |
type |
---|---|---|---|---|
cobyla |
nlopt-ftol |
Maximum absolute tolerance to terminate algorithm. |
1e-6 |
double |
nlopt-maxeval |
Maximum number of iterations allowed |
1000 |
int |
|
l-bfgs |
nlopt-ftol |
Maximum absolute tolerance to terminate algorithm. |
1e-6 |
double |
nlopt-maxeval |
Maximum number of iterations allowed |
1000 |
int |
|
nelder-mead |
nlopt-ftol |
Maximum absolute tolerance to terminate algorithm. |
1e-6 |
double |
nlopt-maxeval |
Maximum number of iterations allowed |
1000 |
int |
Accelerators¶
Here we detail all available XACC Accelerators
and their exposed input parameters.
IBM¶
The IBM Accelerator by default targets the remote ibmq_qasm_simulator
. You can point to a
different backend in two ways:
auto ibm_valencia = xacc::getAccelerator("ibm:ibmq_valencia");
... or ...
auto ibm_valencia = xacc::getAccelerator("ibm", {std::make_pair("backend", "ibmq_valencia")});
in Python
ibm_valencia = xacc.getAccelerator('ibm:ibmq_valencia');
... or ...
ibm_valencia = xacc.getAccelerator('ibm', {'backend':'ibmq_valencia')});
You can specify the number of shots in this way as well
auto ibm_valencia = xacc::getAccelerator("ibm:ibmq_valencia", {std::make_pair("shots", 2048)});
or in Python
ibm_valencia = xacc.getAccelerator('ibm:ibmq_valencia', {'shots':2048)});
In order to target the remote backend (for initialize()
or execute()
) you must provide
your IBM credentials to XACC. To do this add the following to a plain text file $HOME/.ibm_config
key: YOUR_KEY_HERE
hub: HUB
group: GROUP
project: PROJECT
You can also create this file using the xacc
Python module
$ python3 -m xacc -c ibm -k YOUR_KEY --group GROUP --hub HUB --project PROJECT --url URL
[ for public API ]
$ python3 -m xacc -c ibm -k YOUR_KEY
where you provide URL, HUB, PROJECT, GROUP, and YOUR_KEY.
Aer¶
The Aer Accelerator provides a great example of contributing plugins or extensions to core C++ XACC interfaces
from Python. To see how this is done, checkout the code here.
This Accelerator connects the XACC IR infrastructure with the qiskit-aer
simulator, providing a
robust simulator that can mimic noise models published by IBM backends. Note to use these noise models you must
have setup your $HOME/.ibm_config
file (see above discussion on IBM Accelerator).
aer = xacc.getAccelerator('aer')
... or ...
aer = xacc.getAccelerator('aer', {'shots':8192})
... or ...
# For ibmq_johannesburg-like readout error
aer = xacc.getAccelerator('aer', {'shots':2048, 'backend':'ibmq_johannesburg', 'readout_error':True})
... or ...
# For all ibmq_johannesburg-like errors
aer = xacc.getAccelerator('aer', {'shots':2048, 'backend':'ibmq_johannesburg',
'readout_error':True,
'thermal_relaxation':True,
'gate_error':True})
You can also use this simulator from C++, just make sure you load the Python external language plugin.
xacc::Initialize();
xacc::external::load_external_language_plugins();
auto accelerator = xacc::getAccelerator("aer", {std::make_pair("shots", 8192),
std::make_pair("readout_error", true)});
.. run simulation
xacc::external::unload_external_language_plugins();
xacc::Finalize();
QCS¶
XACC provides support for the Rigetti QCS platform through the QCS Accelerator implementation. This
Accelerator requires a few extra third-party libraries that you will need to install in order
to get QCS support. Specifically we need libzmq
, cppzmq
, msgpack-c
, and uuid-dev
.
Note that more than likely this will only be built on the QCS Centos 7 VM, so the following
instructions are specifically for that OS.
$ git clone https://github.com/zeromq/libzmq
$ cd libzmq/ && mkdir build && cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/.zmq
$ make -j12 install
$ cd ../..
$ git clone https://github.com/zeromq/cppzmq
$ cd cppzmq/ && mkdir build && cd build/
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/.zmq -DCMAKE_PREFIX_PATH=~/.zmq
$ make -j12 install
$ cd ../..
$ git clone https://github.com/msgpack/msgpack-c/
$ cd msgpack-c/ && mkdir build && cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/.zmq
$ make -j12 install
$ cd ../..
$ sudo yum install uuid-dev devtoolset-8-gcc devtoolset-8-gcc-c++
$ scl enable devtoolset-8 -- bash
[go to your xacc build directory]
cmake .. -DUUID_LIBRARY=/usr/lib64/libuuid.so.1
make install
There is no further configuration for using the QCS platform.
To use the QCS Accelerator targeting something like Aspen-4-2Q-A
(for example, replace with your lattice):
auto qcs = xacc::getAccelerator("qcs:Aspen-4-2Q-A", {std::make_pair("shots", 10000)});
or in Python
qcs = xacc.getAccelerator('qcs:Aspen-4-2Q-A', {'shots':10000)});
For now you must manually map your CompositeInstruction
to the correct physical bits
provided by your lattice. To do so, run
qpu = xacc.getAccelerator('qcs:Aspen-4-2Q-A')
[given CompositeInstruction f]
f.defaultPlacement(qpu)
[or manually]
f.mapBits([5,9])
IonQ¶
The IonQ Accelerator by default targets the remote simulator
backend. You can point to the physical
QPU in two ways:
auto ionq = xacc::getAccelerator("ionq:qpu");
... or ...
auto ionq = xacc::getAccelerator("ionq", {std::make_pair("backend", "qpu")});
in Python
ionq = xacc.getAccelerator('ionq:qpu');
... or ...
ionq = xacc.getAccelerator('ionq', {'backend':'qpu')});
You can specify the number of shots in this way as well
auto ionq = xacc::getAccelerator("ionq", {std::make_pair("shots", 2048)});
or in Python
ionq = xacc.getAccelerator('ionq', {'shots':2048)});
In order to target the simulator or QPU (for initialize()
or execute()
) you must provide
your IonQ credentials to XACC. To do this add the following to a plain text file $HOME/.ionq_config
key: YOUR_KEY_HERE
url: https://api.ionq.co/v0
DWave¶
The DWave Accelerator by default targets the remote DW_2000Q_VFYC_2_1
backend. You can point to a
different backend in two ways:
auto dw = xacc::getAccelerator("dwave:DW_2000Q");
... or ...
auto dw = xacc::getAccelerator("dwave", {std::make_pair("backend", "DW_2000Q")});
in Python
dw = xacc.getAccelerator('dwave:DW_2000Q');
... or ...
dw = xacc.getAccelerator('dwave', {'backend':'DW_2000Q')});
You can specify the number of shots in this way as well
auto dw = xacc::getAccelerator("dwave", {std::make_pair("shots", 2048)});
or in Python
dw = xacc.getAccelerator('dwave', {'shots':2048)});
In order to target the remote backend (for initialize()
or execute()
) you must provide
your DWave credentials to XACC. To do this add the following to a plain text file $HOME/.dwave_config
key: YOUR_KEY_HERE
url: https://cloud.dwavesys.com
You can also create this file using the xacc
Python module
$ python3 -m xacc -c dwave -k YOUR_KEY
where you provide YOUR_KEY.
DWave Neal¶
The DWave Neal Accelerator provides another example of contributing plugins or extensions to core C++ XACC interfaces
from Python. To see how this is done, checkout the code here.
This Accelerator connects the XACC IR infrastructure with the dwave-neal
simulator, providing a local
simulator that can mimic DWave QPU execution.
aer = xacc.getAccelerator('dwave-neal')
... or ...
aer = xacc.getAccelerator('dwave-neal', {'shots':2000})
You can also use this simulator from C++, just make sure you load the Python external language plugin.
xacc::Initialize();
xacc::external::load_external_language_plugins();
auto accelerator = xacc::getAccelerator("dwave-neal", {std::make_pair("shots", 8192)});
.. run simulation
xacc::external::unload_external_language_plugins();
xacc::Finalize();
TNQVM¶
TNQVM provides an Accelerator implementation that leverages tensor network theory to simulate quantum circuits.
TNQVM implements a few tensor-based quantum circuit simulators that can be specified by the tnqvm-visitor configuration key. The list of visitors and their descriptions are:
tnqvm-visitor |
Description |
---|---|
itensor-mps |
MPS simulator based on itensor library. |
exatn |
Full tensor contraction simulator based on ExaTN library. Tensor element type (float or double) can also specified after a ‘:’ separator, e.g., exatn:double (default) or exatn:float |
exatn-mps |
MPS simulator based on ExaTN library. |
exatn-pmps |
Purified-MPS (density matrix) simulator based on ExaTN library. |
auto qpu = xacc::getAccelerator("tnqvm", {{"tnqvm-visitor", "exatn"}});
For the exatn simulator, there are additional options that users can set during initialization:
Initialization Parameter |
Parameter Description |
type |
default |
---|---|---|---|
exatn-buffer-size-gb |
ExaTN’s host memory buffer size (in GB) |
int |
8 (GB) |
exatn-contract-seq-optimizer |
ExaTN’s contraction sequence optimizer to use. |
string |
metis |
calc-contract-cost-flops |
Estimate the Flops and Memory requirements only (no tensor contraction) If true, the following info will be added to the AcceleratorBuffer:
|
bool |
false |
bitstring |
If provided, the output amplitude/partial state vector associated with that bitstring will be computed. The length of the input bitstring must match the number of qubits. Non-projected bits (partial state vector) are indicated by -1 values. Returned values in the AcceleratorBuffer:
|
vector<int> |
<unused> |
contract-with-conjugate |
If true, we append the conjugate of the input circuit. This is used to validate internal tensor contraction. contract-with-conjugate-result key in the AcceleratorBuffer will be set to true if the validation is successful. |
bool |
false |
mpi-communicator |
The MPI communicator to initialize ExaTN runtime with. If not provided, by default, ExaTN will use MPI_COMM_WORLD. |
void* |
<unused> |
exp-val-by-conjugate |
If true, the expectation value of large circuits (exceed memory limit) is computed by closing the tensor network with its conjugate. |
bool |
false |
For the exatn-mps simulator, there are additional options that users can set during initialization:
Initialization Parameter |
Parameter Description |
type |
default |
---|---|---|---|
svd-cutoff |
SVD cut-off limit. |
double |
numeric_limits::min |
max-bond-dim |
Max bond dimension to keep. |
int |
no limit |
mpi-communicator |
The MPI communicator to initialize ExaTN runtime with. If not provided, by default, ExaTN will use MPI_COMM_WORLD. |
void* |
<unused> |
For the exatn-pmps simulator, there are additional options that users can set during initialization:
Initialization Parameter |
Parameter Description |
type |
default |
---|---|---|---|
backend-json |
Backend configuration JSON to estimate the noise model from. |
string |
None |
backend |
Name of the IBMQ backend to query the backend configuration. |
string |
None |
If either backend-json or backend is provided, the exatn-pmps simulator will simulate the backend noise associated with each quantum gate.
Atos QLM¶
XACC provides an Accelerator interface to the Atos QLM simulator (plugin name: “atos-qlm”) via the QLMaaS (QLM-as-a-service) API. Users need to have a remote account (QLMaaS) to use this functionality.
Prerequisites: myqlm
and qlmaas
Python packages on your local machine.
These packages can be installed via pip.
Connection configuration set-up:
Method 1 (recommended): create a file named .qlm_config at the $HOME directory with the following information.
Please note that the host address can be changed to access other QLM machines if you have access to them.
host: quantumbull.ornl.gov
username: <your QLM user name>
password: <your QLM password>
Method 2: provide the “username” and “password” fields when retrieving the qlm Accelerator, e.g.,
auto qlm = xacc::getAccelerator("atos-qlm", {{"username", "<your QLM user name>"}, {"password", "<your QLM password>"}});
Method 3: reusing your QLMaaS configuration if you have one.
For example, you have created the configuration using the Python qlmaas package, which is saved to $HOME/.qlmaas/config.ini. In this case, you don’t need to provide any additional information. It should just work.
Here we list all configurations that this Accelerator supports.
Parameter |
Parameter Description |
type |
---|---|---|
sim-type |
Name of the simulator. Options: LinAlg (default), MPS, Feynman, Bdd |
std::string |
shots |
Number of measurement shots. |
int |
mps-threshold |
[MPS] SVD truncation threshold. |
double |
max-bond |
[MPS] Max bond dimension. |
int |
threads |
Number of threads to use. |
int |
qpu = xacc.getAccelerator('atos-qlm', {'sim-type': 'MPS', 'max-bond': 512})
QuaC¶
The QuaC accelerator is a pulse-level accelerator (simulation only) that can execute quantum circuits at both gate and pulse (analog) level.
To use this accelerator, you need to build and install QuaC (see here for instructions.)
In pulse mode, you need to provide the QuaC accelerator a dynamical system model which can be constructed from an OpenPulse-format Hamiltonian JSON:
hamiltonianJson = {
"description": "Hamiltonian of a one-qubit system.\n",
"h_str": ["-0.5*omega0*Z0", "omegaa*X0||D0"],
"osc": {},
"qub": {
"0": 2
},
"vars": {
"omega0": 6.2831853,
"omegaa": 0.0314159
}
}
# Create a pulse system model object
model = xacc.createPulseModel()
# Load the Hamiltonian JSON (string) to the system model
loadResult = model.loadHamiltonianJson(json.dumps(hamiltonianJson))
The QuaC simulator can then be requested by
qpu = xacc.getAccelerator('QuaC', {'system-model': model.name()})
Pulse-level instructions can be constructed manually (assigning sample points)
pulseData = np.ones(pulseLength)
# Register the pulse named 'square' as an XACC instruction
xacc.addPulse('square', pulseData)
provider = xacc.getIRProvider('quantum')
squarePulseInst = provider.createInstruction('square', [0])
squarePulseInst.setChannel('d0')
# This instruction can be added to any XACC quantum Composite Instruction
prog.addInstruction(squarePulseInst)
or automatically (converting from quantum gates to pulses). To use automatic gate-to-pulse functionality, we need to load a pulse library to the accelerator as follows:
# Load the backend JSON file which contains a pulse library
backendJson = open('backends.json', 'r').read()
qpu.contributeInstructions(backendJson)
For more information, please check out these examples.
Qrack¶
The vm6502q/qrack simulator-based accelerator provides optional OpenCL-based GPU acceleration, as well as a novel simulator optimization layer.
auto qrk = xacc::getAccelerator("qrack", {std::make_pair("shots", 2048)});
By default, it selects initialization parameters that are commonly best for a wide range of use cases. However, it is highly configurable through a number of exposed parameters:
Initialization Parameter |
Parameter Description |
type |
default |
---|---|---|---|
shots |
Number of iterations to repeat the circuit for |
int |
-1 (Z-expectation only) |
use_opencl |
Use OpenCL acceleration if available, (otherwise native C++11) |
bool |
true |
use_qunit |
Turn on the novel optimization layer, (otherwise “Schrödinger method”) |
bool |
true |
device_id |
The (Qrack) device ID number of the OpenCL accelerator to use |
int |
-1 (auto-select) |
do_normalize |
Enable small norm probability amplitude flooring and normalization |
bool |
true |
zero_threshold |
Norm threshold for clamping probability amplitudes to 0 |
double |
1e-14/1e-30 float/double |
Algorithms¶
XACC exposes hybrid quantum-classical Algorithm implementations for the variational quantum eigensolver (VQE), data-driven circuit learning (DDCL), and chemistry reduced density matrix generation (RDM).
VQE¶
The VQE Algorithm requires the following input information:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator, vqe computes ground eigenvalue of this |
std::shared_ptr<Observable> |
ansatz |
The unmeasured, parameterized quantum circuit |
std::shared_ptr<CompositeInstruction> |
optimizer |
The classical optimizer to use |
std::shared_ptr<Optimizer> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
This Algorithm will add opt-val
(double
) and opt-params
(std::vector<double>
) to the provided AcceleratorBuffer
.
The results of the algorithm are therefore retrieved via these keys (see snippet below). Note you can
control the initial VQE parameters with the Optimizer
initial-parameters
key (by default all zeros).
#include "xacc.hpp"
#include "xacc_observable.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Get reference to the Accelerator
// specified by --accelerator argument
auto accelerator = xacc::getAccelerator();
// Create the N=2 deuteron Hamiltonian
auto H_N_2 = xacc::quantum::getObservable(
"pauli", std::string("5.907 - 2.1433 X0X1 "
"- 2.1433 Y0Y1"
"+ .21829 Z0 - 6.125 Z1"));
auto optimizer = xacc::getOptimizer("nlopt",
{std::make_pair("initial-parameters", {.5})});
// JIT map Quil QASM Ansatz to IR
xacc::qasm(R"(
.compiler xasm
.circuit deuteron_ansatz
.parameters theta
.qbit q
X(q[0]);
Ry(q[1], theta);
CNOT(q[1],q[0]);
)");
auto ansatz = xacc::getCompiled("deuteron_ansatz");
// Get the VQE Algorithm and initialize it
auto vqe = xacc::getAlgorithm("vqe");
vqe->initialize({std::make_pair("ansatz", ansatz),
std::make_pair("observable", H_N_2),
std::make_pair("accelerator", accelerator),
std::make_pair("optimizer", optimizer)});
// Allocate some qubits and execute
auto buffer = xacc::qalloc(2);
vqe->execute(buffer);
auto ground_energy = (*buffer)["opt-val"].as<double>();
auto params = (*buffer)["opt-params"].as<std::vector<double>>();
}
In Python:
import xacc
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('tnqvm')
buffer = xacc.qalloc(2)
# Construct the Hamiltonian as an XACC-VQE PauliOperator
ham = xacc.getObservable('pauli', '5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1')
xacc.qasm('''.compiler xasm
.circuit ansatz2
.parameters t0
.qbit q
X(q[0]);
Ry(q[1],t0);
CX(q[1],q[0]);
''')
ansatz2 = xacc.getCompiled('ansatz2')
opt = xacc.getOptimizer('nlopt', {'initial-parameters':[.5]})
# Create the VQE algorithm
vqe = xacc.getAlgorithm('vqe', {
'ansatz': ansatz2,
'accelerator': qpu,
'observable': ham,
'optimizer': opt
})
vqe.execute(buffer)
energy = buffer['opt-val']
params = buffer['opt-params']
DDCL¶
The DDCL Algorithm implements the following algorithm - given a target probability distribution, propose a parameterized quantum circuit and train (minimize loss) the circuit to reproduce that given target distribution. We design DDCL to be extensible in loss function computation and gradient computation strategies.
The DDCL Algorithm requires the following input information:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
target_dist |
The target probability distribution to reproduce |
std::vector<double> |
ansatz |
The unmeasured, parameterized quantum circuit |
std::shared_ptr<CompositeInstruction> |
optimizer |
The classical optimizer to use, can be gradient based |
std::shared_ptr<Optimizer> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
loss |
The loss strategy to use |
std::string |
gradient |
The gradient strategy to use |
std::string |
As of this writing, loss can take js
and mmd
values for Jansen-Shannon divergence and Maximum Mean Discrepancy, respectively.
More are being added. Also, gradient can take js-parameter-shift
and mmd-parameter-shift
values. These gradient
strategies will shift each parameter by plus or minus pi over 2.
#include "xacc.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
xacc::external::load_external_language_plugins();
xacc::set_verbose(true);
// Get reference to the Accelerator
auto accelerator = xacc::getAccelerator("aer");
auto optimizer = xacc::getOptimizer("mlpack");
xacc::qasm(R"(
.compiler xasm
.circuit qubit2_depth1
.parameters x
.qbit q
U(q[0], x[0], -pi/2, pi/2 );
U(q[0], 0, 0, x[1]);
U(q[1], x[2], -pi/2, pi/2);
U(q[1], 0, 0, x[3]);
CNOT(q[0], q[1]);
U(q[0], 0, 0, x[4]);
U(q[0], x[5], -pi/2, pi/2);
U(q[1], 0, 0, x[6]);
U(q[1], x[7], -pi/2, pi/2);
)");
auto ansatz = xacc::getCompiled("qubit2_depth1");
std::vector<double> target_distribution {.5, .5, .5, .5};
auto ddcl = xacc::getAlgorithm("ddcl");
ddcl->initialize({std::make_pair("ansatz", ansatz),
std::make_pair("target_dist", target_distribution),
std::make_pair("accelerator", accelerator),
std::make_pair("loss", "js"),
std::make_pair("gradient", "js-parameter-shift"),
std::make_pair("optimizer", optimizer)});
// Allocate some qubits and execute
auto buffer = xacc::qalloc(2);
ddcl->execute(buffer);
// Print the result
std::cout << "Loss: " << buffer["opt-val"].as<double>()
<< "\n";
xacc::external::unload_external_language_plugins();
xacc::Finalize();
}
or in Python
import xacc
# Get the QPU and allocate a single qubit
qpu = xacc.getAccelerator('aer')
qbits = xacc.qalloc(1)
# Get the MLPack Optimizer, default is Adam
optimizer = xacc.getOptimizer('mlpack')
# Create a simple quantum program
xacc.qasm('''
.compiler xasm
.circuit foo
.parameters x,y,z
.qbit q
Ry(q[0], x);
Ry(q[0], y);
Ry(q[0], z);
''')
f = xacc.getCompiled('foo')
# Get the DDCL Algorithm, initialize it
# with necessary parameters
ddcl = xacc.getAlgorithm('ddcl', {'ansatz': f,
'accelerator': qpu,
'target_dist': [.5,.5],
'optimizer': optimizer,
'loss': 'js',
'gradient': 'js-parameter-shift'})
# execute
ddcl.execute(qbits)
print(qbits.keys())
print(qbits['opt-val'])
print(qbits['opt-params'])
Rotoselect¶
The Rotoselect
Quantum Circuit Structure Learning Algorithm (Ostaszewski et al. (2019)) requires the following input information:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator, Rotoselect computes ground eigenvalue of this |
std::shared_ptr<Observable>/Observable* |
layers |
Number of circuit layers. Each layer consists of parametrized single-qubit rotations followed by a ladder of controlled-Z gates. |
int |
iterations |
The number of training iterations |
int |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator>/Accelerator* |
This Rotoselect
algorithm is designed to learn a good circuit structure
(generators of rotation are selected from the set of Pauli gates)
at fixed depth (layers
) to minimize the cost function (observable
).
This Algorithm will add opt-val
(double
) to the provided AcceleratorBuffer
.
The result of the algorithm is therefore retrieved via this key (see snippet below).
RBM Classification¶
The rbm_classification
algorithm provides an implementation that trains a
restricted boltzmann machine via sampling of a quantum annealer for the purpose of
classification. (Caldeira et al. (2019))
It exposes the following input information:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
nv |
The number of visible units |
int |
nh |
The number of hidden units |
int |
batch-size |
The batch size, defaults to 1 |
int |
n-gibbs-steps |
The number of gibbs steps to use in post-processing of dwave data |
int |
train-steps |
Hard-code the number of training iterations/steps, by default this is set to -1, meaning unlimited iterations |
int |
epochs |
The number of training epochs, defaults to 1 |
int |
train-file |
The location (relative to pwd) of the training data (as npy file) |
string |
expectation-strategy |
Strategy to use in computing model expectation values, can be gibbs, quantum-annealing, discriminative, or cd |
string |
backend |
The desired quantum-annealing backend (defaults to dwave-neal), can be any of the available D-Wave backends, must be provided as dwave:BEND |
string |
shots |
The number of samples to draw from the dwave backend |
int |
embedding |
The minor graph embedding to use, if not provided, one will be computed and used for subsequent calls to the dwave backend. |
map<int, vector<int>> |
Example usage in Python:
import xacc
# Create the RBM Classification algorithm
algo = xacc.getAlgorithm('rbm-classification',
{
'nv':64,
'nh':64,
'train-file':'sg_train_64bits.npy',
'expectation-strategy':'quantum-annealing',
'backend':'dwave:DW_2000Q_5',
'shots':100,
})
qbits = xacc.qalloc()
algo.execute(qbits)
# get the trained RBM weights
# for further use and post-processing
w = qbits['w']
bv = qbits['bv']
bh = qbits['bh']
Quantum Process Tomography¶
The qpt
algorithm provides an implementation of Algorithm that uses linear
inversion to compute the chi process matrix for a desired circuit.
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
circuit |
The circuit to characterize |
pointer-like CompositeInstruction |
accelerator |
The backend quantum computer to use |
pointer-like Accelerator |
qubit-map |
The physical qubits to map the logical circuit onto |
vector<int> |
#include "xacc.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
auto acc = xacc::getAccelerator("ibm::ibmq_poughkeepsie");
auto compiler = xacc::getCompiler("xasm");
auto ir = compiler->compile(R"(__qpu__ void f(qbit q) {
H(q[0]);
})", nullptr);
auto h = ir->getComposite("f");
auto qpt = xacc::getAlgorithm("qpt", {
std::make_pair("circuit", h),
std::make_pair("accelerator", acc)
});
auto buffer = xacc::qalloc(1);
qpt->execute(buffer);
auto chi_real = (*buffer)["chi-real"];
auto chi_imag = (*buffer)["chi-imag"];
}
or in Python
import xacc
# Choose the QPU on which to
# characterize the process matrix for a Hadamard
qpu = xacc.getAccelerator('ibm:ibmq_poughkeepsie')
# Create the CompositeInstruction containing a
# single Hadamard instruction
provider = xacc.getIRProvider('quantum')
circuit = provider.createComposite('U')
hadamard = provider.createInstruction('H', [0])
circuit.addInstruction(hadamard)
# Create the Algorithm, give it the circuit
# to characterize and the backend to target
qpt = xacc.getAlgorithm('qpt', {'circuit':circuit, 'accelerator':qpu})
# Allocate a qubit, this will
# store our tomography results
buffer = xacc.qalloc(1)
# Execute
qpt.execute(buffer)
# Compute the fidelity with respect to
# the ideal hadamard process
F = qpt.calculate('fidelity', buffer, {'chi-theoretical-real':[0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 1., 0., 1.]})
print('\nFidelity: ', F)
QAOA¶
The QAOA Algorithm requires the following input information:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator represents the cost Hamiltonian. |
std::shared_ptr<Observable> |
optimizer |
The classical optimizer to use |
std::shared_ptr<Optimizer> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
steps |
The number of timesteps. Corresponds to ‘p’ in the literature. This is optional, default = 1 if not provided. |
int |
parameter-scheme |
The QAOA parameterization scheme (‘Extended’ or ‘Standard’). This is optional, default = ‘Extended’ if not provided. |
string |
graph |
The MaxCut graph problem. If provided, the cost Hamiltonian is constructed automatically. |
std::shared_ptr<Graph> |
This Algorithm will add opt-val
(double
) and opt-params
(std::vector<double>
) to the provided AcceleratorBuffer
.
The results of the algorithm are therefore retrieved via these keys (see snippet below). Note you can
control the initial QAOA parameters with the Optimizer
initial-parameters
key (by default all zeros).
#include "xacc.hpp"
#include "xacc_observable.hpp"
#include "xacc_service.hpp"
#include <random>
// Use XACC built-in QAOA to solve a QUBO problem
// QUBO function:
// y = -5x1 - 3x2 - 8x3 - 6x4 + 4x1x2 + 8x1x3 + 2x2x3 + 10x3x4
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Use the Qpp simulator as the accelerator
auto acc = xacc::getAccelerator("qpp");
auto buffer = xacc::qalloc(4);
// The corresponding QUBO Hamiltonian is:
auto observable = xacc::quantum::getObservable(
"pauli",
std::string("-5.0 - 0.5 Z0 - 1.0 Z2 + 0.5 Z3 + 1.0 Z0 Z1 + 2.0 Z0 Z2 + 0.5 Z1 Z2 + 2.5 Z2 Z3"));
const int nbSteps = 12;
const int nbParams = nbSteps*11;
std::vector<double> initialParams;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(-2.0, 2.0);
// Init random parameters
for (int i = 0; i < nbParams; ++i)
{
initialParams.emplace_back(dis(gen));
}
auto optimizer = xacc::getOptimizer("nlopt",
xacc::HeterogeneousMap {
std::make_pair("initial-parameters", initialParams),
std::make_pair("nlopt-maxeval", nbParams*100) });
auto qaoa = xacc::getService<xacc::Algorithm>("QAOA");
const bool initOk = qaoa->initialize({
std::make_pair("accelerator", acc),
std::make_pair("optimizer", optimizer),
std::make_pair("observable", observable),
// number of time steps (p) param
std::make_pair("steps", nbSteps)
});
qaoa->execute(buffer);
std::cout << "Min QUBO: " << (*buffer)["opt-val"].as<double>() << "\n";
}
In Python:
import xacc,sys, numpy as np
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp')
# Construct the Hamiltonian as an XACC PauliOperator
# This Hamiltonian corresponds to the QUBO problem:
# y = -5x_1 -3x_2 -8x_3 -6x_4 + 4x_1x_2 + 8x_1x_3 + 2x_2x_3 + 10x_3x_4
ham = xacc.getObservable('pauli', '-5.0 - 0.5 Z0 - 1.0 Z2 + 0.5 Z3 + 1.0 Z0 Z1 + 2.0 Z0 Z2 + 0.5 Z1 Z2 + 2.5 Z2 Z3')
# We need 4 qubits
buffer = xacc.qalloc(4)
# There are 7 gamma terms (non-identity) in the cost Hamiltonian
# and 4 beta terms for mixer Hamiltonian
nbParamsPerStep = 7 + 4
# The number of steps (often referred to as 'p' parameter):
# alternating layers of mixer and cost Hamiltonian exponential.
nbSteps = 4
# Total number of params
nbTotalParams = nbParamsPerStep * nbSteps
# Init params randomly:
initParams = np.random.rand(nbTotalParams)
# The optimizer: nlopt
opt = xacc.getOptimizer('nlopt', { 'initial-parameters': initParams })
# Create the QAOA algorithm
qaoa = xacc.getAlgorithm('QAOA', {
'accelerator': qpu,
'observable': ham,
'optimizer': opt,
'steps': nbSteps
})
result = qaoa.execute(buffer)
print('Min QUBO value = ', buffer.getInformation('opt-val'))
Quantum Phase Estimation¶
The QPE
algorithm (also known as quantum eigenvalue estimation algorithm) provides
an implementation of Algorithm that estimates the phase (or eigenvalue) of an eigenvector of a unitary operator.
Here the unitary operator is called an oracle which is a quantum subroutine that acts upon a set of qubits and returns the answer as a phase. The bits precision is automatically inferred from the size of the input buffer.
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
oracle |
The circuit represents the unitary operator. |
pointer-like CompositeInstruction |
accelerator |
The backend quantum computer to use. |
pointer-like Accelerator |
state-preparation |
The circuit to prepare the eigen state. |
pointer-like CompositeInstruction |
#include "xacc.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Accelerator:
auto acc = xacc::getAccelerator("qpp", {std::make_pair("shots", 4096)});
// In this example: we want to estimate the *phase* of an arbitrary 'oracle'
// i.e. Oracle(|State>) = exp(i*Phase)*|State>
// and we need to estimate that Phase.
// Oracle: CPhase(theta) or CU1(theta) which is defined as
// 1 0 0 0
// 0 1 0 0
// 0 0 1 0
// 0 0 0 e^(i*theta)
// The eigenstate is |11>; i.e. CPhase(theta)|11> = e^(i*theta)|11>
// Since this oracle operates on 2 qubits, we need to add more qubits to the buffer.
// The more qubits we have, the more accurate the estimate.
// Resolution := 2^(number qubits in the calculation register).
// 5-bit precision => 7 qubits in total
auto buffer = xacc::qalloc(7);
auto qpe = xacc::getService<xacc::Algorithm>("QPE");
auto compiler = xacc::getCompiler("xasm");
// Create oracle: CPhase gate with theta = 2pi/3
// i.e. the phase value to estimate is 1/3 ~ 0.33333.
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto oracle = gateRegistry->createComposite("oracle");
oracle->addInstruction(gateRegistry->createInstruction("CPhase", { 0, 1 }, { 2.0 * M_PI/ 3.0 }));
// Eigenstate preparation = |11> state
auto statePrep = compiler->compile(R"(__qpu__ void prep1(qbit q) {
X(q[0]);
X(q[1]);
})", nullptr)->getComposite("prep1");
// Initialize the Quantum Phase Estimation:
qpe->initialize({
std::make_pair("accelerator", acc),
std::make_pair("oracle", oracle),
std::make_pair("state-preparation", statePrep)
});
// Run the algorithm
qpe->execute(buffer);
// Expected result:
// The factor here is 2^5 (precision) = 32
// we expect the two most-likely bitstring is 10 and 11
// i.e. the true result is between 10/32 = 0.3125 and 11/32 = 0.34375
std::cout << "Probability of the two most-likely bitstrings 10 (theta = 0.3125) and 11 (theta = 0.34375 ): \n";
std::cout << "Probability of |11010> (11) = " << buffer->computeMeasurementProbability("11010") << "\n";
std::cout << "Probability of |01010> (10) = " << buffer->computeMeasurementProbability("01010") << "\n";
xacc::Finalize();
}
or in Python
import xacc,sys, numpy as np
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp', { 'shots': 4096 })
# In this example: we want to estimate the *phase* of an arbitrary 'oracle'
# i.e. Oracle(|State>) = exp(i*Phase)*|State>
# and we need to estimate that Phase.
# The oracle is a simple T gate, and the eigenstate is |1>
# T|1> = e^(i*pi/4)|1>
# The phase value of pi/4 = 2pi * (1/8)
# i.e. if we use a 3-bit register for estimation,
# we will get the correct answer of 1 deterministically.
xacc.qasm('''.compiler xasm
.circuit oracle
.qbit q
T(q[0]);
''')
oracle = xacc.getCompiled('oracle')
# We need to prepare the eigenstate |1>
xacc.qasm('''.compiler xasm
.circuit prep
.qbit q
X(q[0]);
''')
statePrep = xacc.getCompiled('prep')
# We need 4 qubits (3-bit precision)
buffer = xacc.qalloc(4)
# Create the QPE algorithm
qpe = xacc.getAlgorithm('QPE', {
'accelerator': qpu,
'oracle': oracle,
'state-preparation': statePrep
})
qpe.execute(buffer)
# We should only get the bit string of |100> = 1
# i.e. phase value of 1/2^3 = 1/8.
print(buffer)
QITE¶
The Quantum Imaginary Time Evolution (QITE) Algorithm requires the following input information: (Motta et al. (2020))
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator represents the cost Hamiltonian. |
std::shared_ptr<Observable> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
steps |
The number of Trotter steps. |
int |
step-size |
The Trotter step size. |
double |
Optionally, users can provide these parameters:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
ansatz |
State preparation circuit. |
std::shared_ptr<CompositeInstruction> |
analytical |
If true, perform an analytical run rather than executing quantum circuits on the Accelerator backend. |
boolean |
initial-state |
For analytical mode only, select the initial state. |
int |
This Algorithm will add opt-val
(double
) which is the energy value at the final Trotter step to the provided AcceleratorBuffer
.
The results of the algorithm are therefore retrieved via these keys (see snippet below).
Also, energy values at each Trotter step are stored in the exp-vals
field (vector<double>
).
Note: during execution, the following line may be logged to the output console:
warning: solve(): system seems singular; attempting approx solution
This is completely normal and can be safely ignored.
#include "xacc.hpp"
#include "xacc_observable.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Use the Qpp simulator as the accelerator
auto acc = xacc::getAccelerator("qpp");
auto buffer = xacc::qalloc(1);
auto observable = xacc::quantum::getObservable(
"pauli",
std::string("0.7071067811865475 X0 + 0.7071067811865475 Z0"));
auto qite = xacc::getService<xacc::Algorithm>("qite");
const int nbSteps = 25;
const double stepSize = 0.1;
const bool initOk = qite->initialize({
std::make_pair("accelerator", acc),
std::make_pair("steps", nbSteps),
std::make_pair("observable", observable),
std::make_pair("step-size", stepSize)
});
qite->execute(buffer);
std::cout << "Min Energy: " << (*buffer)["opt-val"].as<double>() << "\n";
}
In Python:
import xacc,sys, numpy as np
import matplotlib.pyplot as plt
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp')
# Construct the Hamiltonian as an XACC PauliOperator
ham = xacc.getObservable('pauli', '0.70710678118 X0 + 0.70710678118 Z0')
# We just need 1 qubit
buffer = xacc.qalloc(1)
# Horizontal axis: 0 -> 2.5
# The number of Trotter steps
nbSteps = 25
# The Trotter step size
stepSize = 0.1
# Create the QITE algorithm
qite = xacc.getAlgorithm('qite', {
'accelerator': qpu,
'observable': ham,
'step-size': stepSize,
'steps': nbSteps
})
result = qite.execute(buffer)
# Expected result: ~ -1
print('Min energy value = ', buffer.getInformation('opt-val'))
E = buffer.getInformation('exp-vals')
# Plot energy vs. beta
plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro', label = 'XACC QITE')
plt.grid()
plt.show()
ADAPT¶
The Adaptive Derivative Assembled Problem Tailored (ADAPT) Algorithm requires the following input information: (Grismley et al. (2018), Tang et al. (2019), Zhu et al. (2020))
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator represents the Hamiltonian |
std::shared_ptr<Observable> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
optimizer |
The classical optimizer to use |
std::shared_ptr<Optimizer> |
pool |
Pool of operators to construct adaptive ansatz |
std::string |
sub-algorithm |
Algorithm called by ADAPT (VQE or QAOA) |
std::string |
Optionally, users can provide these parameters:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
initial-state |
State preparation circuit. |
std::shared_ptr<CompositeInstruction> |
n-electrons |
Required parameter for VQE, unless initial-state is provided |
int |
maxiter |
Maximum number of ADAPT cycles/number of layers in QAOA |
int |
print-threshold |
Value above which commutators are printed (Default 1.0e-10) |
double |
adapt-threshold |
Stops ADAPT when norm of gradient vector falls below this value (Default 1.0e-2) |
double |
ADAPT-VQE
#include "xacc.hpp"
#include "xacc_observable.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Get reference to the Accelerator
// specified by --accelerator argument
auto accelerator = xacc::getAccelerator("qpp");
// Get reference to the Optimizer
// specified by --optimizer argument
auto optimizer = xacc::getOptimizer("nlopt", {std::make_pair("nlopt-optimizer", "l-bfgs")});
// Allocate 4 qubits in the buffer
auto buffer = xacc::qalloc(4);
// Instantiate ADAPT algorithm
auto adapt = xacc::getService<xacc::Algorithm>("adapt");
// Number of electrons
int nElectrons = 2;
// Specify the operator pool
auto pool = "qubit-pool";
// Specify the sub algorithm
auto subAlgo_vqe = "vqe";
// This is the H2 Hamiltonian in a fermionic basis
auto str = std::string("(-0.165606823582,-0) 1^ 2^ 1 2 + (0.120200490713,0) 1^ 0^ 0 1 + "
"(-0.0454063328691,-0) 0^ 3^ 1 2 + (0.168335986252,0) 2^ 0^ 0 2 + "
"(0.0454063328691,0) 1^ 2^ 3 0 + (0.168335986252,0) 0^ 2^ 2 0 + "
"(0.165606823582,0) 0^ 3^ 3 0 + (-0.0454063328691,-0) 3^ 0^ 2 1 + "
"(-0.0454063328691,-0) 1^ 3^ 0 2 + (-0.0454063328691,-0) 3^ 1^ 2 0 + "
"(0.165606823582,0) 1^ 2^ 2 1 + (-0.165606823582,-0) 0^ 3^ 0 3 + "
"(-0.479677813134,-0) 3^ 3 + (-0.0454063328691,-0) 1^ 2^ 0 3 + "
"(-0.174072892497,-0) 1^ 3^ 1 3 + (-0.0454063328691,-0) 0^ 2^ 1 3 + "
"(0.120200490713,0) 0^ 1^ 1 0 + (0.0454063328691,0) 0^ 2^ 3 1 + "
"(0.174072892497,0) 1^ 3^ 3 1 + (0.165606823582,0) 2^ 1^ 1 2 + "
"(-0.0454063328691,-0) 2^ 1^ 3 0 + (-0.120200490713,-0) 2^ 3^ 2 3 + "
"(0.120200490713,0) 2^ 3^ 3 2 + (-0.168335986252,-0) 0^ 2^ 0 2 + "
"(0.120200490713,0) 3^ 2^ 2 3 + (-0.120200490713,-0) 3^ 2^ 3 2 + "
"(0.0454063328691,0) 1^ 3^ 2 0 + (-1.2488468038,-0) 0^ 0 + "
"(0.0454063328691,0) 3^ 1^ 0 2 + (-0.168335986252,-0) 2^ 0^ 2 0 + "
"(0.165606823582,0) 3^ 0^ 0 3 + (-0.0454063328691,-0) 2^ 0^ 3 1 + "
"(0.0454063328691,0) 2^ 0^ 1 3 + (-1.2488468038,-0) 2^ 2 + "
"(0.0454063328691,0) 2^ 1^ 0 3 + (0.174072892497,0) 3^ 1^ 1 3 + "
"(-0.479677813134,-0) 1^ 1 + (-0.174072892497,-0) 3^ 1^ 3 1 + "
"(0.0454063328691,0) 3^ 0^ 1 2 + (-0.165606823582,-0) 3^ 0^ 3 0 + "
"(0.0454063328691,0) 0^ 3^ 2 1 + (-0.165606823582,-0) 2^ 1^ 2 1 + "
"(-0.120200490713,-0) 0^ 1^ 0 1 + (-0.120200490713,-0) 1^ 0^ 1 0 + (0.7080240981,0)");
// Create Observable from Hamiltonian string
auto H = xacc::quantum::getObservable("fermion", str);
// Pass parameters to ADAPT algorithm
adapt->initialize({std::make_pair("accelerator", accelerator),
std::make_pair("observable", H),
std::make_pair("optimizer", optimizer),
std::make_pair("pool", pool),
std::make_pair("n-electrons", nElectrons),
std::make_pair("sub-algorithm", subAlgo_vqe)
});
// Execute ADAPT-VQE
adapt->execute(buffer);
xacc::Finalize();
return 0;
}
In Python:
import xacc
qpu = xacc.getAccelerator('qpp')
optimizer = xacc.getOptimizer('nlopt',{'nlopt-optimizer':'l-bfgs'})
buffer = xacc.qalloc(4)
opstr = '''
(-0.165606823582,-0) 1^ 2^ 1 2 + (0.120200490713,0) 1^ 0^ 0 1 +
(-0.0454063328691,-0) 0^ 3^ 1 2 + (0.168335986252,0) 2^ 0^ 0 2 +
(0.0454063328691,0) 1^ 2^ 3 0 + (0.168335986252,0) 0^ 2^ 2 0 +
(0.165606823582,0) 0^ 3^ 3 0 + (-0.0454063328691,-0) 3^ 0^ 2 1 +
(-0.0454063328691,-0) 1^ 3^ 0 2 + (-0.0454063328691,-0) 3^ 1^ 2 0 +
(0.165606823582,0) 1^ 2^ 2 1 + (-0.165606823582,-0) 0^ 3^ 0 3 +
(-0.479677813134,-0) 3^ 3 + (-0.0454063328691,-0) 1^ 2^ 0 3 +
(-0.174072892497,-0) 1^ 3^ 1 3 + (-0.0454063328691,-0) 0^ 2^ 1 3 +
(0.120200490713,0) 0^ 1^ 1 0 + (0.0454063328691,0) 0^ 2^ 3 1 +
(0.174072892497,0) 1^ 3^ 3 1 + (0.165606823582,0) 2^ 1^ 1 2 +
(-0.0454063328691,-0) 2^ 1^ 3 0 + (-0.120200490713,-0) 2^ 3^ 2 3 +
(0.120200490713,0) 2^ 3^ 3 2 + (-0.168335986252,-0) 0^ 2^ 0 2 +
(0.120200490713,0) 3^ 2^ 2 3 + (-0.120200490713,-0) 3^ 2^ 3 2 +
(0.0454063328691,0) 1^ 3^ 2 0 + (-1.2488468038,-0) 0^ 0 +
(0.0454063328691,0) 3^ 1^ 0 2 + (-0.168335986252,-0) 2^ 0^ 2 0 +
(0.165606823582,0) 3^ 0^ 0 3 + (-0.0454063328691,-0) 2^ 0^ 3 1 +
(0.0454063328691,0) 2^ 0^ 1 3 + (-1.2488468038,-0) 2^ 2 +
(0.0454063328691,0) 2^ 1^ 0 3 + (0.174072892497,0) 3^ 1^ 1 3 +
(-0.479677813134,-0) 1^ 1 + (-0.174072892497,-0) 3^ 1^ 3 1 +
(0.0454063328691,0) 3^ 0^ 1 2 + (-0.165606823582,-0) 3^ 0^ 3 0 +
(0.0454063328691,0) 0^ 3^ 2 1 + (-0.165606823582,-0) 2^ 1^ 2 1 +
(-0.120200490713,-0) 0^ 1^ 0 1 + (-0.120200490713,-0) 1^ 0^ 1 0 + (0.7080240981,0)
'''
H = xacc.getObservable('fermion', opstr)
adapt = xacc.getAlgorithm('adapt', {'accelerator': qpu,
'optimizer': optimizer,
'observable': H,
'n-electrons': 2,
'maxiter': 2,
'sub-algorithm': 'vqe',
'pool': 'qubit-pool'})
adapt.execute(buffer)
ADAPT-QAOA
#include "xacc.hpp" #include "xacc_observable.hpp" #include "xacc_service.hpp" int main(int argc, char **argv) { xacc::Initialize(argc, argv); // Get reference to the Accelerator // specified by --accelerator argument auto accelerator = xacc::getAccelerator("qpp"); // Get reference to the Optimizer // specified by --optimizer argument auto optimizer = xacc::getOptimizer("nlopt", {std::make_pair("nlopt-optimizer", "l-bfgs")}); // Allocate 4 qubits in the buffer auto buffer = xacc::qalloc(4); // Instantiate ADAPT algorithm auto adapt = xacc::getService<xacc::Algorithm>("adapt"); // Specify the operator pool auto pool = "multi-qubit-qaoa"; // Specify the sub algorithm auto subAlgo_qaoa = "QAOA"; // Number of layers auto nLayers = 2; // This is the cost Hamiltonian auto H = xacc::quantum::getObservable( "pauli", std::string("-5.0 - 0.5 Z0 - 1.0 Z2 + 0.5 Z3 + 1.0 Z0 Z1 + 2.0 Z0 Z2 + 0.5 Z1 Z2 + 2.5 Z2 Z3")); // Pass parameters to ADAPT algorithm adapt->initialize({std::make_pair("accelerator", accelerator), std::make_pair("observable", H), std::make_pair("optimizer", optimizer), std::make_pair("pool", pool), std::make_pair("maxiter", nLayers), std::make_pair("sub-algorithm", subAlgo_qaoa) }); // Execute ADAPT-QAOA adapt->execute(buffer); xacc::Finalize(); return 0; }
In Python:
import xacc
accelerator = xacc.getAccelerator("qpp")
buffer = xacc.qalloc(4)
optimizer = xacc.getOptimizer('nlopt',{'nlopt-optimizer':'l-bfgs'})
pool = "multi-qubit-qaoa"
nLayers = 2
subAlgo_qaoa = "QAOA"
H = xacc.getObservable('pauli', '-5.0 - 0.5 Z0 - 1.0 Z2 + 0.5 Z3 + 1.0 Z0 Z1 + 2.0 Z0 Z2 + 0.5 Z1 Z2 + 2.5 Z2 Z3')
adapt = xacc.getAlgorithm('adapt', {
'accelerator': accelerator,
'observable': H,
'optimizer': optimizer,
'pool': pool,
'maxiter': nLayers,
'sub-algorithm': subAlgo_qaoa
})
adapt.execute(buffer)
QCMX¶
The Quantum Connected Moments eXpansion (QCMX) Algorithm requires the following input information: (Kowalski, K. and Peng, Bo. (2018))
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator represents the Hamiltonian |
std::shared_ptr<Observable> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
ansatz |
State preparation circuit |
std::shared_ptr<CompositeInstruction> |
cmx-order |
The order of the leading term in the CMX |
int |
expansion-type |
Expansion type (Cioslowski, Knowles, PDS) |
std::string |
#include "xacc.hpp" #include "xacc_observable.hpp" #include "xacc_service.hpp" int main(int argc, char **argv) { xacc::Initialize(argc, argv); // Get reference to the Accelerator // specified by --accelerator argument auto accelerator = xacc::getAccelerator("qpp"); // Get reference to the Hamiltonian // specified by the --observable argument auto H = xacc::quantum::getObservable("pauli", std::string("0.2976 + 0.3593 Z0 - 0.4826 Z1 + 0.5818 Z0 Z1 + 0.0896 X0 X1 + 0.0896 Y0 Y1")); // Specify the expansion type auto expansion = "Cioslowski"; // Specify the CMX expansion order auto order = 2; // Create reference to the initial state // specified by the --ansatz argument auto provider = xacc::getService<xacc::IRProvider>("quantum"); auto ansatz = provider->createComposite("initial-state"); ansatz->addInstruction(provider->createInstruction("X", {(size_t)0})); // Allocate 2 qubits in the buffer auto buffer = xacc::qalloc(2); // Instantiate the QCMX algorithm auto qcmx = xacc::getService<xacc::Algorithm>("qcmx"); // Pass parameters to QCMX algorithm qcmx->initialize({{"accelerator",accelerator}, {"observable", H}, {"ansatz", ansatz}, {"cmx-order", order}, {"expansion-type", expansion}}); // Execute QCMX qcmx->execute(buffer); xacc::Finalize(); return 0; }
In Python:
import xacc
accelerator = xacc.getAccelerator("qpp")
H = xacc.getObservable('pauli', '0.2976 + 0.3593 Z0 - 0.4826 Z1 + 0.5818 Z0 Z1 + 0.0896 X0 X1 + 0.0896 Y0 Y1')
expansion = 'Cioslowski'
order = 2
provider = xacc.getIRProvider('quantum')
ansatz = provider.createComposite('initial-state')
ansatz.addInstruction(provider.createInstruction('X', [0]))
buffer = xacc.qalloc(2)
qcmx = xacc.getAlgorithm('qcmx', {
'accelerator': accelerator,
'observable': H,
'ansatz': ansatz,
'cmx-order': order,
'expansion-type': expansion
})
qcmx.execute(buffer)
QEOM¶
The Quantum Equation of Motion (QEOM) Algorithm requires the following input information: (Ollitrault et al (2020))
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
observable |
The hermitian operator represents the Hamiltonian |
std::shared_ptr<Observable> |
accelerator |
The Accelerator backend to target |
std::shared_ptr<Accelerator> |
ansatz |
State preparation circuit |
std::shared_ptr<CompositeInstruction> |
n-electrons |
The number of electrons |
int |
operators |
Excitation operators |
std::vector<std::shared_ptr<Observable>> |
Please note that the algorithm requires either n-electrons
or operators
. In the former case, it will default to single and double excitation operators.
#include "xacc.hpp" #include "xacc_service.hpp" #include "Algorithm.hpp" #include "xacc_observable.hpp" #include "OperatorPool.hpp" int main(int argc, char **argv) { xacc::Initialize(argc, argv); // Get reference to the Accelerator // specified by --accelerator argument auto accelerator = xacc::getAccelerator("qpp"); // Allocate 4 qubits in the buffer auto buffer = xacc::qalloc(4); // Get reference to the Hamiltonian // specified by the --observable argument auto str = std::string( "(-0.165606823582,-0) 1^ 2^ 1 2 + (0.120200490713,0) 1^ 0^ 0 1 + " "(-0.0454063328691,-0) 0^ 3^ 1 2 + (0.168335986252,0) 2^ 0^ 0 2 + " "(0.0454063328691,0) 1^ 2^ 3 0 + (0.168335986252,0) 0^ 2^ 2 0 + " "(0.165606823582,0) 0^ 3^ 3 0 + (-0.0454063328691,-0) 3^ 0^ 2 1 + " "(-0.0454063328691,-0) 1^ 3^ 0 2 + (-0.0454063328691,-0) 3^ 1^ 2 0 + " "(0.165606823582,0) 1^ 2^ 2 1 + (-0.165606823582,-0) 0^ 3^ 0 3 + " "(-0.479677813134,-0) 3^ 3 + (-0.0454063328691,-0) 1^ 2^ 0 3 + " "(-0.174072892497,-0) 1^ 3^ 1 3 + (-0.0454063328691,-0) 0^ 2^ 1 3 + " "(0.120200490713,0) 0^ 1^ 1 0 + (0.0454063328691,0) 0^ 2^ 3 1 + " "(0.174072892497,0) 1^ 3^ 3 1 + (0.165606823582,0) 2^ 1^ 1 2 + " "(-0.0454063328691,-0) 2^ 1^ 3 0 + (-0.120200490713,-0) 2^ 3^ 2 3 + " "(0.120200490713,0) 2^ 3^ 3 2 + (-0.168335986252,-0) 0^ 2^ 0 2 + " "(0.120200490713,0) 3^ 2^ 2 3 + (-0.120200490713,-0) 3^ 2^ 3 2 + " "(0.0454063328691,0) 1^ 3^ 2 0 + (-1.2488468038,-0) 0^ 0 + " "(0.0454063328691,0) 3^ 1^ 0 2 + (-0.168335986252,-0) 2^ 0^ 2 0 + " "(0.165606823582,0) 3^ 0^ 0 3 + (-0.0454063328691,-0) 2^ 0^ 3 1 + " "(0.0454063328691,0) 2^ 0^ 1 3 + (-1.2488468038,-0) 2^ 2 + " "(0.0454063328691,0) 2^ 1^ 0 3 + (0.174072892497,0) 3^ 1^ 1 3 + " "(-0.479677813134,-0) 1^ 1 + (-0.174072892497,-0) 3^ 1^ 3 1 + " "(0.0454063328691,0) 3^ 0^ 1 2 + (-0.165606823582,-0) 3^ 0^ 3 0 + " "(0.0454063328691,0) 0^ 3^ 2 1 + (-0.165606823582,-0) 2^ 1^ 2 1 + " "(-0.120200490713,-0) 0^ 1^ 0 1 + (-0.120200490713,-0) 1^ 0^ 1 0 + " "(0.7080240981,0)"); auto H = xacc::quantum::getObservable("fermion", str); // Create reference to the initial state // specified by the --ansatz argument auto pool = xacc::getService<OperatorPool>("singlet-adapted-uccsd"); pool->optionalParameters({{"n-electrons", 2}}); pool->generate(buffer->size()); auto ansatz = xacc::getIRProvider("quantum")->createComposite("ansatz"); ansatz->addInstruction( xacc::getIRProvider("quantum")->createInstruction("X", {0})); ansatz->addInstruction( xacc::getIRProvider("quantum")->createInstruction("X", {2})); ansatz->addVariable("x0"); for (auto &inst : pool->getOperatorInstructions(2, 0)->getInstructions()) { ansatz->addInstruction(inst); } auto kernel = ansatz->operator()({0.0808}); // Instantiate the QEOM algorithm auto qeom = xacc::getAlgorithm("qeom"); // Pass parameters to QEOM algorithm qeom->initialize({{"accelerator",accelerator}, {"observable", H}, {"ansatz", kernel}, {"n-electrons", 2}}); // Execute QEOM qeom->execute(buffer); xacc::Finalize(); return 0; }
In Python:
import xacc
accelerator = xacc.getAccelerator("qpp")
buffer = xacc.qalloc(4)
opstr = '''
(-0.165606823582,-0) 1^ 2^ 1 2 + (0.120200490713,0) 1^ 0^ 0 1 +
(-0.0454063328691,-0) 0^ 3^ 1 2 + (0.168335986252,0) 2^ 0^ 0 2 +
(0.0454063328691,0) 1^ 2^ 3 0 + (0.168335986252,0) 0^ 2^ 2 0 +
(0.165606823582,0) 0^ 3^ 3 0 + (-0.0454063328691,-0) 3^ 0^ 2 1 +
(-0.0454063328691,-0) 1^ 3^ 0 2 + (-0.0454063328691,-0) 3^ 1^ 2 0 +
(0.165606823582,0) 1^ 2^ 2 1 + (-0.165606823582,-0) 0^ 3^ 0 3 +
(-0.479677813134,-0) 3^ 3 + (-0.0454063328691,-0) 1^ 2^ 0 3 +
(-0.174072892497,-0) 1^ 3^ 1 3 + (-0.0454063328691,-0) 0^ 2^ 1 3 +
(0.120200490713,0) 0^ 1^ 1 0 + (0.0454063328691,0) 0^ 2^ 3 1 +
(0.174072892497,0) 1^ 3^ 3 1 + (0.165606823582,0) 2^ 1^ 1 2 +
(-0.0454063328691,-0) 2^ 1^ 3 0 + (-0.120200490713,-0) 2^ 3^ 2 3 +
(0.120200490713,0) 2^ 3^ 3 2 + (-0.168335986252,-0) 0^ 2^ 0 2 +
(0.120200490713,0) 3^ 2^ 2 3 + (-0.120200490713,-0) 3^ 2^ 3 2 +
(0.0454063328691,0) 1^ 3^ 2 0 + (-1.2488468038,-0) 0^ 0 +
(0.0454063328691,0) 3^ 1^ 0 2 + (-0.168335986252,-0) 2^ 0^ 2 0 +
(0.165606823582,0) 3^ 0^ 0 3 + (-0.0454063328691,-0) 2^ 0^ 3 1 +
(0.0454063328691,0) 2^ 0^ 1 3 + (-1.2488468038,-0) 2^ 2 +
(0.0454063328691,0) 2^ 1^ 0 3 + (0.174072892497,0) 3^ 1^ 1 3 +
(-0.479677813134,-0) 1^ 1 + (-0.174072892497,-0) 3^ 1^ 3 1 +
(0.0454063328691,0) 3^ 0^ 1 2 + (-0.165606823582,-0) 3^ 0^ 3 0 +
(0.0454063328691,0) 0^ 3^ 2 1 + (-0.165606823582,-0) 2^ 1^ 2 1 +
(-0.120200490713,-0) 0^ 1^ 0 1 + (-0.120200490713,-0) 1^ 0^ 1 0 + (0.7080240981,0)
'''
H = xacc.getObservable('fermion', opstr)
pool = xacc.quantum.getOperatorPool("singlet-adapted-uccsd")
pool.optionalParameters({"n-electrons": 2})
pool.generate(buffer.size())
provider = xacc.getIRProvider('quantum')
ansatz = provider.createComposite('initial-state')
ansatz.addInstruction(provider.createInstruction('X', [0]))
ansatz.addInstruction(provider.createInstruction('X', [2]))
ansatz.addVariable("x0")
for inst in pool.getOperatorInstructions(2, 0).getInstructions():
ansatz.addInstruction(inst)
kernel = ansatz.eval([0.0808])
qeom = xacc.getAlgorithm('qeom', {
'accelerator': accelerator,
'observable': H,
'ansatz': kernel,
'n-electrons': 2,
})
qeom.execute(buffer)
Accelerator Decorators¶
ROErrorDecorator¶
The ROErrorDecorator
provides an AcceleratorDecorator
implementation for affecting
readout error mitigation as in the deuteron paper.
It takes as input readout error probabilities p(0|1)
and p(1|0)
for all qubits and shifts expecation values
accordingly (see paper).
By default it will request the backend properties from the decorated Accelerator
(Accelerator::getProperties()
). This method
returns a HeterogeneousMap
. If this map contains a vector of doubles at keys p01s
and p10s
, then these
values will be used in the readout error correction. Alternatively, if the backend does not provide this data,
users can provide a custom JSON file containing the probabilities. This file should be structured as such
{
"shots": 1024,
"backend": "qcs:Aspen-2Q-A",
"0": {
"0|1": 0.0565185546875,
"1|0": 0.0089111328125,
"+": 0.0654296875,
"-": 0.047607421875
},
"1": {
"0|1": 0.095458984375,
"1|0": 0.0115966796875,
"+": 0.1070556640625,
"-": 0.0838623046875
}
}
Automating readout error mitigation with this decorator can be done in the following way:
qpu = xacc.getAccelerator('ibm:ibmq_johannesburg', {'shots':1024})
# Turn on readout error correction by decorating qpu
qpu = xacc.getAcceleratorDecorator('ro-error', qpu)
# Now use qpu as your Accelerator...
# execution will be automatically readout
# error corrected
Similarly, with a provided configuration file
auto qpu = xacc::getAccelerator("qcs:Aspen-2Q-A");
qpu = xacc::getAcceleratorDecorator("ro-error", qpu, {std::make_pair("file", "probs.json")});
See readout_error_correction_aer.py
for a full example demonstrating the utility of the ROErrorDecorator
.
RDMPurificationDecorator¶
ImprovedSamplingDecorator¶
VQE Restart Decorator¶
IR Transformations¶
CircuitOptimizer¶
This IRTransformation
of type Optimization
will search the DAG representation
of a quantum circuit and remove all zero-rotations, hadamard and cnot pairs, and merge
adjacent common rotations (e.g. Rx(.1)Rx(.1) -> Rx(.2)
).
# Create a bell state program with too many cnots
xacc.qasm('''
.compiler xasm
.circuit foo
.qbit q
H(q[0]);
CX(q[0], q[1]);
CX(q[0], q[1]);
CX(q[0], q[1]);
Measure(q[0]);
Measure(q[1]);
''')
f = xacc.getCompiled('foo')
assert(6 == f.nInstructions())
# Run the circuit-optimizer IRTransformation, can pass
# accelerator (here None) and options (here empty dict())
optimizer = xacc.getIRTransformation('circuit-optimizer')
optimizer.apply(f, None, {})
# should have 4 instructions, not 6
assert(4 == f.nInstructions())
Observables¶
Psi4 Frozen-Core¶
The psi4-frozen-core
observable generates an fermionic
observable using Psi4 and based on a user provided dictionary of options.
To use this Observable, ensure you have Psi4 installed under the same
python3
used for the XACC Python API.
$ git clone https://github.com/psi4/psi4 && cd psi4 && mkdir build && cd build
$ cmake .. -DPYTHON_EXECUTABLE=$(which python3) -DCMAKE_INSTALL_PREFIX=$(python3 -m site --user-site)/psi4
$ make -j8 install
$ export PYTHONPATH=$(python3 -m site --user-site)/psi4/lib:$PYTHONPATH
This observable type takes a dictionary of options describing the
molecular geometry (key geometry
), the basis set (key basis
),
and the list of frozen (key frozen-spin-orbitals
) and active (key active-spin-orbitals
) spin
orbital lists.
With Psi4 and XACC installed, you can use the frozen-core Observable in the following way in python.
import xacc
geom = '''
0 1
Na 0.000000 0.0 0.0
H 0.0 0.0 1.914388
symmetry c1
'''
fo = [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
ao = [5, 9, 15, 19]
H = xacc.getObservable('psi4-frozen-core', {'basis': 'sto-3g',
'geometry': geom,
'frozen-spin-orbitals': fo,
'active-spin-orbitals': ao})
Circuit Generator¶
ASWAP Ansatz Circuit¶
The ASWAP
circuit generator generates a state preparation (ansatz) circuit for the VQE Algorithm.
(See Gard, Bryan T., et al.)
The ASWAP
circuit generator requires the following input information:
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
nbQubits |
The number of qubits in the circuit. |
int |
nbParticles |
The number of particles. |
int |
timeReversalSymmetry |
Do we have time-reversal symmetry? |
boolean |
Example:
#include "xacc.hpp"
#include "xacc_observable.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
auto accelerator = xacc::getAccelerator("qpp");
auto H_N_2 = xacc::quantum::getObservable(
"pauli", std::string("5.907 - 2.1433 X0X1 "
"- 2.1433 Y0Y1"
"+ .21829 Z0 - 6.125 Z1"));
auto optimizer = xacc::getOptimizer("nlopt");
// Use the ASWAP circuit as the ansatz
xacc::qasm(R"(
.compiler xasm
.circuit deuteron_ansatz
.parameters t0
.qbit q
ASWAP(q, t0, {{"nbQubits", 2}, {"nbParticles", 1}});
)");
auto ansatz = xacc::getCompiled("deuteron_ansatz");
// Get the VQE Algorithm and initialize it
auto vqe = xacc::getAlgorithm("vqe");
vqe->initialize({std::make_pair("ansatz", ansatz),
std::make_pair("observable", H_N_2),
std::make_pair("accelerator", accelerator),
std::make_pair("optimizer", optimizer)});
// Allocate some qubits and execute
auto buffer = xacc::qalloc(2);
vqe->execute(buffer);
// Expected result: -1.74886
std::cout << "Energy: " << (*buffer)["opt-val"].as<double>() << "\n";
}
QFAST Circuit Synthesis¶
The QFAST
circuit generator generates a quantum circuit for an arbitary unitary matrix.
(See Ed Younis, et al.)
The QFAST
circuit generator only requires the unitary
input information.
Optionally, we can provide additional configurations as listed below.
Algorithm Parameter |
Parameter Description |
type |
---|---|---|
unitary |
The unitary matrix. |
Eigen::MatrixXcd/numpy 2-D array |
trace-distance |
The target trace distance of the Hilbert-Schmidt inner produc |
double (default = 0.01) |
explore-trace-distance |
The stopping-condition trace distance for the Explore phase. |
double (default = 0.1) |
initial-depth |
The initial number of circuit layers. |
int (default = 1) |
By default, after each unitary matrix decomposition, the QFAST plugin will save the result (in terms of smaller block matrices) into a cache file located at $XACC_INSTALL_DIR/tmp
. If a cache entry is found, the QFAST plugin will re-use the result automatically.
Users can modify the cache filename via the configuration key cache-file-name
.
Example:
In Cpp,
auto qfast = std::dynamic_pointer_cast<quantum::Circuit>(xacc::getService<Instruction>("QFAST"));
Eigen::MatrixXcd ccnotMat = Eigen::MatrixXcd::Identity(8, 8);
ccnotMat(6, 6) = 0.0;
ccnotMat(7, 7) = 0.0;
ccnotMat(6, 7) = 1.0;
ccnotMat(7, 6) = 1.0;
const bool expandOk = qfast->expand({
std::make_pair("unitary", ccnotMat)
});
In Python,
import xacc, numpy as np
# CCNOT matrix:
# This takes a 2-D numpy array.
ccnotMat = np.identity(8, dtype = np.cdouble)
ccnotMat[6][6] = 0.0
ccnotMat[7][7] = 0.0
ccnotMat[6][7] = 1.0
ccnotMat[7][6] = 1.0
composite = xacc.createCompositeInstruction('QFAST', { 'unitary' : ccnotMat })
print(composite)
Placement¶
Noise Adaptive Layout¶
The triQ
placement IRTransformation plugin implements the noise adaptive layout mapping method
(See Prakash Murali, et al.)
The triQ
plugin will automatically retrieve backend information (e.g. gate and readout fidelity) from the backend
Accelerator
if available.
Otherwise, users can provide the backend configurations in JSON format using the backend-json
key.
The backend Json schema must conform to IBMQ specifications.
The triQ
plugin depends on the Z3 SMT solver (version >= 4.8).
Z3 can be installed via:
Ubuntu:
apt-get install -y libz3-dev
MacOS:
brew install z3
Example:
auto compiler = xacc::getCompiler("staq");
// Specify a remote IBM backend
auto accelerator = xacc::getAccelerator("ibm:ibmq_16_melbourne");
auto q = xacc::qalloc(5);
q->setName("q");
xacc::storeBuffer(q);
auto IR = compiler->compile(R"(__qpu__ void f(qreg q) {
OPENQASM 2.0;
include "qelib1.inc";
creg c[16];
cx q[2],q[1];
cx q[1],q[2];
cx q[3],q[2];
cx q[2],q[3];
cx q[4],q[3];
cx q[3],q[4];
h q[0];
t q[4];
t q[3];
t q[0];
cx q[3],q[4];
cx q[0],q[3];
cx q[4],q[0];
tdg q[3];
})");
auto program = IR->getComposites()[0];
// Apply noise adaptive layout (TriQ)
auto irt = xacc::getIRTransformation("triQ");
irt->apply(program, accelerator);