Skip to content

Commit f0a0c1e

Browse files
authored
Add checks to avoid too many compiler engines in the engine list (#405)
* Add checks to avoid too many compiler engines in the engine list * Update CHANGELOG * Apply suggestions from Andreas
1 parent a0a41fe commit f0a0c1e

File tree

5 files changed

+59
-5
lines changed

5 files changed

+59
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
### Changed
1212
### Deprecated
1313
### Fixed
14+
15+
- Prevent infinite recursion errors when too many compiler engines are added to the MainEngine
16+
1417
### Removed
1518
### Repository
1619

projectq/cengines/_main.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ def receive(self, command_list): # pylint: disable=unused-argument
4747
"""No-op"""
4848

4949

50+
_N_ENGINES_THRESHOLD = 100
51+
52+
5053
class MainEngine(BasicEngine): # pylint: disable=too-many-instance-attributes
5154
"""
5255
The MainEngine class provides all functionality of the main compiler engine.
@@ -61,10 +64,13 @@ class MainEngine(BasicEngine): # pylint: disable=too-many-instance-attributes
6164
dirty_qubits (Set): Containing all dirty qubit ids
6265
backend (BasicEngine): Access the back-end.
6366
mapper (BasicMapperEngine): Access to the mapper if there is one.
64-
67+
n_engines (int): Current number of compiler engines in the engine list
68+
n_engines_max (int): Maximum number of compiler engines allowed in the engine list. Defaults to 100.
6569
"""
6670

67-
def __init__(self, backend=None, engine_list=None, verbose=False):
71+
def __init__( # pylint: disable=too-many-statements,too-many-branches
72+
self, backend=None, engine_list=None, verbose=False
73+
):
6874
"""
6975
Initialize the main compiler engine and all compiler engines.
7076
@@ -118,6 +124,7 @@ def __init__(self, backend=None, engine_list=None, verbose=False):
118124
self.dirty_qubits = set()
119125
self.verbose = verbose
120126
self.main_engine = self
127+
self.n_engines_max = _N_ENGINES_THRESHOLD
121128

122129
if backend is None:
123130
backend = Simulator()
@@ -174,6 +181,10 @@ def __init__(self, backend=None, engine_list=None, verbose=False):
174181
" twice.\n"
175182
)
176183

184+
self.n_engines = len(engine_list)
185+
if self.n_engines > self.n_engines_max:
186+
raise ValueError('Too many compiler engines added to the MainEngine!')
187+
177188
self._qubit_idx = int(0)
178189
for i in range(len(engine_list) - 1):
179190
engine_list[i].next_engine = engine_list[i + 1]

projectq/cengines/_main_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ def test_main_engine_init_defaults():
7373
assert type(engine) == type(expected)
7474

7575

76+
def test_main_engine_too_many_compiler_engines():
77+
old = _main._N_ENGINES_THRESHOLD
78+
_main._N_ENGINES_THRESHOLD = 3
79+
80+
_main.MainEngine(backend=DummyEngine(), engine_list=[DummyEngine(), DummyEngine()])
81+
82+
with pytest.raises(ValueError):
83+
_main.MainEngine(backend=DummyEngine(), engine_list=[DummyEngine(), DummyEngine(), DummyEngine()])
84+
85+
_main._N_ENGINES_THRESHOLD = old
86+
87+
7688
def test_main_engine_init_mapper():
7789
class LinearMapper(BasicMapperEngine):
7890
pass

projectq/meta/_util.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ def insert_engine(prev_engine, engine_to_insert):
3030
engine_to_insert (projectq.cengines.BasicEngine):
3131
The engine to insert at the insertion point.
3232
"""
33+
if prev_engine.main_engine is not None:
34+
prev_engine.main_engine.n_engines += 1
35+
36+
if prev_engine.main_engine.n_engines > prev_engine.main_engine.n_engines_max:
37+
raise RuntimeError('Too many compiler engines added to the MainEngine!')
38+
3339
engine_to_insert.main_engine = prev_engine.main_engine
3440
engine_to_insert.next_engine = prev_engine.next_engine
3541
prev_engine.next_engine = engine_to_insert
@@ -47,6 +53,8 @@ def drop_engine_after(prev_engine):
4753
"""
4854
dropped_engine = prev_engine.next_engine
4955
prev_engine.next_engine = dropped_engine.next_engine
56+
if prev_engine.main_engine is not None:
57+
prev_engine.main_engine.n_engines -= 1
5058
dropped_engine.next_engine = None
5159
dropped_engine.main_engine = None
5260
return dropped_engine

projectq/meta/_util_test.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import pytest
17+
1618
from projectq import MainEngine
1719
from projectq.cengines import DummyEngine
18-
from projectq.meta import insert_engine, drop_engine_after
20+
21+
22+
from . import _util
1923

2024

2125
def test_insert_then_drop():
@@ -30,19 +34,35 @@ def test_insert_then_drop():
3034
assert d1.main_engine is eng
3135
assert d2.main_engine is None
3236
assert d3.main_engine is eng
37+
assert eng.n_engines == 2
3338

34-
insert_engine(d1, d2)
39+
_util.insert_engine(d1, d2)
3540
assert d1.next_engine is d2
3641
assert d2.next_engine is d3
3742
assert d3.next_engine is None
3843
assert d1.main_engine is eng
3944
assert d2.main_engine is eng
4045
assert d3.main_engine is eng
46+
assert eng.n_engines == 3
4147

42-
drop_engine_after(d1)
48+
_util.drop_engine_after(d1)
4349
assert d1.next_engine is d3
4450
assert d2.next_engine is None
4551
assert d3.next_engine is None
4652
assert d1.main_engine is eng
4753
assert d2.main_engine is None
4854
assert d3.main_engine is eng
55+
assert eng.n_engines == 2
56+
57+
58+
def test_too_many_engines():
59+
N = 10
60+
61+
eng = MainEngine(backend=DummyEngine(), engine_list=[])
62+
eng.n_engines_max = N
63+
64+
for _ in range(N - 1):
65+
_util.insert_engine(eng, DummyEngine())
66+
67+
with pytest.raises(RuntimeError):
68+
_util.insert_engine(eng, DummyEngine())

0 commit comments

Comments
 (0)