Skip to content

Commit 96feb1e

Browse files
committed
chore(compare): enhance formatting of compare results
1 parent f1424c6 commit 96feb1e

6 files changed

Lines changed: 1232 additions & 468 deletions

File tree

examples/00_getting_started.ipynb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@
467467
"# List all runs in our project\n",
468468
"print(\"Runs in project:\")\n",
469469
"for run_info in list_runs(project=PROJECT):\n",
470-
" print(f\" {run_info['run_id'][:12]}... ({run_info.get('run_name', 'unnamed')})\")"
470+
" print(f\" {run_info['run_id'][:12]}... ({run_info.get('run_name', 'unnamed')})\")"
471471
]
472472
},
473473
{
@@ -543,7 +543,7 @@
543543
" }\n",
544544
" )\n",
545545
"\n",
546-
" print(f\" lr={lr:.2f} loss={history[-1]:.6f}\")\n",
546+
" print(f\" lr={lr:.2f} => loss={history[-1]:.6f}\")\n",
547547
"\n",
548548
"# Find the best\n",
549549
"best = min(sweep_results, key=lambda x: x[\"final_loss\"])\n",
@@ -571,7 +571,7 @@
571571
" rec = load_run(run_info[\"run_id\"])\n",
572572
" lr = rec.params.get(\"learning_rate\", \"?\")\n",
573573
" loss = rec.metrics.get(\"final_loss\", \"?\")\n",
574-
" print(f\" lr={lr} loss={loss:.6f}\")"
574+
" print(f\" lr={lr} loss={loss:.6f}\")"
575575
]
576576
},
577577
{
@@ -636,9 +636,6 @@
636636
"source": [
637637
"# Compare baseline vs candidate\n",
638638
"comparison = diff(first_run_id, candidate_id)\n",
639-
"\n",
640-
"print(\"Baseline vs Candidate Comparison\")\n",
641-
"print(\"=\" * 50)\n",
642639
"print(comparison)"
643640
]
644641
},
@@ -755,7 +752,7 @@
755752
"if not result.ok:\n",
756753
" print(\"\\nFailures:\")\n",
757754
" for failure in result.failures:\n",
758-
" print(f\" {failure}\")"
755+
" print(f\" {failure}\")"
759756
]
760757
},
761758
{
@@ -841,7 +838,7 @@
841838
" print(\"=\" * 40)\n",
842839
" print(f\"Run ID: {bundle.run_id}\")\n",
843840
" print(f\"Project: {bundle.get_project()}\")\n",
844-
" print(f\"Objects: {len(bundle.list_objects())}\")"
841+
" print(f\"Objects: {len(bundle.list_objects)}\")"
845842
]
846843
},
847844
{

examples/05_drift_detection_runtime.ipynb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -654,11 +654,7 @@
654654
"source": [
655655
"# Compare baseline vs. last candidate\n",
656656
"last_candidate_id = candidate_ids[-1][\"run_id\"]\n",
657-
"\n",
658657
"comparison = diff(baseline_id, last_candidate_id)\n",
659-
"\n",
660-
"print(f\"Baseline vs. {candidate_ids[-1]['name']}\")\n",
661-
"print(\"=\" * 50)\n",
662658
"print(comparison)"
663659
]
664660
},
@@ -762,7 +758,15 @@
762758
"name": "python3"
763759
},
764760
"language_info": {
761+
"codemirror_mode": {
762+
"name": "ipython",
763+
"version": 3
764+
},
765+
"file_extension": ".py",
766+
"mimetype": "text/x-python",
765767
"name": "python",
768+
"nbconvert_exporter": "python",
769+
"pygments_lexer": "ipython3",
766770
"version": "3.12.3"
767771
}
768772
},

packages/devqubit-engine/src/devqubit_engine/circuit/summary.py

Lines changed: 114 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class CircuitSummary:
7878

