Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev update #17

Merged
merged 8 commits into from
Oct 18, 2023
Merged
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
69 changes: 41 additions & 28 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# A set of commands for development utilizing venv to develop, lint, test, document, and build the project.
# The goal is to concretely capture the development/build process for reproducibility of the development workflow.

.PHONY: requirements updatereqs metadeps install install-all docs lint tests doctest doctest2 clean venv dev-all build build-full
.DEFAULT_GOAL := help

.PHONY: requirements updatereqs metadeps install install-all docs lint tests tests-dep doctests tests-all clean venv dev-all build build-full help

# Try to autodetect if python3 or python is the python executable used.
BASEPYTHON := $(shell which python3 2>/dev/null || which python 2>/dev/null)
Expand All @@ -15,75 +18,85 @@ endif
# Requirements
# ------------

requirements: ## Install/refresh Python project requirements
$(VENV_BIN)/python -m pip install --upgrade pip
requirements: upgrade-pip ## Install/refresh Python project requirements
$(VENV_BIN)/pip install --upgrade -r requirements.txt
$(VENV_BIN)/pip install --upgrade -r docs/requirements.txt

updatereqs: ## Autogenerate requirements
$(VENV_BIN)/python -m pip install --upgrade pip
updatereqs: upgrade-pip ## Autogenerate requirements.txt
$(VENV_BIN)/pip install -U pip-tools
rm requirements.txt
-@rm requirements.txt
$(VENV_BIN)/pip-compile --extra=tests --no-annotate --no-emit-index-url --output-file=requirements.txt --strip-extras pyproject.toml

metadeps: ## Install packages used to develop/build this package
$(VENV_BIN)/python -m pip install --upgrade pip
metadeps: upgrade-pip ## Install extra dependencies used to develop/build this package
$(VENV_BIN)/pip install -U build pip-tools pre-commit wheel

# Installation
# ------------

install: ## Install PECOS
$(VENV_BIN)/python -m pip install --upgrade pip
install: upgrade-pip ## Install PECOS
$(VENV_BIN)/pip install .

install-all: ## Install PECOS with all optional dependencies
$(VENV_BIN)/python -m pip install --upgrade pip
install-all: upgrade-pip ## Install PECOS with all optional dependencies
$(VENV_BIN)/pip install .[all]

# Documentation
# -------------

docs: install ## Generate documentation
$(VENV_BIN)/python -m pip install --upgrade pip
$(VENV_BIN)/pip install -r ./docs/requirements.txt
cd docs && make clean && make html && cd - # <<< Will run using the base env... change that make to use .venv?
# TODO: Maybe call sphinx-build directly...
$(MAKE) -C docs SPHINXBUILD=../$(VENV_BIN)/sphinx-build clean html

# Linting / formatting
# --------------------

lint: metadeps ## Run all quality checks / linting
lint: metadeps ## Run all quality checks / linting / reformatting
$(VENV_BIN)/pre-commit run --all-files

# Testing
# -------

tests: install ## Run tests
$(VENV_BIN)/pytest tests
tests: venv install metadeps ## Run tests on the Python package (not including optional dependencies)
$(VENV_BIN)/pytest tests -m "not optional_dependency"

tests-dep: venv install-all metadeps ## Run tests on the Python package only for optional dependencies
$(VENV_BIN)/pytest tests -m optional_dependency

doctest: ## Run doctests
$(VENV_BIN)/sphinx-build -b doctest ./docs ./docs/_build
doctests: ## Run doctests with pytest
$(VENV_BIN)/pytest ./docs --doctest-glob=*.rst --doctest-continue-on-failure

doctest2: ## Run doctests using pytest
$(VENV_BIN)/pytest ./docs --doctest-glob=*.rst # --doctest-module
tests-all: tests tests-dep doctests ## Run all tests

# Building / Developing
# ---------------------

clean: ## Clean up caches and build artifacts
rm -rf *.egg-info dist build docs/_build .pytest_cache/ .ruff_cache/
-rm -rf *.egg-info dist build docs/_build .pytest_cache/ .ruff_cache/

venv: ## Build a new Python virtual environment from scratch
rm -rf .venv/
-rm -rf .venv/
$(BASEPYTHON) -m venv $(VENV)

dev-all: clean venv requirements metadeps ## Create a development environment from scratch
$(VENV_BIN)/python -m pip install --upgrade pip
dev-all: dev-setup ## Create a development environment from scratch with all optional dependencies and PECOS installed in editable mode
$(VENV_BIN)/pip install -e .[all]

build: clean venv requirements metadeps ## Clean, create new environment, and build PECOS for pypi
build: dev-setup ## Clean, create new environment, and build PECOS for pypi
$(VENV_BIN)/python -m build --sdist --wheel -n

build-full: clean venv requirements metadeps updatereqs install-all docs lint tests doctest ## Go through the full linting, testing, and building process
build-full: dev-setup updatereqs install-all docs lint tests-all ## Go through the full linting, testing, and building process
$(VENV_BIN)/python -m build --sdist --wheel -n

