Skip to content

Commit

Permalink
Merge branch 'main' of github.com:BQSKit/bqskit
Browse files Browse the repository at this point in the history
  • Loading branch information
jkalloor3 committed Apr 8, 2024
2 parents 7edccd2 + be4727d commit fcaf7bc
Show file tree
Hide file tree
Showing 43 changed files with 84,854 additions and 127 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/nightly-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Runs slow tests every night
name: nightly tests

on:
schedule:
- cron: "21 2 * * *"

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.12

- name: Upgrade python environment
run: python -m pip install --upgrade virtualenv setuptools pip

- name: Upgrade test dependencies
run: python -m pip install psutil pytest 'hypothesis[zoneinfo]' qiskit

- name: Install BQSKit
env:
SYSTEM_VERSION_COMPAT: 0
run: pip install .

- name: Run tests
env:
NUMBER_RANDOM_CIRCUITS: 100
run: pytest
2 changes: 1 addition & 1 deletion .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/publish-on-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Publish Release
on:
release:

jobs:
build:
name: Build distribution
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install build tool
run: python -m pip install build --user

- name: Build distribution packages
run: python -m build

- name: Store the distribution packages
uses: actions/upload-artifact@v3
with:
name: python-package-distributions
path: dist/

publish-to-pypi:
name: Upload release to PyPI

needs:
- build

runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/bqskit

permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing

steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
15 changes: 10 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,28 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [macOS-latest, windows-latest, ubuntu-latest]

runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Upgrade python environment
run: python -m pip install --upgrade virtualenv setuptools pip

- name: Upgrade test dependencies
run: python -m pip install psutil pytest 'hypothesis[zoneinfo]' qiskit

- name: Install BQSKit
env:
SYSTEM_VERSION_COMPAT: 0
NUMBER_RANDOM_CIRCUITS: 100
run: pip install .

