Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/source/_static/examples/python/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@
print(f"Qubits: {len(circuit_json['qubits'])}")

# 5. Resource estimation
estimate_result = circuit.estimate()
formatted = estimate_result["physicalCountsFormatted"]
print(f"Physical qubits: {formatted['physicalQubits']}")
print(f"Runtime: {formatted['runtime']}")
from qdk_chemistry.algorithms import create as create_algorithm

estimator = create_algorithm("resource_estimator")
estimate_result = estimator.run(circuit)
print(f"Physical qubits: {estimate_result.physical_counts.physical_qubits}")
print(f"Runtime: {estimate_result.physical_counts.runtime}")
# end-cell-qsharp-workflow
################################################################################

Expand Down
2 changes: 0 additions & 2 deletions docs/source/user/comprehensive/data/circuit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@ Each method returns the circuit in the requested format, converting from whateve
- Returns the OpenQASM string. Converts from :term:`QIR` via Qiskit if only :term:`QIR` is available.
* - :meth:`~qdk_chemistry.data.Circuit.get_qiskit_circuit`
- Returns a Qiskit ``QuantumCircuit``. Requires ``qiskit`` to be installed.
* - :meth:`~qdk_chemistry.data.Circuit.estimate`
- Runs Q#'s resource estimator on the circuit. Accepts optional ``params`` for estimation configuration.

