Skip to content

Commit 1ee59f1

Browse files
authored
Merge pull request #207 from BQSKit/1.1.1
1.1.1
2 parents da69b2e + 8b95b1a commit 1ee59f1

File tree

9 files changed

+84
-46
lines changed

9 files changed

+84
-46
lines changed

bqskit/compiler/compile.py

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -989,37 +989,49 @@ def build_multi_qudit_retarget_workflow(
989989
"""
990990
Build standard workflow for circuit multi-qudit gate set retargeting.
991991
992-
This workflow assumes that SetModelPass will be run earlier in the full
993-
workflow and doesn't add it in here.
992+
Notes:
993+
- This workflow assumes that SetModelPass will be run earlier in the
994+
full workflow and doesn't add it in here.
995+
996+
- For the most part, circuit connectivity isn't a concern during
997+
retargeting. However, if the circuit contains many-qudit (>= 3)
998+
gates, then the workflow will not preserve connectivity during
999+
the decomposition of those gates. If your input contains many-qudit
1000+
gates, consider following this with a mapping workflow.
9941001
"""
1002+
1003+
core_retarget_workflow = [
1004+
FillSingleQuditGatesPass(),
1005+
IfThenElsePass(
1006+
NotPredicate(MultiPhysicalPredicate()),
1007+
IfThenElsePass(
1008+
ManyQuditGatesPredicate(),
1009+
[
1010+
ExtractModelConnectivityPass(),
1011+
build_standard_search_synthesis_workflow(
1012+
optimization_level,
1013+
synthesis_epsilon,
1014+
),
1015+
RestoreModelConnevtivityPass(),
1016+
],
1017+
AutoRebase2QuditGatePass(3, 5),
1018+
),
1019+
ScanningGateRemovalPass(
1020+
success_threshold=synthesis_epsilon,
1021+
collection_filter=_mq_gate_collection_filter,
1022+
instantiate_options=get_instantiate_options(optimization_level),
1023+
),
1024+
),
1025+
]
1026+
9951027
return Workflow(
9961028
[
9971029
IfThenElsePass(
9981030
NotPredicate(WidthPredicate(2)),
9991031
[
10001032
LogPass('Retargeting multi-qudit gates.'),
10011033
build_partitioning_workflow(
1002-
[
1003-
FillSingleQuditGatesPass(),
1004-
IfThenElsePass(
1005-
NotPredicate(MultiPhysicalPredicate()),
1006-
IfThenElsePass(
1007-
ManyQuditGatesPredicate(),
1008-
build_standard_search_synthesis_workflow(
1009-
optimization_level,
1010-
synthesis_epsilon,
1011-
),
1012-
AutoRebase2QuditGatePass(3, 5),
1013-
),
1014-
ScanningGateRemovalPass(
1015-
success_threshold=synthesis_epsilon,
1016-
collection_filter=_mq_gate_collection_filter, # noqa: E501
1017-
instantiate_options=get_instantiate_options(
1018-
optimization_level,
1019-
),
1020-
),
1021-
),
1022-
],
1034+
core_retarget_workflow,
10231035
max_synthesis_size,
10241036
None if error_threshold is None else error_sim_size,
10251037
),
@@ -1221,6 +1233,7 @@ def build_seqpam_mapping_optimization_workflow(
12211233
IfThenElsePass(
12221234
NotPredicate(WidthPredicate(2)),
12231235
[
1236+
LogPass('Caching permutation-aware synthesis results.'),
12241237
ExtractModelConnectivityPass(),
12251238
QuickPartitioner(block_size),
12261239
ForEachBlockPass(
@@ -1240,11 +1253,13 @@ def build_seqpam_mapping_optimization_workflow(
12401253
),
12411254
),
12421255
),
1256+
LogPass('Preoptimizing with permutation-aware mapping.'),
12431257
PAMRoutingPass(),
12441258
post_pam_seq,
12451259
UnfoldPass(),
12461260
RestoreModelConnevtivityPass(),
12471261

1262+
LogPass('Recaching permutation-aware synthesis results.'),
12481263
SubtopologySelectionPass(block_size),
12491264
QuickPartitioner(block_size),
12501265
ForEachBlockPass(
@@ -1264,6 +1279,7 @@ def build_seqpam_mapping_optimization_workflow(
12641279
),
12651280
),
12661281
),
1282+
LogPass('Performing permutation-aware mapping.'),
12671283
ApplyPlacement(),
12681284
PAMLayoutPass(num_layout_passes),
12691285
PAMRoutingPass(0.1),

bqskit/compiler/compiler.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import atexit
55
import functools
66
import logging
7-
import os
87
import signal
8+
import subprocess
99
import sys
1010
import time
1111
import uuid
@@ -125,12 +125,16 @@ def _start_server(
125125
params = f'{num_workers}, {runtime_log_level}, {worker_port=}'
126126
import_str = 'from bqskit.runtime.attached import start_attached_server'
127127
launch_str = f'{import_str}; start_attached_server({params})'
128-
self.p = Popen([sys.executable, '-c', launch_str])
128+
if sys.platform == 'win32':
129+
flags = subprocess.CREATE_NEW_PROCESS_GROUP
130+
else:
131+
flags = 0
132+
self.p = Popen([sys.executable, '-c', launch_str], creationflags=flags)
129133
_logger.debug('Starting runtime server process.')
130134

131135
def _connect_to_server(self, ip: str, port: int) -> None:
132136
"""Connect to a runtime server at `ip` and `port`."""
133-
max_retries = 7
137+
max_retries = 8
134138
wait_time = .25
135139
for _ in range(max_retries):
136140
try:
@@ -183,26 +187,35 @@ def close(self) -> None:
183187
# Shutdown server if attached
184188
if self.p is not None and self.p.pid is not None:
185189
try:
186-
os.kill(self.p.pid, signal.SIGINT)
187-
_logger.debug('Interrupted attached runtime server.')
188-
190+
if sys.platform == 'win32':
191+
self.p.send_signal(signal.CTRL_C_EVENT)
192+
else:
193+
self.p.send_signal(signal.SIGINT)
194+
_logger.debug('Interrupting attached runtime server.')
189195
self.p.communicate(timeout=1)
190-
if self.p.returncode is None:
191-
if sys.platform == 'win32':
192-
self.p.terminate()
193-
else:
194-
os.kill(self.p.pid, signal.SIGKILL)
195-
_logger.debug('Killed attached runtime server.')
196+
197+
except subprocess.TimeoutExpired:
198+
self.p.kill()
199+
_logger.debug('Killing attached runtime server.')
200+
try:
201+
self.p.communicate(timeout=30)
202+
except subprocess.TimeoutExpired:
203+
_logger.warning(
204+
'Failed to kill attached runtime server.'
205+
' It may still be running as a zombie process.',
206+
)
207+
else:
208+
_logger.debug('Attached runtime server is down.')
196209

197210
except Exception as e:
198-
_logger.debug(
211+
_logger.warning(
199212
f'Error while shuting down attached runtime server: {e}.',
200213
)
214+
201215
else:
202216
_logger.debug('Successfully shutdown attached runtime server.')
217+
203218
finally:
204-
self.p.communicate()
205-
_logger.debug('Attached runtime server is down.')
206219
self.p = None
207220

208221
# Reset interrupt signal handler and remove exit handler

bqskit/compiler/workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def name(self) -> str:
9090
def __str__(self) -> str:
9191
name_seq = f'Workflow: {self.name}\n\t'
9292
pass_strs = [
93-
f'{i}. {'Workflow: ' + p.name if isinstance(p, Workflow) else p}'
93+
f'{i}. Workflow: {p.name if isinstance(p, Workflow) else p}'
9494
for i, p in enumerate(self._passes)
9595
]
9696
return name_seq + '\n\t'.join(pass_strs)

bqskit/passes/mapping/placement/trivial.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ async def run(self, circuit: Circuit, data: PassData) -> None:
2020
model = BasePass.get_model(circuit, data)
2121
data['placement'] = trivial_placement
2222

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

2525
# Raise an error if this is not a valid placement
2626
sg = model.coupling_graph.get_subgraph(data['placement'])

bqskit/passes/search/generators/fourparam.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ def gen_successors(self, circuit: Circuit, data: PassData) -> list[Circuit]:
7979

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

8486
successor = circuit.copy()
8587
successor.append_gate(CNOTGate(), edge)

bqskit/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""This module contains the version information for BQSKit."""
22
from __future__ import annotations
3-
__version_info__ = ('1', '1', '0')
3+
__version_info__ = ('1', '1', '1')
44
__version__ = '.'.join(__version_info__[:3]) + ''.join(__version_info__[3:])

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
},
6767
packages=find_packages(exclude=['examples*', 'test*']),
6868
install_requires=[
69-
'bqskitrs>=0.4.0',
69+
'bqskitrs>=0.4.1',
7070
'lark-parser',
7171
'numpy>=1.22.0',
7272
'scipy>=1.8.0',

tests/compiler/compile/test_pam_verify.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ def test_pam_verify(compiler: Compiler, medium_qasm_file: str) -> None:
2525
PI = PermutationMatrix.from_qubit_location(out_circuit.num_qudits, pi)
2626
PF = PermutationMatrix.from_qubit_location(out_circuit.num_qudits, pf)
2727
exact_error = out_utry.get_distance_from(PF.T @ circuit.get_unitary() @ PI)
28-
assert upper_bound_error >= exact_error or abs(upper_bound_error - exact_error) < 5e-7
28+
assert upper_bound_error >= exact_error or abs(
29+
upper_bound_error - exact_error,
30+
) < 5e-7

tests/compiler/compile/test_synthesis.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,13 @@ def test_identity_synthesis(
154154
assert out_circuit.get_unitary().get_distance_from(
155155
UnitaryMatrix.identity(dim), 1,
156156
) < 1e-8
157-
if optimization_level == 3:
158-
assert out_circuit.num_operations <= 3
157+
158+
# TODO: Re-enable this check when tree gate deletion hits the OTS.
159+
# In cases where the identity is synthesized to two cnots surrounded
160+
# by a bunch of single-qudit gates, scanning gate removal cannot
161+
# remove either cnot.
162+
# if optimization_level >= 3:
163+
# assert out_circuit.num_operations <= 3
159164

160165

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

0 commit comments

Comments
 (0)