Skip to content

Commit 62a645a

Browse files
Fixed config file issues in which one could not pass a file using simulaqron start ... -network-config-file FILE
In doing so, we also demand the correct network config being passes to start_vnode etc instead of relying from unclear reads later on Some tests have been adapted since Network from network.py now takes another argument
1 parent 145ef74 commit 62a645a

File tree

18 files changed

+204
-76
lines changed

18 files changed

+204
-76
lines changed

examples/distributed/teleport/simulaqron_network.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
},
1212
{
1313
"Bob": {
14-
"app_socket": ["localhost", 8831],
15-
"qnodeos_socket": ["localhost", 8832],
16-
"vnode_socket": ["localhost", 8833]
14+
"app_socket": ["localhost", 9831],
15+
"qnodeos_socket": ["localhost", 9832],
16+
"vnode_socket": ["localhost", 9833]
1717
}
1818
}
1919
],
2020
"topology": null
2121
}
22-
]
22+
]

examples/distributed/teleport/teleport-alice.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set_simulator("simulaqron")
33

44
from simulaqron.settings import simulaqron_settings
5-
simulaqron_settings.network_config_file = "./networkConfig.json"
5+
simulaqron_settings.network_config_file = "./simulaqron_settings.json"
66

77
from netqasm.sdk.external import NetQASMConnection
88
from netqasm.sdk import Qubit, EPRSocket

examples/distributed/teleport/teleport-bob.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set_simulator("simulaqron")
33

44
from simulaqron.settings import simulaqron_settings
5-
simulaqron_settings.network_config_file = "./networkConfig.json"
5+
simulaqron_settings.network_config_file = "./simulaqron_settings.json"
66

77
from netqasm.sdk.external import NetQASMConnection
88
from netqasm.sdk import EPRSocket

examples/nativeMode/corrRNG/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Check if SimulaQron is already running
44
if [ ! -f ~/.simulaqron_pids/simulaqron_network_default.pid ]; then
5-
simulaqron start --nodes=Alice,Bob --force
5+
simulaqron start --nodes=Alice,Bob --network-config-file classicalNet.json
66
fi
77

88
python3 bobTest.py &

simulaqron/network.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import networkx as nx
3636
from multiprocess.context import ForkProcess as Process
3737
import logging
38+
from pathlib import Path
3839

3940
from simulaqron.settings import network_config
4041
from simulaqron.settings.network_config import NodeConfig
@@ -52,17 +53,22 @@
5253

5354

5455
class Network:
55-
def __init__(self, nodes: List[str], network_name: str = "default"):
56+
def __init__(self, nodes: List[str], network_config_file: Path, network_name: str = "default"):
5657
"""
5758
Used to spin up a simulated network.
5859
This class uses the network configuration loaded in the global network_config object and
5960
starts the nodes mentioned in the constructor of this class.
6061
6162
:param network_name: str
6263
The name of network to start. Defaults to "default".
64+
:param network_config_file: Path
65+
Path to network config file (required).
6366
:param nodes: list of str
6467
A list of strings with the node names to start.
6568
"""
69+
70+
self._network_config_file = network_config_file
71+
6672
self._running = False
6773
self.name = network_name
6874

