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

1.1.1 #207

Merged
merged 11 commits into from
Dec 11, 2023
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
Loading