Skip to content

Commit

Permalink
Merge pull request #207 from BQSKit/1.1.1
Browse files Browse the repository at this point in the history
1.1.1
  • Loading branch information
edyounis authored Dec 11, 2023
2 parents da69b2e + 8b95b1a commit 1ee59f1
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 46 deletions.
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),
),
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
2 changes: 1 addition & 1 deletion bqskit/passes/mapping/placement/trivial.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async def run(self, circuit: Circuit, data: PassData) -> None:
model = BasePass.get_model(circuit, data)
data['placement'] = trivial_placement

_logger.info(f'Placed qudits on {data['placement']}')
_logger.info(f'Placed qudits on {data["placement"]}')

# Raise an error if this is not a valid placement
sg = model.coupling_graph.get_subgraph(data['placement'])
Expand Down
4 changes: 3 additions & 1 deletion bqskit/passes/search/generators/fourparam.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ def gen_successors(self, circuit: Circuit, data: PassData) -> list[Circuit]:

if self.count_outer_cnots(circuit, edge) >= 3:
# No need to build circuits with more than 3 cnots in a row
continue
if circuit.num_qudits != 2:
# Guard on >2 qubit to prevent high-error glitches
continue

successor = circuit.copy()
successor.append_gate(CNOTGate(), edge)
Expand Down
2 changes: 1 addition & 1 deletion bqskit/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""This module contains the version information for BQSKit."""
from __future__ import annotations
__version_info__ = ('1', '1', '0')
__version_info__ = ('1', '1', '1')
__version__ = '.'.join(__version_info__[:3]) + ''.join(__version_info__[3:])
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
},
packages=find_packages(exclude=['examples*', 'test*']),
install_requires=[
'bqskitrs>=0.4.0',
'bqskitrs>=0.4.1',
'lark-parser',
'numpy>=1.22.0',
'scipy>=1.8.0',
Expand Down
4 changes: 3 additions & 1 deletion tests/compiler/compile/test_pam_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ def test_pam_verify(compiler: Compiler, medium_qasm_file: str) -> None:
PI = PermutationMatrix.from_qubit_location(out_circuit.num_qudits, pi)
PF = PermutationMatrix.from_qubit_location(out_circuit.num_qudits, pf)
exact_error = out_utry.get_distance_from(PF.T @ circuit.get_unitary() @ PI)
assert upper_bound_error >= exact_error or abs(upper_bound_error - exact_error) < 5e-7
assert upper_bound_error >= exact_error or abs(
upper_bound_error - exact_error,
) < 5e-7
9 changes: 7 additions & 2 deletions tests/compiler/compile/test_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,13 @@ def test_identity_synthesis(
assert out_circuit.get_unitary().get_distance_from(
UnitaryMatrix.identity(dim), 1,
) < 1e-8
if optimization_level == 3:
assert out_circuit.num_operations <= 3

# TODO: Re-enable this check when tree gate deletion hits the OTS.
# In cases where the identity is synthesized to two cnots surrounded
# by a bunch of single-qudit gates, scanning gate removal cannot
# remove either cnot.
# if optimization_level >= 3:
# assert out_circuit.num_operations <= 3


@pytest.mark.parametrize('num_qudits', [1, 2])
Expand Down

0 comments on commit 1ee59f1

Please sign in to comment.