@@ -111,10 +117,14 @@ def _setup_processes(self):
111117
"""
112118
for node in self._nodes_to_start:
113119
process_virtual = Process(
114-
target=start_vnode, args=(node.name, self.name, simulaqron_settings.log_level), name=f"VirtNode {node.name}"
120+
target=start_vnode,
121+
args=(node.name, self._network_config_file, self.name, simulaqron_settings.log_level),
122+
name=f"VirtNode {node.name}"
115123
)
116124
process_qnodeos = Process(
117-
target=start_qnodeos, args=(node.name, self.name, simulaqron_settings.log_level), name=f"QnodeOSNode {node.name}"
125+
target=start_qnodeos,
126+
args=(node.name, self._network_config_file, self.name, simulaqron_settings.log_level),
127+
name=f"QnodeOSNode {node.name}"
118128
)
119129
self.processes += [process_virtual, process_qnodeos]
120130

simulaqron/run/run.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from simulaqron.network import Network
2727
from simulaqron.sdk import SimulaQronConnection
2828
from simulaqron.settings import simulaqron_settings, network_config
29+
from simulaqron.settings import get_default_network_config_file
2930
from simulaqron.settings.simulaqron_config import SimBackend
3031

3132
logger = logging.getLogger()
@@ -183,19 +184,22 @@ def run_applications(
183184
app_instance.logging_cfg.comm_log_dir = timed_log_dir
184185

185186
results: List[Dict[str, Any]] = []
186-
if isinstance(network_cfg, str) or isinstance(network_cfg, PathLike):
187-
net_cfg = str(network_cfg)
188-
network_config.read_from_file(net_cfg)
189-
elif isinstance(network_cfg, Path):
190-
net_cfg = str(network_cfg.resolve())
191-
network_config.read_from_file(net_cfg)
192-
else:
193-
# If no network config file was given, we keep with the default-loaded (pwd, or home)
194-
pass
195187

196-
for _ in range(num_rounds):
197-
network = Network(network_name="default", nodes=network_config.get_node_names("default"))
188+
# Read the network config
189+
if network_cfg is None:
190+
network_cfg = get_default_network_config_file()
191+
192+
network_cfg = Path(network_cfg).resolve()
193+
network_config.read_from_file(network_cfg)
194+
network_config.read_from_file(network_cfg)
195+
198196

197+
for _ in range(num_rounds):
198+
network = Network(
199+
nodes=network_config.get_node_names("default"),
200+
network_config_file=network_cfg,
201+
network_name="default",
202+
)
199203
# Start the processes that support the simulator: QNodeOS + VirtualNode
200204
network.start()
201205

simulaqron/settings/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .network_config import (NetworkConfigBuilder, DEFAULT_SIMULAQRON_NETWORK_FILENAME,
2-
LOCAL_NETWORK_SETTINGS, HOME_NETWORK_SETTINGS)
2+
LOCAL_NETWORK_SETTINGS, HOME_NETWORK_SETTINGS,
3+
get_default_network_config_file)
34
from .simulaqron_config import (SimulaqronConfig, DEFAULT_SIMULAQRON_SETTINGS_FILENAME,
45
LOCAL_SIMULAQRON_SETTINGS, HOME_SIMULAQRON_SETTINGS)
56
from ._serialization import init_serialization

simulaqron/settings/network_config.py

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,21 @@ class NetworkConfigBuilder(JSONSerializerMixin):
169169
networks: Dict[str, NetworkConfig] = field(default_factory=dict)
170170
used_sockets: List[Tuple[str, int]] = field(default_factory=list)
171171

172+
173+
172174
def using_default_network(self):
175+
"""
176+
Load the embedded default network configuration.
177+
178+
Used for test isolation - always loads the embedded default
179+
regardless of local config files.
180+
"""
181+
173182
# We use the embedded default network here
174-
default_network_path = resources.files(simulaqron._default_config).joinpath("default_network.json")
183+
default_network_path = get_default_network_config_file(use_embedded=True)
184+
175185
new_builder = NetworkConfigBuilder()
176-
new_builder.read_from_file(Path(str(default_network_path)))
186+
new_builder.read_from_file(default_network_path)
177187
self.networks = new_builder.networks
178188
self.used_sockets = new_builder.used_sockets
179189

@@ -400,6 +410,7 @@ def read_from_file(self, file_path: PathLike | str):
400410
:param file_path: None or str
401411
If a file_path was specified upon __init__ this will be used if file_path is None.
402412
"""
413+
403414
if file_path is None:
404415
raise ValueError("No path specified to read the network configuration")
405416

@@ -429,23 +440,16 @@ def _deserialize_from_file(cls, file_path: Path) -> Self:
429440

430441
@classmethod
431442
def load_from_known_sources(cls) -> Self:
432-
cwd_networks_file = LOCAL_NETWORK_SETTINGS.resolve()
433-
home_networks_file = HOME_NETWORK_SETTINGS.resolve()
434-
435-
files_to_load = [cwd_networks_file, home_networks_file]
436-
437-
for file in files_to_load:
438-
try:
439-
if file.exists() and file.is_file():
440-
return cls._deserialize_from_file(file)
441-
except json.JSONDecodeError:
442-
# Nothing to do; try next one
443-
pass
444-
445-
# Ultimate case; we create a new config file in the home and load it
446-
default_net_cfg_path = Path(str(resources.files(simulaqron._default_config).joinpath("default_network.json")))
447-
shutil.copyfile(default_net_cfg_path, home_networks_file)
448-
return cls._deserialize_from_file(home_networks_file)
443+
"""
444+
Load config from the default config file.
445+
446+
Uses :func:`get_default_network_config_file` to resolve the path.
447+
448+
:return: Loaded network configuration.
449+
:rtype: NetworkConfigBuilder
450+
"""
451+
config_file = get_default_network_config_file()
452+
return cls._deserialize_from_file(config_file)
449453

