From 6a7eabf939edf45f2f4ff84b4c4add64fe292ba0 Mon Sep 17 00:00:00 2001 From: xoth42 Date: Fri, 19 Dec 2025 22:52:13 -0500 Subject: [PATCH 1/2] move tests to tests/, add descriptive CI, add requirements and test pyproject --- .github/workflows/ci.yml | 103 +++++++++++++++--- .gitignore | 4 +- pyproject.toml | 32 ++++++ requirements.txt | 4 +- tests/__init__.py | 1 + .../test_qml_parallel_run.py | 0 test_qml_run.py => tests/test_qml_run.py | 0 .../test_qnn_layers.py | 0 .../test_quantum_simulator.py | 0 .../test_run_experiments.py | 0 .../test_state_encoding.py | 0 11 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 pyproject.toml create mode 100644 tests/__init__.py rename test_qml_parallel_run.py => tests/test_qml_parallel_run.py (100%) rename test_qml_run.py => tests/test_qml_run.py (100%) rename test_qnn_layers.py => tests/test_qnn_layers.py (100%) rename test_quantum_simulator.py => tests/test_quantum_simulator.py (100%) rename test_run_experiments.py => tests/test_run_experiments.py (100%) rename test_state_encoding.py => tests/test_state_encoding.py (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd52b3e..4849311 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,103 @@ name: CI -on: [push] +on: + push: + pull_request: + workflow_dispatch: jobs: - test: + test-quantum-simulator: runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: pip install torch matplotlib scikit-learn + - name: Run quantum simulator tests + run: python -m unittest discover -s tests -p "test_quantum_simulator.py" -v + + test-qnn-layers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: pip install torch matplotlib scikit-learn + - name: Run QNN layers tests + run: python -m unittest discover -s tests -p "test_qnn_layers.py" -v + + test-qml-run: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: pip install torch matplotlib scikit-learn + - name: Run QML run tests + run: python -m unittest discover -s tests -p "test_qml_run.py" -v + + test-qml-parallel-run: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: pip install torch matplotlib scikit-learn + - name: Run QML parallel run tests + run: python -m unittest discover -s tests -p "test_qml_parallel_run.py" -v + test-run-experiments: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: pip install torch matplotlib scikit-learn + - name: Run experiments tests + run: python -m unittest discover -s tests -p "test_run_experiments.py" -v + test-state-encoding: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.x' + - name: Install dependencies + run: pip install torch matplotlib scikit-learn + - name: Run state encoding tests + run: python -m unittest discover -s tests -p "test_state_encoding.py" -v + run-training: + runs-on: ubuntu-latest + needs: [test-quantum-simulator, test-qnn-layers, test-qml-run, test-qml-parallel-run, test-run-experiments, test-state-encoding] + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' - name: Install dependencies - run: | - pip install torch matplotlib scikit-learn - - - name: Run tests - run: | - python -m unittest test_quantum_simulator.py && python -m unittest test_qnn_layers.py - - - name: Run qnn_training - run: | - python qml_training.py - - - name: Run qnn_training-parallel - run: | - python qml_training_parallel.py + run: pip install torch matplotlib scikit-learn + - name: Run qml_training + run: python qml_training.py + - name: Run qml_training_parallel + run: python qml_training_parallel.py + diff --git a/.gitignore b/.gitignore index 936ccc7..7eada52 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,6 @@ dmypy.json *.code-workspace data/* -plots/* \ No newline at end of file +plots/* +*.lock +.vscode/* \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..361062a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "compsci648-qml" +version = "0.1.0" +description = "Quantum Machine Learning Project for COMPSCI648" +requires-python = ">=3.8" +dependencies = [ + "torch>=2.0.0,<2.3.0", + "numpy>=1.24.0,<2.0.0", + "matplotlib", + "scikit-learn", +] + +[tool.setuptools] +py-modules = [ + "quantum_simulator", + "qml_training", + "qml_training_parallel", + "run_experiments", + "metrics", + "error_kraus", + "visualizer", +] + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] diff --git a/requirements.txt b/requirements.txt index 0aa4701..e0c784f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -torch>=2.0.0 -numpy>=1.24.0 +torch>=2.0.0,<2.3.0 +numpy>=1.24.0,<2.0.0 matplotlib>=3.7.0 scikit-learn>=1.3.0 pandas>=2.0.0 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..eeefabd --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# Test package for COMPSCI648 Quantum ML Project diff --git a/test_qml_parallel_run.py b/tests/test_qml_parallel_run.py similarity index 100% rename from test_qml_parallel_run.py rename to tests/test_qml_parallel_run.py diff --git a/test_qml_run.py b/tests/test_qml_run.py similarity index 100% rename from test_qml_run.py rename to tests/test_qml_run.py diff --git a/test_qnn_layers.py b/tests/test_qnn_layers.py similarity index 100% rename from test_qnn_layers.py rename to tests/test_qnn_layers.py diff --git a/test_quantum_simulator.py b/tests/test_quantum_simulator.py similarity index 100% rename from test_quantum_simulator.py rename to tests/test_quantum_simulator.py diff --git a/test_run_experiments.py b/tests/test_run_experiments.py similarity index 100% rename from test_run_experiments.py rename to tests/test_run_experiments.py diff --git a/test_state_encoding.py b/tests/test_state_encoding.py similarity index 100% rename from test_state_encoding.py rename to tests/test_state_encoding.py From 7f60ea77765425a67057575a70944ba4d8d7b809 Mon Sep 17 00:00:00 2001 From: xoth42 Date: Sat, 20 Dec 2025 01:18:50 -0500 Subject: [PATCH 2/2] Readme, usage, docstrings --- README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ metrics.py | 4 +++ 2 files changed, 85 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc8c010 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# COMPSCI 648 - Quantum Machine Learning Project + +Quantum machine learning for binary classification using variational quantum circuits, built with PyTorch. + +## Project Structure + +``` +quantum_simulator.py # States, gates, measurements +qml_training.py # VQC training pipelines +run_experiments.py # CLI experiment runner +error_kraus.py # T1/T2 noise via Kraus operators +metrics.py # Accuracy, F1, ROC-AUC +visualizer.py # Training curve plots +demos/circuit_visualization.ipynb +data/ # Output CSVs +tests/ +``` + +## Encoding + +- **Angle**: Feature θ → RY(θ) rotation. N features use N qubits. +- **Amplitude**: Features as state amplitudes. 2^N values use N qubits. + +## Noise Model + +T1/T2 thermal relaxation applied after each gate: +- T1: amplitude damping (|1⟩ → |0⟩ decay) +- T2: phase damping (coherence loss) + + Circuit error-adding functionality: + - Time-based idle noise between gates via `gate_durations` (μs) + - Optional decoherence during gates using `gate_noise_fraction` (0–1, default to 1) + - Kraus-based simulation with `T_φ` derived from `T1,T2`; enforces `T2 ≤ 2·T1` + - Units: all times in microseconds; typical: H≈0.025 μs, CNOT≈0.2 μs + - Relaxation model based on superconducting transmon qubit + +## Models + +**Deep VQC**: Multi-layer variational circuit with RY, RZ, CNOT layers. + +**Noise-Aware VQC**: Same architecture, trained with simulated T1/T2 noise using density matrix evolution. + +## Usage + +```bash +# Default run +python run_experiments.py + +# Custom parameters +python run_experiments.py --epochs 50 --T1 50 --T2 100 + +# Specific configuration +python run_experiments.py --models deep_vqc --encodings angle --datasets moons --plot +``` + +| Arg | Default | Options | +|-----|---------|---------| +| `--epochs` | 25 | | +| `--T1` | 100 | T1 relaxation (μs) | +| `--T2` | 200 | T2 dephasing (μs) | +| `--models` | all | `deep_vqc`, `noise_aware` | +| `--encodings` | all | `angle`, `amplitude` | +| `--datasets` | all | `real`, `moons` | +| `--plot` | off | Save comparison plots | + +## Datasets + +- **real**: Breast cancer (UCI), PCA to 2-4 features +- **moons**: Synthetic two-moons, 2D + +## Demo Notebook + +`demos/circuit_visualization.ipynb` shows encoding comparisons, kernel matrices, and noise effects on fidelity. + +## Install + +```bash +pip install -e . +``` + +Requires Python ≥3.8, PyTorch ≥2.0, NumPy, Matplotlib, scikit-learn. diff --git a/metrics.py b/metrics.py index 261ebfd..dfb4fdc 100644 --- a/metrics.py +++ b/metrics.py @@ -7,19 +7,23 @@ def binary_predictions(scores): return torch.where(scores >= 0, 1.0, -1.0) def accuracy(y_true, y_pred): + """Binary accuracy for {-1,+1} labels.""" return (y_true == y_pred).float().mean().item() def precision(y_true, y_pred): + """Positive predictive value for {-1,+1} labels.""" tp = ((y_pred == 1) & (y_true == 1)).sum().item() fp = ((y_pred == 1) & (y_true == -1)).sum().item() return tp / (tp + fp + 1e-9) def recall(y_true, y_pred): + """True positive rate for {-1,+1} labels.""" tp = ((y_pred == 1) & (y_true == 1)).sum().item() fn = ((y_pred == -1) & (y_true == 1)).sum().item() return tp / (tp + fn + 1e-9) def f1_score(y_true, y_pred): + """Harmonic mean of precision and recall.""" p = precision(y_true, y_pred) r = recall(y_true, y_pred) return 2 * p * r / (p + r + 1e-9)