QuantumLearning/tests/test_notebook_pedagogy.py

235 lines
8.9 KiB
Python

from __future__ import annotations
import json
from quantum_learning import legacy_archive_dir, project_root
def _notebook_stats(relative_path: str) -> dict[str, int]:
path = project_root() / "notebooks" / relative_path
data = json.loads(path.read_text())
markdown = "\n".join(
"".join(cell["source"]) for cell in data["cells"] if cell["cell_type"] == "markdown"
)
code = ["".join(cell["source"]) for cell in data["cells"] if cell["cell_type"] == "code"]
return {
"word_count": len(markdown.split()),
"quiz_blocks": sum("quiz_block(" in cell for cell in code),
"editable_labs": sum("editable_circuit_lab(" in cell for cell in code),
"reflections": sum("reflection_box(" in cell for cell in code),
"rubrics": sum("rubric_scorecard(" in cell for cell in code),
"feedback_loops": sum("feedback_iteration_panel(" in cell for cell in code),
"checklists": sum("evidence_checklist(" in cell for cell in code),
}
def test_legacy_single_notebook_sequence_is_hidden_from_main_view():
archive = legacy_archive_dir()
assert archive.exists()
notebook_names = [
"00_circuit_literacy.ipynb",
"01_statevectors.ipynb",
"02_measurement_and_counts.ipynb",
"03_transpilation_and_backend_reality.ipynb",
"04_noise_and_robustness.ipynb",
"05_design_patterns_and_subcircuits.ipynb",
"06_reversible_logic_and_oracles.ipynb",
"07_synthesis_and_parametric_design.ipynb",
"08_hardware_aware_design_studio.ipynb",
"09_verification_and_noise_debugging.ipynb",
"10_capstone_circuit_design_review.ipynb",
]
for notebook_name in notebook_names:
assert (archive / notebook_name).exists()
assert not (project_root() / "notebooks" / notebook_name).exists()
def test_orientation_notebooks_are_substantial():
start_stats = _notebook_stats("START_HERE.ipynb")
blueprint_stats = _notebook_stats("COURSE_BLUEPRINT.ipynb")
assert start_stats["word_count"] >= 1500
assert start_stats["quiz_blocks"] >= 2
assert start_stats["reflections"] >= 1
assert blueprint_stats["word_count"] >= 1000
assert blueprint_stats["quiz_blocks"] >= 2
assert blueprint_stats["reflections"] >= 1
def test_foundations_bundle_modules_are_rich_and_interactive():
blueprint_stats = _notebook_stats("COURSE_BLUEPRINT.ipynb")
assert blueprint_stats["word_count"] >= 1000
assert blueprint_stats["quiz_blocks"] >= 2
assert blueprint_stats["reflections"] >= 1
module_roots = [
"foundations/module_01_principles_and_circuit_literacy",
"foundations/module_02_qubit_and_statevector_intuition",
"foundations/module_03_gates_and_measurement",
]
for module_root in module_roots:
lecture_stats = _notebook_stats(f"{module_root}/lecture.ipynb")
lab_stats = _notebook_stats(f"{module_root}/lab.ipynb")
problems_stats = _notebook_stats(f"{module_root}/problems.ipynb")
studio_stats = _notebook_stats(f"{module_root}/studio.ipynb")
assert lecture_stats["word_count"] >= 1000
assert lecture_stats["quiz_blocks"] >= 2
assert lecture_stats["editable_labs"] >= 1
assert lecture_stats["reflections"] >= 2
assert lab_stats["word_count"] >= 300
assert lab_stats["quiz_blocks"] >= 2
assert lab_stats["editable_labs"] >= 3
assert lab_stats["reflections"] >= 2
assert problems_stats["word_count"] >= 250
assert problems_stats["quiz_blocks"] >= 4
assert problems_stats["reflections"] >= 2
assert studio_stats["word_count"] >= 200
assert studio_stats["quiz_blocks"] >= 1
assert studio_stats["editable_labs"] >= 3
assert studio_stats["reflections"] >= 4
assert (
lecture_stats["word_count"]
+ lab_stats["word_count"]
+ problems_stats["word_count"]
+ studio_stats["word_count"]
>= 2200
)
def test_qiskit_engineering_bundle_modules_are_rich_and_interactive():
module_roots = [
"qiskit_engineering/module_01_circuit_construction_and_analysis",
"qiskit_engineering/module_02_transpilation_and_visualization",
"qiskit_engineering/module_03_simulation_and_noise_models",
]
for module_root in module_roots:
lecture_stats = _notebook_stats(f"{module_root}/lecture.ipynb")
lab_stats = _notebook_stats(f"{module_root}/lab.ipynb")
problems_stats = _notebook_stats(f"{module_root}/problems.ipynb")
studio_stats = _notebook_stats(f"{module_root}/studio.ipynb")
assert lecture_stats["word_count"] >= 1000
assert lecture_stats["quiz_blocks"] >= 2
assert lecture_stats["editable_labs"] >= 1
assert lecture_stats["reflections"] >= 2
assert lab_stats["word_count"] >= 300
assert lab_stats["quiz_blocks"] >= 2
assert lab_stats["editable_labs"] >= 3
assert lab_stats["reflections"] >= 2
assert problems_stats["word_count"] >= 250
assert problems_stats["quiz_blocks"] >= 4
assert problems_stats["reflections"] >= 2
assert studio_stats["word_count"] >= 200
assert studio_stats["quiz_blocks"] >= 1
assert studio_stats["editable_labs"] >= 3
assert studio_stats["reflections"] >= 4
assert (
lecture_stats["word_count"]
+ lab_stats["word_count"]
+ problems_stats["word_count"]
+ studio_stats["word_count"]
>= 2200
)
def test_algorithmic_design_bundle_modules_are_rich_and_interactive():
module_roots = [
"algorithms/module_01_deutsch_family",
"algorithms/module_02_bernstein_vazirani",
"algorithms/module_03_qft",
"algorithms/module_04_grover",
]
for module_root in module_roots:
lecture_stats = _notebook_stats(f"{module_root}/lecture.ipynb")
lab_stats = _notebook_stats(f"{module_root}/lab.ipynb")
problems_stats = _notebook_stats(f"{module_root}/problems.ipynb")
studio_stats = _notebook_stats(f"{module_root}/studio.ipynb")
assert lecture_stats["word_count"] >= 1000
assert lecture_stats["quiz_blocks"] >= 2
assert lecture_stats["editable_labs"] >= 1
assert lecture_stats["reflections"] >= 2
assert lab_stats["word_count"] >= 300
assert lab_stats["quiz_blocks"] >= 2
assert lab_stats["editable_labs"] >= 3
assert lab_stats["reflections"] >= 2
assert problems_stats["word_count"] >= 250
assert problems_stats["quiz_blocks"] >= 4
assert problems_stats["reflections"] >= 2
assert studio_stats["word_count"] >= 200
assert studio_stats["quiz_blocks"] >= 1
assert studio_stats["editable_labs"] >= 3
assert studio_stats["reflections"] >= 4
assert (
lecture_stats["word_count"]
+ lab_stats["word_count"]
+ problems_stats["word_count"]
+ studio_stats["word_count"]
>= 2200
)
def test_professional_design_bundle_modules_are_rich_and_interactive():
module_roots = [
"professional/module_01_qiskit_patterns",
"professional/module_02_hardware_aware_redesign",
"professional/module_03_noise_aware_verification",
"professional/module_04_capstone_design_review",
]
for module_root in module_roots:
lecture_stats = _notebook_stats(f"{module_root}/lecture.ipynb")
lab_stats = _notebook_stats(f"{module_root}/lab.ipynb")
problems_stats = _notebook_stats(f"{module_root}/problems.ipynb")
studio_stats = _notebook_stats(f"{module_root}/studio.ipynb")
assert lecture_stats["word_count"] >= 1000
assert lecture_stats["quiz_blocks"] >= 2
assert lecture_stats["editable_labs"] >= 1
assert lecture_stats["reflections"] >= 2
assert lecture_stats["rubrics"] >= 1
assert lecture_stats["feedback_loops"] >= 1
assert lab_stats["word_count"] >= 300
assert lab_stats["quiz_blocks"] >= 2
assert lab_stats["editable_labs"] >= 3
assert lab_stats["reflections"] >= 2
assert lab_stats["rubrics"] >= 1
assert lab_stats["feedback_loops"] >= 1
assert problems_stats["word_count"] >= 250
assert problems_stats["quiz_blocks"] >= 4
assert problems_stats["reflections"] >= 2
assert problems_stats["rubrics"] >= 1
assert problems_stats["feedback_loops"] >= 1
assert studio_stats["word_count"] >= 200
assert studio_stats["quiz_blocks"] >= 1
assert studio_stats["editable_labs"] >= 3
assert studio_stats["reflections"] >= 4
assert studio_stats["rubrics"] >= 1
assert studio_stats["feedback_loops"] >= 1
assert studio_stats["checklists"] >= 1
assert (
lecture_stats["word_count"]
+ lab_stats["word_count"]
+ problems_stats["word_count"]
+ studio_stats["word_count"]
>= 2200
)