450454
# Helper properties and pythonic accessors
451455
@property
@@ -515,3 +519,34 @@ def _check_socket_is_free(port: int) -> bool:
515519
except socket.error:
516520
return False
517521
return True
522+
523+
###########
524+
#
525+
526+
def get_default_network_config_file(use_embedded: bool = False) -> Path:
527+
"""
528+
Get the network config file path to use.
529+
530+
:param use_embedded: If True, always use the embedded default (for tests).
531+
If False, uses priority: LOCAL > HOME > embedded.
532+
:type use_embedded: bool
533+
:return: Path to the network config file.
534+
:rtype: Path
535+
"""
536+
537+
# We will use an embedded default which is used in testing
538+
if use_embedded:
539+
return Path(str(resources.files(simulaqron._default_config).joinpath("default_network.json")))
540+
541+
# Implements using the local directory setting as a priority
542+
if LOCAL_NETWORK_SETTINGS.exists():
543+
return LOCAL_NETWORK_SETTINGS
544+
if HOME_NETWORK_SETTINGS.exists():
545+
return HOME_NETWORK_SETTINGS
546+
547+
# Create default in HOME (matches load_from_known_sources behavior)
548+
# XXX I have mixed feelings we should do this, but I leave it for now
549+
default_net_cfg_path = Path(str(resources.files(simulaqron._default_config).joinpath("default_network.json")))
550+
HOME_NETWORK_SETTINGS.parent.mkdir(parents=True, exist_ok=True)
551+
shutil.copyfile(default_net_cfg_path, HOME_NETWORK_SETTINGS)
552+
return HOME_NETWORK_SETTINGS

simulaqron/simulaqron.py

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from simulaqron.network import Network
1313
from simulaqron.settings import LOCAL_SIMULAQRON_SETTINGS, LOCAL_NETWORK_SETTINGS, HOME_NETWORK_SETTINGS
14-
from simulaqron.settings import simulaqron_settings, network_config
14+
from simulaqron.settings import simulaqron_settings, get_default_network_config_file, network_config
1515
from simulaqron.settings.network_config import NodeConfig, DEFAULT_SIMULAQRON_NETWORK_FILENAME
1616
from simulaqron.settings.simulaqron_config import SimBackend
1717

@@ -38,12 +38,41 @@ def __init__(self, pidfile: Path):
3838
)
3939

4040
class SimulaQronDaemon(run.RunDaemon):
41-
def __init__(self, pidfile: Path, name: str, nodes: List[str]):
41+
"""
42+
Daemon process that runs a SimulaQron network in the background.
43+
44+
This daemon spawns virtual nodes and QNodeOS servers for each node
45+
in the network configuration. It runs until explicitly stopped.
46+
47+
Attributes
48+
----------
49+
name : str
50+
Name of the network (e.g., 'default').
51+
nodes : List[str]
52+
List of node names to start (e.g., ['Alice', 'Bob']).
53+
network_config_file : Path
54+
Path to the network configuration JSON file.
55+
"""
56+
def __init__(self, pidfile: Path, name: str, nodes: List[str], network_config_file: Path):
57+
"""
58+
Initialize the SimulaQron daemon.
59+
60+
:param pidfile: Path to the PID file used to track the daemon process.
61+
:type pidfile: Path
62+
:param name: Name of the network (e.g., 'default').
63+
:type name: str
64+
:param nodes: List of node names to start (e.g., ['Alice', 'Bob']).
65+
:type nodes: List[str]
66+
:param network_config_file: Path to the network configuration file.
67+
:type network_config_file: Path
68+
69+
"""
4270
super().__init__(
4371
pidfile=pidfile,
4472
)
4573
self.name = name
4674
self.nodes = nodes
75+
self.network_config_file = network_config_file
4776

