Source code for iqm.qdmi.estimator
# Copyright (c) 2025 - 2026 IQM Finland Oy
# All rights reserved.
#
# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://github.com/iqm-finland/QDMI-on-IQM/blob/main/LICENSE
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""Command line interface for VQE parameter estimation on IQM devices."""
from __future__ import annotations
import argparse
import base64
import pickle # noqa: S403
from pathlib import Path
from typing import TYPE_CHECKING
try:
from mqt.core.plugins.qiskit.estimator import QDMIEstimator
from mqt.core.plugins.qiskit.provider import QDMIProvider
from qiskit import qpy
from qiskit_algorithms import VQE
from qiskit_algorithms.optimizers import L_BFGS_B
except ImportError as e:
msg = (
"Failed to import Qiskit plugin and VQE requirements. "
"Ensure that `iqm-qdmi` is installed with the `qiskit` extra, e.g., via `uv pip install iqm-qdmi[qiskit]`."
)
raise ImportError(msg) from e
from iqm.qdmi.qiskit import IQMBackend
if TYPE_CHECKING:
from collections.abc import Sequence
from qiskit.quantum_info import SparsePauliOp
[docs]
def main(argv: Sequence[str] | None = None) -> None:
"""Execute the estimator CLI subcommand."""
parser = argparse.ArgumentParser(description="Estimate VQE parameters for a serialized ansatz.")
parser.add_argument("circuit", type=str, help="Path to the QPY circuit file.")
parser.add_argument("observable", type=str, help="Path to the serialized observable file.")
parser.add_argument("--maxiter", type=int, required=True, help="Maximum number of iterations.")
parser.add_argument("--timeout", type=int, help="Timeout passed to the IQM Backend in seconds.", default=300)
parser.add_argument("--simulator", action="store_true", help="Use the simulator instead of the actual backend.")
parser.add_argument("--base-url", type=str, dest="base_url", help="IQM server base URL.", default=None)
parser.add_argument("--tokens-file", type=str, help="IQM tokens file for authentication.", default=None)
parser.add_argument("--qc-id", type=str, dest="qc_id", help="Quantum computer ID to use.", default=None)
parser.add_argument(
"--qc-alias",
type=str,
dest="qc_alias",
help="Quantum computer alias to use (e.g., 'emerald:mock').",
default=None,
)
args = parser.parse_args(list(argv) if argv is not None else None)
with Path(args.circuit).open("rb") as file_obj:
ansatz = qpy.load(file_obj)[0]
with Path(args.observable).open("rb") as file_obj:
observable: SparsePauliOp = pickle.load(file_obj) # noqa: S301
if args.simulator:
backend = QDMIProvider().get_backend("MQT Core DDSIM QDMI Device")
estimator = QDMIEstimator(backend)
else:
backend = IQMBackend(
base_url=args.base_url,
tokens_file=args.tokens_file,
qc_id=args.qc_id,
qc_alias=args.qc_alias,
)
estimator = backend.estimator()
vqe = VQE(estimator, ansatz, L_BFGS_B(maxiter=args.maxiter))
result = vqe.compute_minimum_eigenvalue(operator=observable)
pickled = pickle.dumps(result)
print(base64.b64encode(pickled).decode("utf-8"))
if __name__ == "__main__":
main()