7979
def to_dict(self) -> dict[str, Any]:
8080
"""
81-
Convert to dictionary.
81+
Convert to dictionary for serialization.
8282
8383
Returns
8484
-------
@@ -94,7 +94,7 @@ def to_dict(self) -> dict[str, Any]:
9494
"gate_count_multi": self.gate_count_multi,
9595
"gate_count_measure": self.gate_count_measure,
9696
"gate_count_total": self.gate_count_total,
97-
"gate_types": self.gate_types,
97+
"gate_types": self.gate_types.copy(),
9898
"has_parameters": self.has_parameters,
9999
"parameter_count": self.parameter_count,
100100
"is_clifford": self.is_clifford,
@@ -103,13 +103,13 @@ def to_dict(self) -> dict[str, Any]:
103103
}
104104

105105
@classmethod
106-
def from_dict(cls, d: dict[str, Any]) -> CircuitSummary:
106+
def from_dict(cls, data: dict[str, Any]) -> CircuitSummary:
107107
"""
108-
Create from dictionary.
108+
Create instance from dictionary.
109109
110110
Parameters
111111
----------
112-
d : dict
112+
data : dict
113113
Dictionary with summary fields.
114114
115115
Returns
@@ -118,24 +118,24 @@ def from_dict(cls, d: dict[str, Any]) -> CircuitSummary:
118118
Reconstructed summary.
119119
"""
120120
return cls(
121-
num_qubits=int(d.get("num_qubits", 0)),
122-
num_clbits=int(d.get("num_clbits", 0)),
123-
depth=int(d.get("depth", 0)),
124-
gate_count_1q=int(d.get("gate_count_1q", 0)),
125-
gate_count_2q=int(d.get("gate_count_2q", 0)),
126-
gate_count_multi=int(d.get("gate_count_multi", 0)),
127-
gate_count_measure=int(d.get("gate_count_measure", 0)),
128-
gate_count_total=int(d.get("gate_count_total", 0)),
129-
gate_types=dict(d.get("gate_types", {})),
130-
has_parameters=bool(d.get("has_parameters", False)),
131-
parameter_count=int(d.get("parameter_count", 0)),
132-
is_clifford=d.get("is_clifford"),
133-
source_format=CircuitFormat(d.get("source_format", "unknown")),
134-
sdk=SDK(d.get("sdk", "unknown")),
121+
num_qubits=int(data.get("num_qubits", 0)),
122+
num_clbits=int(data.get("num_clbits", 0)),
123+
depth=int(data.get("depth", 0)),
124+
gate_count_1q=int(data.get("gate_count_1q", 0)),
125+
gate_count_2q=int(data.get("gate_count_2q", 0)),
126+
gate_count_multi=int(data.get("gate_count_multi", 0)),
127+
gate_count_measure=int(data.get("gate_count_measure", 0)),
128+
gate_count_total=int(data.get("gate_count_total", 0)),
129+
gate_types=dict(data.get("gate_types", {})),
130+
has_parameters=bool(data.get("has_parameters", False)),
131+
parameter_count=int(data.get("parameter_count", 0)),
132+
is_clifford=data.get("is_clifford"),
133+
source_format=CircuitFormat(data.get("source_format", "unknown")),
134+
sdk=SDK(data.get("sdk", "unknown")),
135135
)
136136

137137
def __repr__(self) -> str:
138-
"""Return string representation."""
138+
"""Return concise string representation."""
139139
return (
140140
f"CircuitSummary(qubits={self.num_qubits}, depth={self.depth}, "
141141
f"gates={self.gate_count_total}, sdk={self.sdk.value})"
@@ -172,7 +172,7 @@ class CircuitDiff:
172172

173173
def to_dict(self) -> dict[str, Any]:
174174
"""
175-
Convert to dictionary.
175+
Convert to dictionary for serialization.
176176
177177
Returns
178178
-------
@@ -181,14 +181,14 @@ def to_dict(self) -> dict[str, Any]:
181181
"""
182182
return {
183183
"match": self.match,
184-
"changes": self.changes,
185-
"metrics": self.metrics,
184+
"changes": self.changes.copy(),
185+
"metrics": self.metrics.copy(),
186186
"summary_a": self.summary_a.to_dict(),
187187
"summary_b": self.summary_b.to_dict(),
188188
}
189189

190190
def __repr__(self) -> str:
191-
"""Return string representation."""
191+
"""Return concise string representation."""
192192
status = "match" if self.match else f"{len(self.changes)} changes"
193193
return f"CircuitDiff({status})"
194194

@@ -241,7 +241,7 @@ def summarize_circuit_data(data: CircuitData) -> CircuitSummary:
241241

242242
def summarize_circuit(circuit: Any, sdk: SDK | None = None) -> CircuitSummary:
243243
"""
244-
Extract summary from SDK-native circuit.
244+
Extract summary from SDK-native circuit object.
245245
246246
Parameters
247247
----------
@@ -310,6 +310,26 @@ def _detect_sdk_from_circuit(circuit: Any) -> SDK:
310310
)
311311

312312

