Skip to content
Closed
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
99 changes: 99 additions & 0 deletions samples/qre/dollar_cost.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "89fb2f00",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>qubits</th>\n",
" <th>runtime</th>\n",
" <th>error</th>\n",
" <th>USD cost</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>32110</td>\n",
" <td>0 days 00:00:00.122500</td>\n",
" <td>0.003948</td>\n",
" <td>122.5</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" qubits runtime error USD cost\n",
"0 32110 0 days 00:00:00.122500 0.003948 122.5"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from qdk.estimator import LogicalCounts\n",
"from qdk.qre import estimate\n",
"from qdk.qre.application import QSharpApplication\n",
"from qdk.qre.models import GateBased, RoundBasedFactory, SurfaceCode\n",
"\n",
"app = QSharpApplication(LogicalCounts({\"numQubits\": 100, \"tCount\": 10000, \"cczCount\": 1000, \"measurementCount\": 1000}))\n",
"architecture = GateBased(gate_time=50, measurement_time=1000)\n",
"architecture.usd_cost_per_hour = 3_600.0 * 1000\n",
"\n",
"results = estimate(\n",
" app,\n",
" architecture,\n",
" SurfaceCode.q() * RoundBasedFactory.q(),\n",
" max_error=0.01,\n",
")\n",
"results.as_frame()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
26 changes: 26 additions & 0 deletions source/qdk_package/qdk/qre/_architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class Architecture(ABC):

family: TechnologyFamily = TechnologyFamily.UNKNOWN

# Cost of running application, in USD per hour.
# If defined, it's assumed that the cost of running application is proportional to
# the runtime.
usd_cost_per_hour: float | None = None

@abstractmethod
def provided_isa(self, ctx: ISAContext) -> ISA:
"""
Expand All @@ -63,6 +68,26 @@ def context(self) -> ISAContext:
"""
return ISAContext(self)

def cost_usd(self, qubits: int, runtime_nanos: int) -> float | None:
Comment thread
fedimser marked this conversation as resolved.
"""Estimates cost, in US dollars, of running an application.

Subclasses need to define `usd_cost_per_hour` (if USD cost is proportional to
runtime) or override `cost_usd`.

Args:
qubits: estimated number of qubits.
runtime_nanos: estimated runtime, in nanoseconds.

Returns:
If there is not enough information to estimate cost, returns None.
If application cannot be run on this architecture, returns Infinity.
Otherwise, returns estimated cost of running an application, in dollars.
"""
if self.usd_cost_per_hour is not None:
runtime_hours = runtime_nanos / (3600 * 1e9)
return runtime_hours * self.usd_cost_per_hour
return None

@property
def assumptions(self) -> list[str]:
"""
Expand Down Expand Up @@ -233,6 +258,7 @@ def __init__(self, arch: Architecture):

self._bindings: dict[str, ISA] = {}
self._transforms: dict[int, Architecture | ISATransform] = {0: arch}
self.arch = arch

def _with_binding(self, name: str, isa: ISA) -> ISAContext:
"""Return a new context with an additional binding (internal use)."""
Expand Down
7 changes: 7 additions & 0 deletions source/qdk_package/qdk/qre/_estimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,13 @@ def run_estimation(traces: list[Trace]):
EstimationTableEntry.from_result(result, arch_ctx) for result in collection
)

if any(e.cost_usd is not None for e in table):
table.add_column(
"USD cost",
lambda entry: entry.cost_usd,
formatter=lambda x: round(x, 2) if x is not None else None,
)

# Fill in the stats for this estimation run
table.stats.num_traces = num_traces
table.stats.num_isas = num_isas
Expand Down
3 changes: 3 additions & 0 deletions source/qdk_package/qdk/qre/_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class EstimationTableEntry:
and the number of copies required.
properties: Additional key-value properties attached to the
estimation result.
cost_usd: Cost of running application, in US dollars.
"""

qubits: int
Expand All @@ -221,6 +222,7 @@ class EstimationTableEntry:
source: InstructionSource
factories: dict[int, FactoryResult] = field(default_factory=dict)
properties: dict[int, int | float | bool | str] = field(default_factory=dict)
cost_usd: float | None = None
Comment thread
fedimser marked this conversation as resolved.

@classmethod
def from_result(
Expand All @@ -242,6 +244,7 @@ def from_result(
source=InstructionSource.from_isa(ctx, result.isa),
factories=result.factories.copy(),
properties=result.properties.copy(),
cost_usd=ctx.arch.cost_usd(result.qubits, result.runtime),
)


Expand Down
18 changes: 18 additions & 0 deletions source/qdk_package/tests/qre/test_estimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,24 @@ def test_estimation_max_error():
assert next(iter(results)).error <= max_error


def test_estimation_includes_usd_cost():
app = QSharpApplication(LogicalCounts({"numQubits": 1, "measurementCount": 1}))
arch = GateBased(gate_time=50, measurement_time=100)
arch.usd_cost_per_hour = 2 * 3600 * 1e9 # 2 USD per nanosecond.

results = estimate(
app,
arch,
SurfaceCode.q() * ExampleFactory.q(),
PSSPC.q() * LatticeSurgery.q(),
)
assert results[0].runtime == 1050

frame = results.as_frame()
assert "USD cost" in frame.columns
assert frame["USD cost"].tolist() == [2100.0]


@pytest.mark.skipif(
"SLOW_TESTS" not in os.environ,
reason="turn on slow tests by setting SLOW_TESTS=1 in the environment",
Expand Down