5 #include "gradient_function.hpp"
6 #include "qcor_observable.hpp"
7 #include "qcor_utils.hpp"
8 #include "quantum_kernel.hpp"
11 using OptimizerFunctorNoGrad =
12 std::function<double(
const std::vector<double> &)>;
13 using OptimizerFunctor =
14 std::function<double(
const std::vector<double> &, std::vector<double> &)>;
28 std::shared_ptr<CompositeInstruction> kernel;
32 HeterogeneousMap options;
33 std::vector<double> current_iterate_parameters;
36 std::function<double(
const std::vector<double> &, std::vector<double> &)>
40 bool gradients_computed =
false;
45 : _function([&](
const std::vector<double> &x, std::vector<double> &dx) {
55 const std::string name()
const override {
return "base-objective-function"; }
56 const std::string description()
const override {
return ""; }
58 virtual const int dimensions()
const {
return _dim; }
60 virtual double operator()(
const std::vector<double> &x) {
61 std::vector<double> unused_grad;
62 return operator()(x, unused_grad);
65 virtual double operator()(
const std::vector<double> &x,
66 std::vector<double> &dx) {
67 return _function(x, dx);
71 std::vector<double> &dx) {
72 throw std::bad_function_call();
76 void update_observable(
Operator updated_observable) {
77 observable = updated_observable;
80 Operator get_observable() {
return observable; }
82 void update_kernel(std::shared_ptr<CompositeInstruction> updated_kernel) {
83 kernel = updated_kernel;
86 void update_current_iterate_parameters(std::vector<double> x) {
87 current_iterate_parameters = x;
90 virtual void set_options(HeterogeneousMap &opts) { options = opts; }
92 void update_options(
const std::string key, T value) {
93 options.insert(key, value);
99 virtual std::function<
100 std::shared_ptr<CompositeInstruction>(std::vector<double>)>
101 get_kernel_evaluator() {
103 error(
"Illegal call to get_kernel_evaluator().");
108 namespace __internal__ {
110 std::shared_ptr<ObjectiveFunction> get_objective(
const std::string &type);
112 template <
typename T>
113 std::shared_ptr<T> qcor_as_shared(T *t) {
114 return std::shared_ptr<T>(t, [](T *
const) {});
117 template <std::size_t... Is>
118 auto create_tuple_impl(std::index_sequence<Is...>,
119 const std::vector<double> &arguments) {
120 return std::make_tuple(arguments[Is]...);
123 template <std::
size_t N>
124 auto create_tuple(
const std::vector<double> &arguments) {
125 return create_tuple_impl(std::make_index_sequence<N>{}, arguments);
129 std::shared_ptr<ArgsTranslator<qreg, std::vector<double>>> operator()(
130 qreg &q, std::tuple<
qreg, std::vector<double>> &&) {
131 return std::make_shared<ArgsTranslator<qreg, std::vector<double>>>(
132 [&](
const std::vector<double> &x) {
return std::make_tuple(q, x); });
135 template <
typename... DoubleTypes>
137 qreg &q, std::tuple<qreg, DoubleTypes...> &&t) {
138 if constexpr ((std::is_same<DoubleTypes, double>::value && ...)) {
140 [&](
const std::vector<double> &x) {
141 auto qreg_tuple = std::make_tuple(q);
142 auto double_tuple = create_tuple<
sizeof...(DoubleTypes)>(x);
143 return std::tuple_cat(qreg_tuple, double_tuple);
147 "QCOR cannot auto-generate a ArgsTranslator for this "
148 "ObjectiveFunction. Please provide a custom ArgsTranslator to "
149 "createObjectiveFunction.");
151 [&](
const std::vector<double> &x) {
return t; });
158 template <
typename... KernelArgs>
163 std::shared_ptr<CompositeInstruction> create_new_composite() {
165 std::stringstream name_ss;
166 name_ss <<
this <<
"_qkernel";
167 auto _kernel = qcor::__internal__::create_composite(name_ss.str());
172 std::shared_ptr<LocalArgsTranslator> args_translator;
173 std::shared_ptr<ObjectiveFunction> helper;
175 std::shared_ptr<GradientFunction> gradiend_method;
178 std::function<std::shared_ptr<CompositeInstruction>(std::vector<double>)>>
179 lambda_kernel_evaluator;
184 std::shared_ptr<LocalArgsTranslator> translator,
185 std::shared_ptr<ObjectiveFunction> obj_helper,
186 const int dim, HeterogeneousMap opts)
191 args_translator = translator;
195 options.insert(
"observable", observable);
196 helper->update_observable(observable);
197 helper->set_options(options);
204 std::shared_ptr<LocalArgsTranslator> translator,
205 std::shared_ptr<ObjectiveFunction> obj_helper,
206 std::shared_ptr<GradientFunction> grad_calc,
207 const int dim, HeterogeneousMap opts)
210 gradiend_method = grad_calc;
214 std::shared_ptr<ObjectiveFunction> obj_helper,
215 const int dim, HeterogeneousMap opts) {
216 qreg = ::qalloc(obs.nBits());
221 args_translator = auto_gen(
qreg, std::tuple<KernelArgs...>());
226 options.insert(
"observable", observable);
227 helper->update_observable(observable);
228 helper->set_options(options);
232 std::function<
void(std::shared_ptr<CompositeInstruction>, KernelArgs...)>
235 std::shared_ptr<LocalArgsTranslator> translator,
236 std::shared_ptr<ObjectiveFunction> obj_helper,
const int dim,
237 HeterogeneousMap opts)
241 lambda_kernel_evaluator =
243 std::vector<double> x) -> std::shared_ptr<CompositeInstruction> {
247 auto m_kernel = create_new_composite();
248 auto kernel_composite_tuple = std::make_tuple(m_kernel);
251 auto translated_tuple = (*args_translator)(x);
255 std::tuple_cat(kernel_composite_tuple, translated_tuple);
256 std::apply(functor, concatenated);
261 args_translator = translator;
265 options.insert(
"observable", observable);
266 helper->update_observable(observable);
267 helper->set_options(options);
270 void set_options(HeterogeneousMap &opts)
override {
272 helper->set_options(opts);
276 std::function<std::shared_ptr<CompositeInstruction>(std::vector<double>)>
277 get_kernel_evaluator()
override {
280 std::function<std::shared_ptr<CompositeInstruction>(std::vector<double>)>
281 kernel_evaluator = lambda_kernel_evaluator.has_value()
282 ? lambda_kernel_evaluator.value()
283 : [&](std::vector<double> x)
284 -> std::shared_ptr<CompositeInstruction> {
286 void (*kernel_functor)(std::shared_ptr<CompositeInstruction>,
290 kernel_functor =
reinterpret_cast<void (*)(
291 std::shared_ptr<CompositeInstruction>, KernelArgs...)
>(kernel_ptr);
295 auto m_kernel = create_new_composite();
296 auto kernel_composite_tuple = std::make_tuple(m_kernel);
299 auto translated_tuple = (*args_translator)(x);
303 std::tuple_cat(kernel_composite_tuple, translated_tuple);
306 qcor::__internal__::evaluate_function_with_tuple_args(kernel_functor,
311 return kernel_evaluator;
317 std::vector<double> &dx)
override {
321 double operator()(
const std::vector<double> &x,
322 std::vector<double> &dx)
override {
323 current_iterate_parameters = x;
324 helper->update_current_iterate_parameters(x);
327 void (*kernel_functor)(std::shared_ptr<CompositeInstruction>,
331 kernel_functor =
reinterpret_cast<void (*)(
332 std::shared_ptr<CompositeInstruction>, KernelArgs...)
>(kernel_ptr);
337 std::function<std::shared_ptr<CompositeInstruction>(std::vector<double>)>
338 kernel_evaluator = lambda_kernel_evaluator.has_value()
339 ? lambda_kernel_evaluator.value()
340 : [&](std::vector<double> x)
341 -> std::shared_ptr<CompositeInstruction> {
344 auto m_kernel = create_new_composite();
345 auto kernel_composite_tuple = std::make_tuple(m_kernel);
348 auto translated_tuple = (*args_translator)(x);
352 std::tuple_cat(kernel_composite_tuple, translated_tuple);
355 qcor::__internal__::evaluate_function_with_tuple_args(kernel_functor,
357 std::cout << m_kernel->toString() <<
"\n";
362 helper->update_options(
"kernel-evaluator", kernel_evaluator);
365 kernel = kernel_evaluator(x);
366 helper->update_kernel(kernel);
369 const auto input_dx = dx;
371 auto cost_val = (*helper)(
qreg, dx);
376 if (!dx.empty() && !helper->gradients_computed){
377 if (dx.size() != x.size()) {
379 "Dimension mismatched: gradients and parameters vectors have "
383 if (!gradiend_method) {
384 std::string gradient_method_name =
385 qcor::__internal__::DEFAULT_GRADIENT_METHOD;
388 if (options.stringExists(
"gradient-strategy")) {
389 gradient_method_name = options.getString(
"gradient-strategy");
391 gradiend_method = qcor::__internal__::get_gradient_method(
392 gradient_method_name, __internal__::qcor_as_shared(
this),
396 dx = (*gradiend_method)(x, cost_val);
406 const std::string name()
const override {
return "objective-impl"; }
407 const std::string description()
const override {
return ""; }
411 template <
typename... Args>
412 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
413 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
416 HeterogeneousMap &&options = {}) {
417 auto helper = qcor::__internal__::get_objective(
"vqe");
419 auto args_translator = auto_gen(q, std::tuple<Args...>());
421 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
424 kernel_ptr, observable, q, args_translator, helper, nParams, options);
428 template <
typename... Args>
429 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
430 const std::string obj_name,
431 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
433 Operator observable,
qreg &q,
const int nParams,
434 HeterogeneousMap &&options = {}) {
435 auto helper = qcor::__internal__::get_objective(obj_name);
436 __internal__::ArgsTranslatorAutoGenerator auto_gen;
437 auto args_translator = auto_gen(q, std::tuple<Args...>());
439 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
441 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
442 kernel_ptr, observable, q, args_translator, helper, nParams, options);
446 template <
typename... Args>
447 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
448 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
450 qreg &q,
const int nParams, HeterogeneousMap &&options = {}) {
451 auto helper = qcor::__internal__::get_objective(
"vqe");
452 __internal__::ArgsTranslatorAutoGenerator auto_gen;
453 auto args_translator = auto_gen(q, std::tuple<Args...>());
455 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
460 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
461 kernel_ptr, allZs(q.size()), q, args_translator, helper, nParams,
466 template <
typename... Args>
467 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
468 const std::string obj_name,
469 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
471 qreg &q,
const int nParams, HeterogeneousMap &&options = {}) {
472 auto helper = qcor::__internal__::get_objective(obj_name);
473 __internal__::ArgsTranslatorAutoGenerator auto_gen;
474 auto args_translator = auto_gen(q, std::tuple<Args...>());
476 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
478 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
479 kernel_ptr, allZs(q.size()), q, args_translator, helper, nParams,
485 template <
typename... Args>
486 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
487 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
489 Operator &observable,
490 std::shared_ptr<ArgsTranslator<Args...>> args_translator,
qreg &q,
491 const int nParams, HeterogeneousMap &&options = {}) {
492 auto helper = qcor::__internal__::get_objective(
"vqe");
494 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
496 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
497 kernel_ptr, observable, q, args_translator, helper, nParams, options);
500 template <
typename... Args>
501 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
502 const std::string obj_name,
503 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
505 Operator &observable,
506 std::shared_ptr<ArgsTranslator<Args...>> args_translator,
qreg &q,
507 const int nParams, HeterogeneousMap &&options = {}) {
508 auto helper = qcor::__internal__::get_objective(obj_name);
510 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
512 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
513 kernel_ptr, observable, q, args_translator, helper, nParams, options);
516 template <
typename... Args>
517 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
518 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
520 std::shared_ptr<ArgsTranslator<Args...>> args_translator,
qreg &q,
521 const int nParams, HeterogeneousMap &&options = {}) {
522 auto helper = qcor::__internal__::get_objective(
"vqe");
524 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
529 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
530 kernel_ptr, allZs(q.size()), q, args_translator, helper, nParams,
534 template <
typename... Args>
535 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
536 const std::string obj_name,
537 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
539 std::shared_ptr<ArgsTranslator<Args...>> args_translator,
qreg &q,
540 const int nParams, HeterogeneousMap &&options = {}) {
541 auto helper = qcor::__internal__::get_objective(obj_name);
543 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
545 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
546 kernel_ptr, allZs(q.size()), q, args_translator, helper, nParams,
552 template <
typename... Args>
553 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
554 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
556 Operator &observable,
const int nParams, HeterogeneousMap &&options = {}) {
557 auto helper = qcor::__internal__::get_objective(
"vqe");
559 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
561 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
562 kernel_ptr, observable, helper, nParams, options);
565 template <
typename... Args>
566 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
567 const std::string obj_name,
568 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
570 Operator &observable,
const int nParams, HeterogeneousMap &&options = {}) {
571 auto helper = qcor::__internal__::get_objective(obj_name);
573 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
575 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
576 kernel_ptr, observable, helper, nParams, options);
579 template <
typename... CaptureArgs,
typename... Args>
580 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
581 _qpu_lambda<CaptureArgs...> &lambda,
582 std::shared_ptr<ArgsTranslator<Args...>> args_translator,
583 Operator &observable,
qreg &q,
const int nParams,
584 HeterogeneousMap &&options = {}) {
585 auto helper = qcor::__internal__::get_objective(
"vqe");
586 std::function<void(std::shared_ptr<CompositeInstruction>, Args...)>
587 kernel_fn = [&lambda](std::shared_ptr<CompositeInstruction> comp,
588 Args... args) ->
void {
589 return lambda.eval_with_parent(comp, args...);
592 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
593 kernel_fn, observable, q, args_translator, helper, nParams, options);
598 template <
typename... CaptureArgs>
599 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
600 _qpu_lambda<CaptureArgs...> &lambda, Operator &observable,
qreg &q,
601 const int nParams, HeterogeneousMap &&options = {}) {
602 if (lambda.var_type ==
603 _qpu_lambda<CaptureArgs...>::Variational_Arg_Type::None) {
605 "qpu_lambda has an incompatible signature. Please provide an "
608 auto helper = qcor::__internal__::get_objective(
"vqe");
609 std::function<void(std::shared_ptr<CompositeInstruction>,
qreg,
610 std::vector<double>)>
611 kernel_fn = [&lambda](std::shared_ptr<CompositeInstruction> comp,
qreg q,
612 std::vector<double> params) ->
void {
613 if (lambda.var_type ==
614 _qpu_lambda<CaptureArgs...>::Variational_Arg_Type::Vec_Double) {
615 return lambda.eval_with_parent(comp, q, params);
617 if (lambda.var_type ==
618 _qpu_lambda<CaptureArgs...>::Variational_Arg_Type::Double) {
619 if (params.size() != 1) {
620 error(
"Invalid number of parameters. Expected 1, got " +
621 std::to_string(params.size()));
623 return lambda.eval_with_parent(comp, q, params[0]);
625 error(
"Internal error: invalid qpu lambda type encountered.");
628 auto args_translator =
629 std::make_shared<ArgsTranslator<qreg, std::vector<double>>>(
630 [&](
const std::vector<double> x) {
return std::make_tuple(q, x); });
632 return std::make_shared<ObjectiveFunctionImpl<qreg, std::vector<double>>>(
633 kernel_fn, observable, q, args_translator, helper, nParams, options);
638 template <
typename... Args>
639 std::shared_ptr<ObjectiveFunction> createObjectiveFunction(
640 void (*quantum_kernel_functor)(std::shared_ptr<CompositeInstruction>,
642 Operator &observable,
qreg &q,
const int nParams,
643 std::shared_ptr<GradientFunction> gradient_method,
644 HeterogeneousMap &&options = {}) {
645 auto helper = qcor::__internal__::get_objective(
"vqe");
646 __internal__::ArgsTranslatorAutoGenerator auto_gen;
647 auto args_translator = auto_gen(q, std::tuple<Args...>());
649 void *kernel_ptr =
reinterpret_cast<void *
>(quantum_kernel_functor);
651 return std::make_shared<ObjectiveFunctionImpl<Args...>>(
652 kernel_ptr, observable, q, args_translator, helper, gradient_method,