313+
# =============================================================================
314+
# Diff functions
315+
# =============================================================================
316+
317+
318+
# Label width for aligned diff output
319+
_DIFF_LABEL_WIDTH = 14
320+
321+
# Fields to compare with optional percentage display
322+
_NUMERIC_DIFF_FIELDS = (
323+
("num_qubits", "qubits", False),
324+
("num_clbits", "clbits", False),
325+
("depth", "depth", True),
326+
("gate_count_1q", "1Q gates", True),
327+
("gate_count_2q", "2Q gates", True),
328+
("gate_count_total", "total gates", True),
329+
("parameter_count", "parameters", False),
330+
)
331+
332+
313333
def diff_summaries(
314334
summary_a: CircuitSummary,
315335
summary_b: CircuitSummary,
@@ -336,41 +356,26 @@ def diff_summaries(
336356
metrics: dict[str, Any] = {}
337357

338358
# Compare numeric fields
339-
_NUMERIC_FIELDS = (
340-
("num_qubits", "qubits", False),
341-
("num_clbits", "clbits", False),
342-
("depth", "depth", True),
343-
("gate_count_1q", "1Q gates", True),
344-
("gate_count_2q", "2Q gates", True),
345-
("gate_count_total", "total gates", True),
346-
("parameter_count", "parameters", False),
347-
)
348-
349-
for field_name, label, show_pct in _NUMERIC_FIELDS:
359+
for field_name, label, show_pct in _NUMERIC_DIFF_FIELDS:
350360
val_a = getattr(summary_a, field_name)
351361
val_b = getattr(summary_b, field_name)
352-
_compare_numeric(field_name, label, val_a, val_b, show_pct, changes, metrics)
362+
_record_numeric_diff(
363+
field_name, label, val_a, val_b, show_pct, changes, metrics
364+
)
353365

354-
# Clifford status change
366+
# Compare Clifford status
355367
if summary_a.is_clifford != summary_b.is_clifford:
368+
label = "is_clifford"
356369
changes.append(
357-
f"is_clifford: {summary_a.is_clifford}{summary_b.is_clifford}"
370+
f"{label:<{_DIFF_LABEL_WIDTH}} "
371+
f"{summary_a.is_clifford} => {summary_b.is_clifford}"
358372
)
359373
metrics["is_clifford_changed"] = True
360374
metrics["is_clifford_a"] = summary_a.is_clifford
361375
metrics["is_clifford_b"] = summary_b.is_clifford
362376

363-
# Gate type changes
364-
types_a = set(summary_a.gate_types.keys())
365-
types_b = set(summary_b.gate_types.keys())
366-
367-
if new_gates := types_b - types_a:
368-
changes.append(f"new gate types: {', '.join(sorted(new_gates))}")
369-
metrics["new_gate_types"] = sorted(new_gates)
370-
371-
if removed_gates := types_a - types_b:
372-
changes.append(f"removed gate types: {', '.join(sorted(removed_gates))}")
373-
metrics["removed_gate_types"] = sorted(removed_gates)
377+
# Compare gate types
378+
_record_gate_type_diff(summary_a, summary_b, changes, metrics)
374379

375380
logger.debug(
376381
"Compared summaries: %s",
@@ -386,7 +391,7 @@ def diff_summaries(
386391
)
387392

388393

389-
def _compare_numeric(
394+
def _record_numeric_diff(
390395
metric_key: str,
391396
label: str,
392397
val_a: int,
@@ -395,7 +400,26 @@ def _compare_numeric(
395400
changes: list[str],
396401
metrics: dict[str, Any],
397402
) -> None:
398-
"""Compare two numeric values and record changes."""
403+
"""
404+
Compare two numeric values and record changes.
405+
406+
Parameters
407+
----------
408+
metric_key : str
409+
Key for metrics dict.
410+
label : str
411+
Human-readable label.
412+
val_a : int
413+
Baseline value.
414+
val_b : int
415+
Comparison value.
416+
show_pct : bool
417+
Whether to show percentage change.
418+
changes : list of str
419+
List to append change descriptions to.
420+
metrics : dict
421+
Dict to record numeric metrics to.
422+
"""
399423
if val_a == val_b:
400424
return
401425

@@ -405,6 +429,42 @@ def _compare_numeric(
405429
if show_pct and val_a > 0:
406430
pct = (delta / val_a) * 100
407431
metrics[f"{metric_key}_delta_pct"] = pct
408-
changes.append(f"{label}: {val_a}{val_b} ({pct:+.1f}%)")
432+
changes.append(
433+
f"{label:<{_DIFF_LABEL_WIDTH}} {val_a} => {val_b} ({pct:+.1f}%)"
434+
)
409435
else:
410-
changes.append(f"{label}: {val_a}{val_b}")
436+
changes.append(f"{label:<{_DIFF_LABEL_WIDTH}} {val_a} => {val_b}")
437+
438+
439+
def _record_gate_type_diff(
440+
summary_a: CircuitSummary,
441+
summary_b: CircuitSummary,
442+
changes: list[str],
443+
metrics: dict[str, Any],
444+
) -> None:
445+
"""
446+
Compare gate types between summaries.
447+
448+
Parameters
449+
----------
450+
summary_a : CircuitSummary
451+
Baseline summary.
452+
summary_b : CircuitSummary
453+
Comparison summary.
454+
changes : list of str
455+
List to append change descriptions to.
456+
metrics : dict
457+
Dict to record gate type changes to.
458+
"""
459+
types_a = set(summary_a.gate_types.keys())
460+
types_b = set(summary_b.gate_types.keys())
461+
462+
if new_gates := types_b - types_a:
463+
sorted_gates = ", ".join(sorted(new_gates))
464+
changes.append(f"{'+ gates':<{_DIFF_LABEL_WIDTH}} {sorted_gates}")
465+
metrics["new_gate_types"] = sorted(new_gates)
466+
467+
if removed_gates := types_a - types_b:
468+
sorted_gates = ", ".join(sorted(removed_gates))
469+
changes.append(f"{'- gates':<{_DIFF_LABEL_WIDTH}} {sorted_gates}")
470+
metrics["removed_gate_types"] = sorted(removed_gates)

0 commit comments

Comments
 (0)