# Help
# ----

help: ## Show the help menu
@echo "Available make commands:"
@echo ""
@grep -E '^[a-z.A-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-22s\033[0m %s\n", $$1, $$2}'

# Utility targets
# ---------------

upgrade-pip:
$(VENV_BIN)/python -m pip install --upgrade pip

dev-setup: clean venv requirements metadeps
10 changes: 1 addition & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![PyPI version](https://badge.fury.io/py/quantum-pecos.svg)](https://badge.fury.io/py/quantum-pecos)
[![Documentation Status](https://readthedocs.org/projects/quantum-pecos/badge/?version=latest)](https://quantum-pecos.readthedocs.io/en/latest/?badge=latest)
[![Python versions](https://img.shields.io/badge/python-3.9%2C%203.10%2C%203.11%2C%203.12-blue.svg)](https://img.shields.io/badge/python-3.9%2C%203.10%2C%203.11-blue.svg)
[![Python versions](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue.svg)](https://img.shields.io/badge/python-3.9%2C%203.10%2C%203.11-blue.svg)
[![Supported by Quantinuum](https://img.shields.io/badge/supported_by-Quantinuum-blue)](https://www.quantinuum.com/)

**Performance Estimator of Codes On Surfaces (PECOS)** is a library/framework dedicated to the study, development, and
Expand All @@ -17,14 +17,6 @@ hybrid computation.
With an emphasis on clarity, flexibility, and performance and catering to both QEC students and developers, PECOS is
refined continually with these attributes in mind.

## Dependencies

- Python 3.9+
- NumPy >= 1.15.0, < 2.0
- SciPy >= 1.1.0, < 2.0
- NetworkX >= 2.1.0, < 3.0
- Matplotlib >= 2.2.0, < 3.0

## Features

- Quantum Error-Correction Tools: Advanced tools for studying quantum error-correction protocols and error models.
Expand Down
21 changes: 21 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[pytest]
addopts =
--strict-config
--strict-markers
--import-mode=importlib
# By default, run fast tests and only tests using required dependencies. To run all tests: pytest -m ""
# -m not slow and not optional_dependency
-m "not optional_dependency"

testpaths = tests
markers =
# slow: mark test as slow.
optional_dependency: mark a test as using one or more optional dependencies.
optional_unix: mark tests as using an optional dependency that only work with Unix-based systems.
wasmer: mark test as using the "wasmer" option.
wasmtime: mark test as using the "wasmtime" option.

# ProjectQ has a bunch of deprecation warnings from NumPy because they still use np.matrix instead of np.array
# TODO: comment this to deal with ProjectQ gate warnings
filterwarnings =
ignore::PendingDeprecationWarning:projectq.ops._gates
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ def reset(self) -> None:
def init(self, program: Any, foreign_classical_obj: object | None = None) -> int:
pass

@abc.abstractmethod
def optimize(self, machine=None, error_model=None, qsim=None) -> None:
pass

@abc.abstractmethod
def shot_reinit(self) -> None:
pass
Expand Down
33 changes: 23 additions & 10 deletions python/pecos/classical_interpreters/phir_classical_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import annotations

import json
import warnings
from typing import TYPE_CHECKING, Any

import numpy as np
Expand Down Expand Up @@ -86,21 +87,30 @@ def init(self, program: str | (dict | QuantumCircuit), foreign_obj: ForeignObjec
if not isinstance(self.program, PyPMIR):
self.program = PyPMIR.from_phir(self.program)

self.check_ffc(self.program.foreign_func_calls, self.foreign_obj)

self.initialize_cenv()

return self.program.num_qubits

def check_ffc(self, call_list: list[str], fobj: ForeignObject):
if self.program.foreign_func_calls:
func_names = set(fobj.get_funcs())
not_supported = set(call_list) - func_names
if not_supported:
msg = (
f"The following foreign function calls are listed in the program but not supported by the "
f"supplied foreign object: {not_supported}"
)
raise Exception(msg)
elif not self.program.foreign_func_calls and self.foreign_obj:
msg = "No foreign function calls being made but foreign object is supplied."
raise warnings.warn(msg, stacklevel=2)

def shot_reinit(self):
"""Run all code needed at the beginning of each shot, e.g., resetting state."""
self.initialize_cenv()

def optimize(self, machine=None, error_model=None, qsim=None):
"""Engine provides an opportunity for the classical interpreter to optimize program after the other components
have initialized. This allows the interpreter to optimize utilizing knowledge and methods provided by other
simulation components.
"""
...

def initialize_cenv(self) -> None:
self._reset_env()
if self.program:
Expand All @@ -112,7 +122,6 @@ def initialize_cenv(self) -> None:

def execute(self, sequence: Sequence) -> Generator[list, Any, None]:
"""A generator that runs through and executes classical logic and yields other operations via a buffer."""
# TODO: Recursively move through the program

op_buffer = []

Expand All @@ -134,7 +143,8 @@ def execute(self, sequence: Sequence) -> Generator[list, Any, None]:
op_buffer.append(op)

else:
print("!!!!!!!!!!!!!!", op)
msg = f"Statement not recognized: {op}"
raise TypeError(msg)

def get_cval(self, cvar):
cid = self.program.csym2id[cvar]
Expand Down Expand Up @@ -241,8 +251,11 @@ def handle_cops(self, op):

args.append(int(val))

if "namespace" in op.metadata:
if op.metadata and "namespace" in op.metadata:
results = self.foreign_obj.exec(op.name, args, op.metadata["namespace"])
elif self.foreign_obj is None:
msg = f"Trying to call foreign function `{op.name}` but no foreign object supplied!"
raise Exception(msg)
else:
results = self.foreign_obj.exec(op.name, args)

Expand Down
22 changes: 3 additions & 19 deletions python/pecos/engines/hybrid_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,14 @@ def initialize_sim_components(
self,
program: Any,
foreign_object: ForeignObject | None = None,
machine_params: dict | None = None,
error_params: dict | None = None,
) -> None:
"""Get objects to initialize before potentially running many simulations."""
self.init()
if foreign_object is not None:
foreign_object.init()
num_qubits = self.cinterp.init(program, foreign_object)
self.machine.init(machine_params, num_qubits)
self.error_model.init(error_params, num_qubits, self.machine)
self.machine.init(num_qubits)
self.error_model.init(num_qubits, self.machine)
self.op_processor.init()
self.qsim.init(num_qubits)

Expand Down Expand Up @@ -134,11 +132,8 @@ def run(
foreign_object: ForeignObject = None,
*,
shots: int = 1,
error_params: dict | None = None,
machine_params: dict | None = None,
seed: int | None = None,
initialize: bool = True,
optimize: bool = True,
return_int=False,
) -> dict:
"""Main method to run simulations.
Expand All @@ -148,11 +143,8 @@ def run(
program:
foreign_object:
shots:
error_params:
machine_params:
seed:
initialize:
optimize:
return_int:

Returns:
Expand All @@ -163,11 +155,7 @@ def run(

if initialize:
self.seed = self.use_seed(seed)
self.initialize_sim_components(program, foreign_object, machine_params, error_params)

if optimize:
# Potentially, optimize program for running simulations
self.cinterp.optimize(self.op_processor, self.qsim)
self.initialize_sim_components(program, foreign_object)

for _ in range(shots):
self.shot_reinit_components()
Expand All @@ -193,8 +181,6 @@ def run_multisim(
program,
foreign_object: ForeignObject = None,
shots: int = 1,
error_params: dict | None = None,
machine_params: dict | None = None,
seed: int | None = None,
pool_size: int = 1,
) -> dict:
Expand All @@ -204,8 +190,6 @@ def run_multisim(
program=program,
foreign_object=foreign_object,
shots=shots,
error_params=error_params,
machine_params=machine_params,
seed=seed,
pool_size=pool_size,
)
6 changes: 0 additions & 6 deletions python/pecos/engines/hybrid_engine_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ def run_multisim(
foreign_object: object = None,
*,
shots: int = 1,
error_params: dict | None = None,
machine_params: dict | None = None,
seed: int | None = None,
optimize: bool = True,
pool_size: int = 1,
reset_engine: bool = True,
):
Expand All @@ -57,11 +54,8 @@ def run_multisim(
"program": program,
"foreign_object": foreign_object,
"shots": shots,
"error_params": error_params,
"machine_params": machine_params,
"seed": seed,
"initialize": True,
"optimize": optimize,
}

np.random.seed(seed)
Expand Down
7 changes: 5 additions & 2 deletions python/pecos/error_models/error_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
class NoErrorModel(ErrorModel):
"""Represents having no error model."""

def __init__(self):
super().__init__(error_params={})

def reset(self) -> None:
"""Reset state to initialization state."""

def init(self, error_params, num_qubits, machine=None):
super().init(error_params, num_qubits, machine)
def init(self, num_qubits, machine=None):
super().init(num_qubits=num_qubits, machine=machine)
if self.error_params:
msg = "No error model is being utilized but error parameters are being provided!"
raise Exception(msg)
Expand Down
7 changes: 3 additions & 4 deletions python/pecos/error_models/error_model_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,17 @@


class ErrorModel(metaclass=ABCMeta):
def __init__(self) -> None:
self.error_params = None
def __init__(self, error_params: dict) -> None:
self.error_params = dict(error_params)
self.machine = None
self.num_qubits = None

@abc.abstractmethod
def reset(self) -> None:
"""Reset state to initialization state."""

def init(self, error_params, num_qubits, machine=None):
def init(self, num_qubits, machine=None):
self.machine = machine
self.error_params = error_params
self.num_qubits = num_qubits

@abc.abstractmethod
Expand Down
Loading