- name: Run tests
run: pytest
env:
NUMBER_RANDOM_CIRCUITS: 100
run: pytest --ignore=tests/compiler/compile --ignore=tests/passes/partitioning/test_parts.py --ignore=tests/passes/synthesis/test_qfast.py
62 changes: 39 additions & 23 deletions bqskit/compiler/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -989,37 +989,49 @@ def build_multi_qudit_retarget_workflow(
"""
Build standard workflow for circuit multi-qudit gate set retargeting.
This workflow assumes that SetModelPass will be run earlier in the full
workflow and doesn't add it in here.
Notes:
- This workflow assumes that SetModelPass will be run earlier in the
full workflow and doesn't add it in here.
- For the most part, circuit connectivity isn't a concern during
retargeting. However, if the circuit contains many-qudit (>= 3)
gates, then the workflow will not preserve connectivity during
the decomposition of those gates. If your input contains many-qudit
gates, consider following this with a mapping workflow.
"""

core_retarget_workflow = [
FillSingleQuditGatesPass(),
IfThenElsePass(
NotPredicate(MultiPhysicalPredicate()),
IfThenElsePass(
ManyQuditGatesPredicate(),
[
ExtractModelConnectivityPass(),
build_standard_search_synthesis_workflow(
optimization_level,
synthesis_epsilon,
),
RestoreModelConnevtivityPass(),
],
AutoRebase2QuditGatePass(3, 5, synthesis_epsilon),
),
ScanningGateRemovalPass(
success_threshold=synthesis_epsilon,
collection_filter=_mq_gate_collection_filter,
instantiate_options=get_instantiate_options(optimization_level),
),
),
]

return Workflow(
[
IfThenElsePass(
NotPredicate(WidthPredicate(2)),
[
LogPass('Retargeting multi-qudit gates.'),
build_partitioning_workflow(
[
FillSingleQuditGatesPass(),
IfThenElsePass(
NotPredicate(MultiPhysicalPredicate()),
IfThenElsePass(
ManyQuditGatesPredicate(),
build_standard_search_synthesis_workflow(
optimization_level,
synthesis_epsilon,
),
AutoRebase2QuditGatePass(3, 5),
),
ScanningGateRemovalPass(
success_threshold=synthesis_epsilon,
collection_filter=_mq_gate_collection_filter, # noqa: E501
instantiate_options=get_instantiate_options(
optimization_level,
),
),
),
],
core_retarget_workflow,
max_synthesis_size,
None if error_threshold is None else error_sim_size,
),
Expand Down Expand Up @@ -1221,6 +1233,7 @@ def build_seqpam_mapping_optimization_workflow(
IfThenElsePass(
NotPredicate(WidthPredicate(2)),
[
LogPass('Caching permutation-aware synthesis results.'),
ExtractModelConnectivityPass(),
QuickPartitioner(block_size),
ForEachBlockPass(
Expand All @@ -1240,11 +1253,13 @@ def build_seqpam_mapping_optimization_workflow(
),
),
),
LogPass('Preoptimizing with permutation-aware mapping.'),
PAMRoutingPass(),
post_pam_seq,
UnfoldPass(),
RestoreModelConnevtivityPass(),

LogPass('Recaching permutation-aware synthesis results.'),
SubtopologySelectionPass(block_size),
QuickPartitioner(block_size),
ForEachBlockPass(
Expand All @@ -1264,6 +1279,7 @@ def build_seqpam_mapping_optimization_workflow(
),
),
),
LogPass('Performing permutation-aware mapping.'),
ApplyPlacement(),
PAMLayoutPass(num_layout_passes),
PAMRoutingPass(0.1),
Expand Down
43 changes: 28 additions & 15 deletions bqskit/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import atexit
import functools
import logging
import os
import signal
import subprocess
import sys
import time
import uuid
Expand Down Expand Up @@ -125,12 +125,16 @@ def _start_server(
params = f'{num_workers}, {runtime_log_level}, {worker_port=}'
import_str = 'from bqskit.runtime.attached import start_attached_server'
launch_str = f'{import_str}; start_attached_server({params})'
self.p = Popen([sys.executable, '-c', launch_str])
if sys.platform == 'win32':
flags = subprocess.CREATE_NEW_PROCESS_GROUP
else:
flags = 0
self.p = Popen([sys.executable, '-c', launch_str], creationflags=flags)
_logger.debug('Starting runtime server process.')

def _connect_to_server(self, ip: str, port: int) -> None:
"""Connect to a runtime server at `ip` and `port`."""
max_retries = 7
max_retries = 8
wait_time = .25
for _ in range(max_retries):
try:
Expand Down Expand Up @@ -183,26 +187,35 @@ def close(self) -> None:
# Shutdown server if attached
if self.p is not None and self.p.pid is not None:
try:
os.kill(self.p.pid, signal.SIGINT)
_logger.debug('Interrupted attached runtime server.')

if sys.platform == 'win32':
self.p.send_signal(signal.CTRL_C_EVENT)
else:
self.p.send_signal(signal.SIGINT)
_logger.debug('Interrupting attached runtime server.')
self.p.communicate(timeout=1)
if self.p.returncode is None:
if sys.platform == 'win32':
self.p.terminate()
else:
os.kill(self.p.pid, signal.SIGKILL)
_logger.debug('Killed attached runtime server.')

except subprocess.TimeoutExpired:
self.p.kill()
_logger.debug('Killing attached runtime server.')
try:
self.p.communicate(timeout=30)
except subprocess.TimeoutExpired:
_logger.warning(
'Failed to kill attached runtime server.'
' It may still be running as a zombie process.',
)
else:
_logger.debug('Attached runtime server is down.')

except Exception as e:
_logger.debug(
_logger.warning(
f'Error while shuting down attached runtime server: {e}.',
)

else:
_logger.debug('Successfully shutdown attached runtime server.')

finally:
self.p.communicate()
_logger.debug('Attached runtime server is down.')
self.p = None

# Reset interrupt signal handler and remove exit handler
Expand Down
2 changes: 1 addition & 1 deletion bqskit/compiler/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def name(self) -> str:
def __str__(self) -> str:
name_seq = f'Workflow: {self.name}\n\t'
pass_strs = [
f'{i}. {"Workflow: " + p.name if isinstance(p, Workflow) else p}'
f'{i}. Workflow: {p.name if isinstance(p, Workflow) else p}'
for i, p in enumerate(self._passes)
]
return name_seq + '\n\t'.join(pass_strs)
Expand Down
3 changes: 2 additions & 1 deletion bqskit/ext/qiskit/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
if TYPE_CHECKING:
from qiskit import QuantumCircuit

from qiskit import qasm2
from bqskit.ir.circuit import Circuit
from bqskit.ir.lang.qasm2 import OPENQASM2Language


def qiskit_to_bqskit(qc: QuantumCircuit) -> Circuit:
"""Convert Qiskit's QuantumCircuit `qc` to a BQSKit Circuit."""
circuit = OPENQASM2Language().decode(qc.qasm())
circuit = OPENQASM2Language().decode(qasm2.dumps(qc))
# circuit.renumber_qudits(list(reversed(range(circuit.num_qudits))))
return circuit
# TODO: support gates not captured by qasm
Expand Down
7 changes: 6 additions & 1 deletion bqskit/ir/gates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
CircuitGate
MeasurementPlaceholder
Reset
BarrierPlaceholder
.. rubric:: Gate Base Classes
Expand Down Expand Up @@ -146,11 +147,15 @@
from bqskit.ir.gates.qubitgate import QubitGate
from bqskit.ir.gates.quditgate import QuditGate
from bqskit.ir.gates.qutritgate import QutritGate
from bqskit.ir.gates.reset import Reset

__all__ = composed_all + constant_all + parameterized_all
__all__ += ['ComposedGate', 'ConstantGate']
__all__ += ['QubitGate', 'QutritGate', 'QuditGate']
__all__ += ['CircuitGate', 'MeasurementPlaceholder', 'BarrierPlaceholder']
__all__ += [
'CircuitGate', 'MeasurementPlaceholder',
'Reset', 'BarrierPlaceholder',
]
__all__ += ['GeneralGate']

# TODO: Implement the rest of the gates in:
Expand Down
27 changes: 27 additions & 0 deletions bqskit/ir/gates/composed/controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ def __init__(
ctrl_U = np.kron(self.ctrl, U) + self.ihalf
self._utry = UnitaryMatrix(ctrl_U, self.radixes)

@property
def qasm_name(self) -> str:
"""
Override default `Gate.qasm_name` method.
If the core gate is a standard gate, this function will output
qasm in the form 'c+<gate_qasm>'. Otherwise an error will be raised.
Raises:
ValueError: If the core gate is non-standard in OpenQASM 2.0.
"""
_core_gate = self.gate.qasm_name
if self.num_controls <= 2:
_controls = 'c' * self.num_controls
else:
_controls = f'c{self.num_controls}'
qasm_name = _controls + _core_gate
supported_gates = ('cu1', 'cu2', 'cu3', 'cswap', 'c3x', 'c4x')
if qasm_name not in supported_gates:
raise ValueError(
f'Controlled gate {_core_gate} with {self.num_controls} '
'controls is not a standard OpenQASM 2.0 identifier. '
'To encode this gate, try decomposing it into gates with'
'standard identifiers.',
)
return qasm_name

def get_unitary(self, params: RealVector = []) -> UnitaryMatrix:
"""Return the unitary for this gate, see :class:`Unitary` for more."""
if hasattr(self, '_utry'):
Expand Down
Loading

0 comments on commit fcaf7bc

Please sign in to comment.