Data Model
Variational Data Model ¶
Due to the ubiquity of variational quantum algorithms in near-term quantum computation, the QCOR language specification puts forward novel data-types that enable efficient programming of quantum-classical variational algorithms. We specify the structure of these types here:
HeterogeneousMap ¶
HeterogeneousMap
is used across the QCOR variational API as a container for a heterogeneous mapping of data. Specifically, this data type should map string keys to values of any type. This can be accomplished in C++ for instance via a std::map<std::string, std::any>
.
Operator ¶
Operator
is an interface that encapsulates the behavior of a quantum mechanical operator. It exposes an API for algebraic operations on multiple Operators
, as well as a method for observing an input unmeasured quantum kernel (e.g. state-preparation ansatz for variational algorithms). Operators
can be constructed from a string representation or a HeterogeneousMap
of options. Observation via an exposed observe()
method takes as input a quantum kernel and returns a list or vector of measured quantum kernels, whereby measurements for return kernel i
are dictated by the Operator
’s ith
term (e.g. X0X1
would append Hadamard gates followed by measure instructions). The Operator
is ultimately just a high-level interface and is meant to be subclassed by concrete operator types. The default types that must be provided by language implementors are PauliOperator
and FermionOperator
, any further sub-type definitions are up to the implementation. The base-level Operator
subtype is the PauliOperator
since it is required ultimately for any calls to observe()
. In addition to the Operator
, the language specification also mandates and OperatorTransform
interface, which is meant to be sub-typed by concrete realizations that can transform higher-level Operators
to the PauliOperator
. Moreover, this interface can be used for any general transformation on Operators
(e.g. symmetry reductions, etc.).
The language extension should provide utility functions for generating PauliOperator
and FermionOperator
instances efficiently. The implementation should provide X(int)
, Y(int)
, and Z(int)
function calls to generate the x,y,z pauli matrices, respectively. The implementation should provide adag(int)
and a(int)
to provide creation and annhilation FermionOperator
instances.
The language extension should provide the following Operator
creation API:
createOperator(expression : string) : Operator
, which defaults toPauliOperator
creation and requires a string representation of the desiredPauliOperator
createOperator(type: string, expression : string) : Operator
, which creates anOperator
of the provided type (pauli or fermion) and requires a string representation of the desiredOperator
createOperator(type: string, options : HeterogeneousMap) : Operator
, which creates anOperator
of the provided type (pauli or fermion) from a provided set of options represented as aHeterogeneousMap
(e.g. create a chemistry Hamiltonian from the basis set and geometry).
ObjectiveFunction ¶
ObjectiveFunction
is a representation of a parameterized function y = f(U(x'),x,O)
where x
denotes a list of scalar parameters, O
denotes an Operator
, and U(x')
denotes a quantum kernel whose behavior is controlled by x'
(related to x
as defined by user). ObjectiveFunction
is initialized with Operator
and quantum kernel. As a default the ObjectiveFunction
evaluates the expectation value of O
with respect to U
at a given x'
(e.g. <0|U(x')^\dagger \hat{O} U(x')|0>
). ObjectiveFunction
can be extended to provide specialized processing of the expectation values for non-VQE use cases. Using the default and extended ObjectiveFunction
, in tandem with the Optimizer
, one can succinctly implement variational quantum algorithms, for example VQE.
Optimizer ¶
Optimizer
represents a data-type encapsulating an optimization strategy executed on a given ObjectiveFunction
. Specically the Optimizer
exposes a member function, optimize
, that takes as input an ObjectiveFunction
argument and computes its optimal value and parameters which are returned as either a pair or tuple. The language extension should also enable the ability to use the optimize()
method on any general function instance, as long as the function argument structure is correct. The correct structure should be double(std::vector<double>,std::vector<double>&)
(as an example in C++), where the first vector is the input parameters and the second is a reference to the gradient vector.
Example Usage ¶
__qpu__ void ansatz(qreg q, double theta) {
X(q[0]);
auto exponent_op = X(0) * Y(1) - Y(0) * X(1);
exp_i_theta(q, theta, exponent_op);
}
int main(int argc, char **argv) {
// Allocate 2 qubits
auto q = qalloc(2);
// Create the Deuteron Hamiltonian
auto H = 5.907 - 2.1433 * X(0) * X(1) - 2.1433 * Y(0) * Y(1) + .21829 * Z(0) -
6.125 * Z(1);
// Create the objective function
auto objective = createObjectiveFunction(ansatz, H, q, 1);
// Create a qcor Optimizer
auto optimizer = createOptimizer("nlopt");
// Optimize the above function
auto [optval, opt_params] = optimizer->optimize(objective);
// Print the result
printf("energy = %f\n", optval);
return 0;
}