Example: Convert state preparation circuits to different formats
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
10 changes: 7 additions & 3 deletions examples/qpe_stretched_n2.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"source": [
"from qdk_chemistry.data import Structure\n",
"\n",
"# Stretched N2 structure at 1.270025 Å bond length\n",
"# Stretched N2 structure at 1.270025 \u00c5 bond length\n",
"structure = Structure.from_xyz_file(Path(\"data/stretched_n2.structure.xyz\"))"
]
},
Expand Down Expand Up @@ -334,7 +334,9 @@
"\n",
"# Print logical qubit counts estimated from the circuit\n",
"df = pd.DataFrame(\n",
" regular_isometry_circuit.estimate().logical_counts.items(),\n",
"from qdk_chemistry.algorithms import create as create_algorithm\n",
"estimator = create_algorithm('resource_estimator')\n",
" estimator.run(regular_isometry_circuit).logical_counts.items(),\n",
" columns=['Logical Estimate', 'Counts']\n",
")\n",
"display(df)"
Expand Down Expand Up @@ -367,7 +369,9 @@
"\n",
"# Print logical qubit counts estimated from the circuit\n",
"df = pd.DataFrame(\n",
" sparse_isometry_circuit.estimate().logical_counts.items(),\n",
"from qdk_chemistry.algorithms import create as create_algorithm\n",
"estimator = create_algorithm('resource_estimator')\n",
" estimator.run(sparse_isometry_circuit).logical_counts.items(),\n",
" columns=['Logical Estimate', 'Counts']\n",
")\n",
"display(df)"
Expand Down
918 changes: 461 additions & 457 deletions examples/state_prep_energy.ipynb

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions python/src/qdk_chemistry/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
)
from qdk_chemistry.algorithms.qubit_hamiltonian_solver import QubitHamiltonianSolver
from qdk_chemistry.algorithms.qubit_mapper import QdkQubitMapper, QubitMapper
from qdk_chemistry.algorithms.resource_estimator.base import ResourceEstimator
from qdk_chemistry.algorithms.resource_estimator.qdk import QdkQreV1
from qdk_chemistry.algorithms.scf_solver import QdkScfSolver, ScfSolver
from qdk_chemistry.algorithms.stability_checker import QdkStabilityChecker, StabilityChecker
from qdk_chemistry.algorithms.state_preparation import StatePreparation
Expand Down Expand Up @@ -79,13 +81,15 @@
"QdkMacisPmc",
"QdkOccupationActiveSpaceSelector",
"QdkPipekMezeyLocalizer",
"QdkQreV1",
"QdkQubitMapper",
"QdkScfSolver",
"QdkStabilityChecker",
"QdkVVHVLocalizer",
"QdkValenceActiveSpaceSelector",
"QubitHamiltonianSolver",
"QubitMapper",
"ResourceEstimator",
"ScfSolver",
"StabilityChecker",
"StatePreparation",
Expand Down
4 changes: 4 additions & 0 deletions python/src/qdk_chemistry/algorithms/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ def _register_python_factories():
from qdk_chemistry.algorithms.phase_estimation import PhaseEstimationFactory # noqa: PLC0415
from qdk_chemistry.algorithms.qubit_hamiltonian_solver import QubitHamiltonianSolverFactory # noqa: PLC0415
from qdk_chemistry.algorithms.qubit_mapper import QubitMapperFactory # noqa: PLC0415
from qdk_chemistry.algorithms.resource_estimator import ResourceEstimatorFactory # noqa: PLC0415
from qdk_chemistry.algorithms.state_preparation import StatePreparationFactory # noqa: PLC0415
from qdk_chemistry.algorithms.time_evolution.builder import TimeEvolutionBuilderFactory # noqa: PLC0415
from qdk_chemistry.algorithms.time_evolution.controlled_circuit_mapper import ( # noqa: PLC0415
Expand All @@ -523,6 +524,7 @@ def _register_python_factories():
register_factory(ControlledEvolutionCircuitMapperFactory())
register_factory(CircuitExecutorFactory())
register_factory(PhaseEstimationFactory())
register_factory(ResourceEstimatorFactory())


_ = _register_cpp_factories()
Expand Down Expand Up @@ -584,6 +586,7 @@ def _register_python_algorithms():
)
from qdk_chemistry.algorithms.qubit_hamiltonian_solver import DenseMatrixSolver, SparseMatrixSolver # noqa: PLC0415
from qdk_chemistry.algorithms.qubit_mapper import QdkQubitMapper # noqa: PLC0415
from qdk_chemistry.algorithms.resource_estimator.qdk import QdkQreV1 # noqa: PLC0415
from qdk_chemistry.algorithms.state_preparation import SparseIsometryGF2XStatePreparation # noqa: PLC0415
from qdk_chemistry.algorithms.time_evolution.builder.partially_randomized import ( # noqa: PLC0415
PartiallyRandomized,
Expand All @@ -610,6 +613,7 @@ def _register_python_algorithms():
register(lambda: QdkFullStateSimulator())
register(lambda: QdkSparseStateSimulator())
register(lambda: IterativePhaseEstimation())
register(lambda: QdkQreV1())


_register_python_algorithms()
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""QDK/Chemistry resource estimator module.

This module provides algorithm support for quantum resource estimation.
"""

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from .base import ResourceEstimatorFactory

__all__: list[str] = ["ResourceEstimatorFactory"]
63 changes: 63 additions & 0 deletions python/src/qdk_chemistry/algorithms/resource_estimator/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""QDK/Chemistry resource estimator abstractions.

This module defines the abstract base class for resource estimator algorithms
that estimate quantum resources required to execute a circuit.
"""

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from abc import abstractmethod

from qdk_chemistry.algorithms.base import Algorithm, AlgorithmFactory
from qdk_chemistry.data import Circuit
from qdk_chemistry.data.resource_estimator_data import ResourceEstimatorData

__all__: list[str] = ["ResourceEstimator", "ResourceEstimatorFactory"]


class ResourceEstimator(Algorithm):
"""Abstract base class for quantum resource estimator algorithms."""

def __init__(self):
"""Initialize the ResourceEstimator with default settings."""
super().__init__()

def type_name(self) -> str:
"""Return the algorithm type name as resource_estimator."""
return "resource_estimator"

@abstractmethod
def _run_impl(
self,
circuit: Circuit,
) -> ResourceEstimatorData:
"""Estimate the quantum resources required for the given circuit.

Estimation parameters are provided via ``self.settings()``.

Args:
circuit: The quantum circuit to estimate resources for.

Returns:
ResourceEstimatorData: The estimated resources.

"""


class ResourceEstimatorFactory(AlgorithmFactory):
"""Factory class for creating ResourceEstimator instances."""

def __init__(self):
"""Initialize the ResourceEstimatorFactory."""
super().__init__()

def algorithm_type_name(self) -> str:
"""Return the algorithm type name as resource_estimator."""
return "resource_estimator"

def default_algorithm_name(self) -> str:
"""Return the qdk_qre_v1 as default algorithm name."""
return "qdk_qre_v1"
150 changes: 150 additions & 0 deletions python/src/qdk_chemistry/algorithms/resource_estimator/qdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
"""QDK/Chemistry Resource Estimator implementation using QDK.

This module provides a ResourceEstimator implementation that uses the QDK
resource estimation backend to estimate quantum resources required for a circuit.
It supports circuits provided as Q# factory data or QASM.
"""

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import qsharp
import qsharp.estimator
import qsharp.openqasm

from qdk_chemistry.algorithms.resource_estimator.base import ResourceEstimator
from qdk_chemistry.data import Circuit, Settings
from qdk_chemistry.data.resource_estimator_data import (
ErrorBudget,
EstimationConfig,
LogicalCounts,
LogicalQubit,
PhysicalCounts,
ResourceEstimatorData,
)
from qdk_chemistry.utils import Logger

__all__: list[str] = ["QdkQreV1", "QdkQreV1Settings"]


class QdkQreV1Settings(Settings):
"""Settings for the QDK QRE v1 Resource Estimator."""

def __init__(self) -> None:
"""Initialize QDK QRE v1 settings."""
Logger.trace_entering()
super().__init__()
self._set_default(
"error_budget", "double", 0.001,
"Total error budget for the estimation"
)


class QdkQreV1(ResourceEstimator):
"""QDK QRE v1 Resource Estimator algorithm implementation.

Uses the Q# resource estimator (v1/v2 API) to estimate physical
resources required for a quantum circuit.
"""

def __init__(self) -> None:
"""Initialize the QDK QRE v1 Resource Estimator."""
Logger.trace_entering()
super().__init__()
self._settings = QdkQreV1Settings()

def name(self) -> str:
"""Return the algorithm name as qdk_qre_v1."""
return "qdk_qre_v1"

def _run_impl(
self,
circuit: Circuit,
) -> ResourceEstimatorData:
"""Estimate the quantum resources required for the given circuit.

Estimation parameters are taken from ``self.settings()``.

Args:
circuit: The quantum circuit to estimate resources for.

Returns:
ResourceEstimatorData: The estimated resources.

Raises:
RuntimeError: If no suitable circuit representation is available for estimation.

"""
Logger.trace_entering()

params = {"errorBudget": self._settings.get("error_budget")}

if circuit._qsharp_factory is not None:
result = qsharp.estimate(
circuit._qsharp_factory.program,
params,
*circuit._qsharp_factory.parameter.values(),
)
elif circuit.qasm is not None:
result = qsharp.openqasm.estimate(circuit.qasm, params)
else:
raise RuntimeError(
"Cannot estimate resources: no Q# factory data or QASM representation is available."
)

# Convert the EstimatorResult (dict subclass) to typed data
raw = dict(result) if isinstance(result, dict) else result

lc_raw = raw.get("logicalCounts", {})
pc_raw = raw.get("physicalCounts", {})
bd_raw = pc_raw.get("breakdown", {})
lq_raw = raw.get("logicalQubit", {})
eb_raw = raw.get("errorBudget", {})
jp_raw = raw.get("jobParams", {})

# Build provenance config from jobParams
qp = jp_raw.get("qubitParams", {})
qec = jp_raw.get("qecScheme", {})
config = EstimationConfig(
qubit_model=qp.get("name", qp.get("instructionSet", "")),
qec_scheme=qec.get("name", ""),
error_budget=float(jp_raw.get("errorBudget", 0.0)),
)

return ResourceEstimatorData(
logical_counts=LogicalCounts(
num_qubits=lc_raw.get("numQubits", 0),
t_count=lc_raw.get("tCount", 0),
rotation_count=lc_raw.get("rotationCount", 0),
rotation_depth=lc_raw.get("rotationDepth", 0),
ccz_count=lc_raw.get("cczCount", 0),
ccix_count=lc_raw.get("ccixCount", 0),
measurement_count=lc_raw.get("measurementCount", 0),
),
physical_counts=PhysicalCounts(
physical_qubits=pc_raw.get("physicalQubits", 0),
runtime=pc_raw.get("runtime", 0),
runtime_unit="ns",
rqops=pc_raw.get("rqops", 0),
algorithm_qubits=bd_raw.get("physicalQubitsForAlgorithm", 0),
factory_qubits=bd_raw.get("physicalQubitsForTfactories", 0),
algorithmic_logical_depth=bd_raw.get("algorithmicLogicalDepth", 0),
logical_depth=bd_raw.get("logicalDepth", 0),
),
logical_qubit=LogicalQubit(
code_distance=lq_raw.get("codeDistance", 0),
logical_cycle_time=lq_raw.get("logicalCycleTime", 0),
logical_error_rate=lq_raw.get("logicalErrorRate", 0.0),
physical_qubits=lq_raw.get("physicalQubits", 0),
),
error_budget=ErrorBudget(
logical=eb_raw.get("logical", 0.0),
rotations=eb_raw.get("rotations", 0.0),
tstates=eb_raw.get("tstates", 0.0),
),
estimator=self.name(),
status=raw.get("status", "unknown"),
error=eb_raw.get("logical", 0.0) + eb_raw.get("rotations", 0.0) + eb_raw.get("tstates", 0.0),
config=config,
)
16 changes: 16 additions & 0 deletions python/src/qdk_chemistry/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@
from qdk_chemistry.data.noise_models import QuantumErrorProfile
from qdk_chemistry.data.qpe_result import QpeResult
from qdk_chemistry.data.qubit_hamiltonian import QubitHamiltonian
from qdk_chemistry.data.resource_estimator_data import (
CircuitCounts,
ErrorBudget,
EstimationConfig,
LogicalCounts,
LogicalQubit,
PhysicalCounts,
ResourceEstimatorData,
)
from qdk_chemistry.data.symmetries import Symmetries
from qdk_chemistry.data.time_evolution.base import TimeEvolutionUnitary
from qdk_chemistry.data.time_evolution.containers.base import TimeEvolutionUnitaryContainer
Expand All @@ -134,6 +143,7 @@
"CasWavefunctionContainer",
"CholeskyHamiltonianContainer",
"Circuit",
"CircuitCounts",
"CircuitExecutorData",
"Configuration",
"ConfigurationSet",
Expand All @@ -144,11 +154,15 @@
"Element",
"EncodingMismatchError",
"EnergyExpectationResult",
"ErrorBudget",
"EstimationConfig",
"FermionModeOrder",
"Hamiltonian",
"HamiltonianContainer",
"HamiltonianType",
"LatticeGraph",
"LogicalCounts",
"LogicalQubit",
"MP2Container",
"MeasurementData",
"ModelOrbitals",
Expand All @@ -157,9 +171,11 @@
"PauliOperator",
"PauliProductFormulaContainer",
"PauliTermAccumulator",
"PhysicalCounts",
"QpeResult",
"QuantumErrorProfile",
"QubitHamiltonian",
"ResourceEstimatorData",
"SciWavefunctionContainer",
"SettingNotFound",
"SettingNotFoundError",
Expand Down
Loading
Loading