Skip to content

Commit 6692ed8

Browse files
Fixed async model mismatch between NetQASM and SimulaQron, make tests
now all passed NetQASM uses Python generators, SimulaQron uses Twisted deferreds. handle_netqasm_message was fixed to bridge the two. This caused previously various weird errors including going into some infinite loop in which it kept allocating qubits
1 parent 1c70717 commit 6692ed8

File tree

5 files changed

+66
-8
lines changed

5 files changed

+66
-8
lines changed

simulaqron/netqasm_backend/executioner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def cmd_new(self, physical_address):
152152
q = VirtualQubitRef(q_id, int(time.time()), virt)
153153
self.factory.qubitList[q_id] = q
154154
self._logger.info("Requested new physical qubit %d)", q_id)
155-
155+
print(f"DEBUG: Added qubit {q_id} to qubitList", flush=True) # ADD THIS
156156
finally:
157157
self.factory._lock.release()
158158

simulaqron/netqasm_backend/qnodeos.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import logging
2+
import os
3+
import sys
4+
15
from typing import Optional, Dict, Callable, Generator, Any, List, Type
26

37
from netqasm.backend.executor import Executor
@@ -10,14 +14,28 @@
1014
from simulaqron.netqasm_backend.executioner import VanillaSimulaQronExecutioner
1115
from simulaqron.sdk.connection import (NewMessageType, GetQubitStateMessage,
1216
ReturnQubitStateMessage)
13-
17+
from simulaqron.settings import simulaqron_settings
1418

1519
class SubroutineHandler(QNodeController):
1620
def __init__(self, factory: "NetQASMFactory", instr_log_dir: Optional[str] = None, # noqa: F821
1721
flavour: Optional[Flavour] = None):
1822
super().__init__(factory.name, instr_log_dir=instr_log_dir, flavour=flavour)
1923

2024
self.factory = factory
25+
self._logger = logging.getLogger("QnodeController")
26+
# logging that the user will later see on the screen
27+
#stdout_file = open(f"/tmp/simulaqron-stdout-stderr-netqasmQ-{os.getpid()}.out.txt", "w")
28+
#sys.stdout = stdout_file
29+
#sys.stderr = stdout_file
30+
31+
# Force configure root logger with a handler, ensure our log output to this file
32+
# will allow us to trace back exactly where it came from in the codebase
33+
logging.basicConfig(
34+
format="%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(lineno)d:%(message)s",
35+
level=simulaqron_settings.log_level,
36+
force=True,
37+
stream=sys.stdout # send logs to the same file
38+
)
2139

2240
# Give a way for the executioner to return messages
2341
self._executor.add_return_msg_func(self._return_msg)
@@ -35,11 +53,52 @@ def protocol(self, protocol: Protocol):
3553

3654
@inlineCallbacks
3755
def handle_netqasm_message(self, msg_id: int, msg: Message):
38-
yield from super().handle_netqasm_message(
56+
"""
57+
Handle incoming NetQASM messages by bridging two async models.
58+
59+
NetQASM's executor uses Python generators (yield from) while SimulaQron
60+
uses Twisted deferreds (@inlineCallbacks). This method bridges them by:
61+
1. Running the parent's generator manually
62+
2. Detecting whether each yielded item is a Twisted Deferred or a nested generator
63+
3. For Deferreds: yielding to Twisted's reactor to await completion
64+
4. For nested generators: consuming them fully and capturing their return value
65+
66+
Without this bridge, nested generator return values (like physical_address
67+
from _instr_qalloc) would be lost, causing None to propagate through the system.
68+
This is also what caused the tests to fail, and probably other random weird things.
69+
"""
70+
print(f"DEBUG handle_netqasm_message: msg_id={msg_id}", flush=True)
71+
gen = super().handle_netqasm_message(
3972
msg_id=msg_id,
4073
msg=msg,
4174
)
4275

76+
# The following is a bug fix to properly wait for twisted deferreds
77+
78+
try:
79+
result = None
80+
iteration = 0
81+
while True:
82+
iteration = iteration + 1
83+
item = gen.send(result)
84+
if hasattr(item, 'addCallback'): # Deferred
85+
result = yield item
86+
elif hasattr(item, '__next__'): # Nested generator - consume it
87+
nested_result = None
88+
try:
89+
while True:
90+
nested_item = item.send(nested_result)
91+
if hasattr(nested_item, 'addCallback'):
92+
nested_result = yield nested_item
93+
else:
94+
nested_result = None
95+
except StopIteration as e:
96+
result = e.value # Get the return value from the generator
97+
else:
98+
result = None
99+
except StopIteration:
100+
pass
101+
43102
def _handle_get_qubit_state(self, get_quibit_state_msg: GetQubitStateMessage) -> Generator[Any, None, None]:
44103
assert isinstance(self._executor, VanillaSimulaQronExecutioner)
45104
casted_executor: VanillaSimulaQronExecutioner = self._executor

simulaqron/start/start_qnodeos.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from simulaqron.general.host_config import SocketsConfig
1717
from simulaqron.settings import simulaqron_settings, network_config
1818

19-
logger = logging.getLogger("start_vnode")
19+
logger = logging.getLogger("start_qnodeos")
2020

2121
_RETRY_TIME = 0.1
2222
_TIMEOUT = 10
@@ -131,7 +131,7 @@ def start_qnodeos(node_name: str, network_name: str = "default", log_level: str
131131
# Force configure root logger with a handler
132132
logging.basicConfig(
133133
format="%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(lineno)d:%(message)s",
134-
level=logging.DEBUG,
134+
level=simulaqron_settings.log_level,
135135
force=True,
136136
stream=stdout_file # send logs to the same file
137137
)

simulaqron/virtual_node/quantum.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ def __init__(self, node, register, simNum, num=0):
8080
self.T1 = settings.simulaqron_settings.t1
8181
self.last_accessed = time.time()
8282

83-
self._logger = get_netqasm_logger(
84-
f"{self.__class__.__name__}(node={node.name}, sim_num={simNum})"
85-
)
83+
self._logger = logging.getLogger()
8684

8785
@inlineCallbacks
8886
def lock(self):

simulaqron/virtual_node/virtual.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ def remote_new_qubit(self, ignore_max_qubits=False):
446446
yield self._get_global_lock()
447447

448448
try:
449+
print(f"DEBUG new_qubit: len(virtQubits)={len(self.virtQubits)}, maxQubits={self.maxQubits}", flush=True)
449450
if (len(self.virtQubits) >= self.maxQubits) and (not ignore_max_qubits):
450451
self._logger.error("Maximum number of virtual qubits reached.")
451452
raise NoQubitError("Max virtual qubits reached")

0 commit comments

Comments
 (0)