4877
def run(self):
4978
"""Starts all nodes defined in netsim's config directory."""
@@ -52,7 +81,15 @@ def run(self):
5281
sys.stdout = open('/tmp/simulaqron.out', 'w', buffering=1)
5382
sys.stderr = open('/tmp/simulaqron.err', 'w', buffering=1)
5483

55-
network = Network(network_name=self.name, nodes=self.nodes)
84+
# Let's read the config file we should be working from
85+
network_config.read_from_file(self.network_config_file)
86+
87+
# Start the network to be simulated on this node
88+
network = Network(
89+
nodes=self.nodes,
90+
network_config_file=self.network_config_file,
91+
network_name=self.name,
92+
)
5693
network.start()
5794

5895
while True:
@@ -87,7 +124,7 @@ def version():
87124
help=f"Use the given network config file. Defaults to the file named " # noqa: E131
88125
f"'{DEFAULT_SIMULAQRON_NETWORK_FILENAME}' on the current directory.", # noqa: E131
89126
type=click.Path(exists=True, dir_okay=False, resolve_path=True, path_type=Path),
90-
default=LOCAL_NETWORK_SETTINGS
127+
default=None
91128
)
92129
@click.option(
93130
"--name",
@@ -112,7 +149,13 @@ def version():
112149
)
113150
def start(name: str, nrnodes: int, nodes: str, network_config_file: Path):
114151
"""Starts a network with the given parameters or from config files."""
115-
network_config.read_from_file(network_config_file)
152+
153+
# Read the network configuration from the indicated file
154+
if network_config_file is None:
155+
network_config_file = get_default_network_config_file()
156+
elif not network_config_file.exists():
157+
raise click.BadParameter(f"File '{network_config_file}' does not exist.")
158+
116159
pidfile = PID_FOLDER / f"simulaqron_network_{name}.pid"
117160
if pidfile.exists():
118161
logging.warning("Network with name %s is already running", name)
@@ -124,7 +167,10 @@ def start(name: str, nrnodes: int, nodes: str, network_config_file: Path):
124167
"this can be normal. Please check your invocation line if needed.")
125168
if nrnodes > 0 and len(nodes) < nrnodes:
126169
nodes += [f"Node{i}" for i in range(nrnodes - len(nodes))]
127-
d = SimulaQronDaemon(pidfile=pidfile, name=name, nodes=nodes)
170+
171+
# Let's start the simulaqron daemon. We will pass the config file so it will be available
172+
# in the child process and load the same config
173+
d = SimulaQronDaemon(pidfile=pidfile, name=name, nodes=nodes, network_config_file=network_config_file)
128174
try:
129175
d.start()
130176
except SystemExit as e:

simulaqron/start/start_qnodeos.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import logging
1010
from twisted.internet.error import ConnectionRefusedError, CannotListenError
1111
from twisted.spread import pb
12+
from pathlib import Path
1213

1314
from simulaqron.reactor import reactor
1415
from simulaqron.netqasm_backend.factory import NetQASMFactory
@@ -121,7 +122,24 @@ def sigterm_handler(_signo, _stack_frame):
121122
reactor.stop()
122123

123124

124-
def start_qnodeos(node_name: str, network_name: str = "default", log_level: str = "WARNING"):
125+
def start_qnodeos(node_name: str, network_config_file: Path, network_name: str = "default", log_level: str = "WARNING"):
126+
"""
127+
Start the QNPU that accepts NetQASM subroutines, and sends them as instructions to the SimulaQron virtual node
128+
backend over twisted PB (Native Mode SimulaQron).
129+
130+
:param name: Name of the node (e.g., 'Alice').
131+
:type name: str
132+
:param network_config_file: Path to network config file.
133+
:type network_config_file: Path
134+
:param network_name: Name of the network (e.g., 'default').
135+
:type network_name: str
136+
:param log_level: Logging level (e.g., 'DEBUG', 'INFO', 'WARNING').
137+
:type log_level: str
138+
"""
139+
140+
# Let's ensure we read the config file
141+
network_config.read_from_file(network_config_file)
142+
125143
if simulaqron_settings.log_level == logging.DEBUG:
126144
global stdout_file
127145
stdout_file = open(f"/tmp/simulaqron-stdout-stderr-qnos-{node_name}-{os.getpid()}.out.txt", "w")

0 commit comments

Comments
 (0)