Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2nsviz-graphstate-gen-example.cc
Go to the documentation of this file.
1/*-----------------------------------------------------------------------------
2 * Q2NS - Quantum Network Simulator
3 * Copyright (c) 2026 quantuminternet.it
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *---------------------------------------------------------------------------*/
9/**
10 * @file q2nsviz-graphstate-gen-example.cc
11 * @brief Distributed graph state generation across an orchestrator and three clients.
12 *
13 * An orchestrator locally builds a 5-qubit linear cluster state (graph state):
14 * q0 - q1 - q2 - q3 - q4
15 * It then distributes the even-indexed qubits (q0, q2, q4) to three clients
16 * and measures the odd-indexed qubits (q1, q3) in the X-basis, effectively
17 * transmitting entanglement onto the clients via single-qubit measurements.
18 *
19 * Timing model (illustrative):
20 * kSingleGate = 100 ns (single-qubit gate)
21 * kTwoQGate = 300 ns (two-qubit gate)
22 * kQDelay = 10 ns (quantum channel propagation, ~2 m fiber)
23 *
24 * CZ gate scheduling uses two non-overlapping layers:
25 * Layer 1 (non-overlapping): CZ(q0,q1), CZ(q2,q3)
26 * Layer 2 (non-overlapping): CZ(q1,q2), CZ(q3,q4)
27 * The 5 Hadamards (independent qubits) can be applied in parallel.
28 *
29 * Visualization output is written to
30 * examples/example_traces/q2nsviz-graphstate-gen-example.json and can be loaded in the
31 * q2nsviz viewer (src/q2ns/utils/q2nsviz-serve.sh).
32 *
33 * See docs/tutorials/tutorial-01.md for a detailed walkthrough.
34 */
35
36#include "ns3/core-module.h"
37#include "ns3/internet-module.h"
38#include "ns3/network-module.h"
39#include "ns3/point-to-point-module.h"
40#include "ns3/simulator.h"
41
42#include "ns3/q2ns-netcontroller.h"
43#include "ns3/q2ns-qgate.h"
44#include "ns3/q2ns-qnode.h"
45#include "ns3/q2ns-qubit.h"
46
47#include "ns3/q2nsviz-trace-writer.h"
48#include "ns3/q2nsviz-trace.h"
49
50#include <iostream>
51#include <vector>
52
53using namespace ns3;
54using namespace q2ns;
55
56static const uint16_t kCtrlPort = 9200;
57
58int main(int argc, char** argv) {
59 std::cout << "[DEMO] Distributed graph state generation starting\n";
60
61 RngSeedManager::SetSeed(1);
62 RngSeedManager::SetRun(1);
63
64 CommandLine cmd;
65 cmd.Parse(argc, argv);
66
67 // Visualization output
68 TraceWriter::Instance().Open("examples/example_traces/q2nsviz-graphstate-gen-example.json");
69
70 Time::SetResolution(Time::NS);
71
72 NetController net;
73 net.SetQStateBackend(QStateBackend::Ket);
74
75 // --- Instantiate nodes ---
76 Ptr<QNode> orch = net.CreateNode();
77 Ptr<QNode> client1 = net.CreateNode();
78 Ptr<QNode> client2 = net.CreateNode();
79 Ptr<QNode> client3 = net.CreateNode();
80
81 TraceCreateNode("Orchestrator", 50, 50);
82 TraceCreateNode("Client1", 25, 20);
83 TraceCreateNode("Client2", 75, 20);
84 TraceCreateNode("Client3", 50, 85);
85
86 // --- Timing constants ---
87 // Values are illustrative
88 const Time kSingleGate = NanoSeconds(100); // single-qubit gate (H)
89 const Time kTwoQGate = NanoSeconds(300); // two-qubit gate (CZ)
90 const Time kQDelay = NanoSeconds(10); // quantum link propagation (~2 m fiber)
91 const Time kClassical = kQDelay; // classical propagation = quantum (~2 m fiber)
92
93 // --- Quantum links ---
94 auto ch1 = net.InstallQuantumLink(orch, client1);
95 auto ch2 = net.InstallQuantumLink(orch, client2);
96 auto ch3 = net.InstallQuantumLink(orch, client3);
97 ch1->SetAttribute("Delay", TimeValue(kQDelay));
98 ch2->SetAttribute("Delay", TimeValue(kQDelay));
99 ch3->SetAttribute("Delay", TimeValue(kQDelay));
100
101 TraceCreateChannel("Orchestrator", "Client1", "quantum");
102 TraceCreateChannel("Orchestrator", "Client2", "quantum");
103 TraceCreateChannel("Orchestrator", "Client3", "quantum");
104
105 // --- Classical network ---
106 InternetStackHelper internet;
107 internet.Install(orch);
108 internet.Install(client1);
109 internet.Install(client2);
110 internet.Install(client3);
111
112 PointToPointHelper p2p;
113 p2p.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
114 p2p.SetChannelAttribute("Delay", StringValue("10ns"));
115
116 auto dev1 = p2p.Install(orch, client1);
117 auto dev2 = p2p.Install(orch, client2);
118 auto dev3 = p2p.Install(orch, client3);
119
120 Ipv4AddressHelper ipv4;
121 ipv4.SetBase("10.1.1.0", "255.255.255.0");
122 auto ifs1 = ipv4.Assign(dev1);
123 ipv4.SetBase("10.1.2.0", "255.255.255.0");
124 auto ifs2 = ipv4.Assign(dev2);
125 ipv4.SetBase("10.1.3.0", "255.255.255.0");
126 auto ifs3 = ipv4.Assign(dev3);
127 Ipv4GlobalRoutingHelper::PopulateRoutingTables();
128 const Ipv4Address kClient1Addr = ifs1.GetAddress(1);
129 const Ipv4Address kClient2Addr = ifs2.GetAddress(1);
130 const Ipv4Address kClient3Addr = ifs3.GetAddress(1);
131
132 TraceCreateChannel("Orchestrator", "Client1", "classical");
133 TraceCreateChannel("Orchestrator", "Client2", "classical");
134 TraceCreateChannel("Orchestrator", "Client3", "classical");
135
136 // ---------------------------------------------------------------------------
137 // Classical control: Orchestrator sends X-basis measurement outcomes to the
138 // two neighbors of each measured qubit so clients can apply byproduct corrections.
139 // q1 measured -> outcome m1 sent to Client1 (holds q0) and Client2 (holds q2)
140 // q3 measured -> outcome m3 sent to Client2 (holds q2) and Client3 (holds q4)
141 // ---------------------------------------------------------------------------
142
143 // Client receive sockets for classical outcome messages from Orchestrator
144 auto c1RxSock = Socket::CreateSocket(client1, UdpSocketFactory::GetTypeId());
145 c1RxSock->Bind(InetSocketAddress(Ipv4Address::GetAny(), kCtrlPort));
146 c1RxSock->SetRecvCallback([&](Ptr<Socket> sock) {
147 Address from;
148 while (Ptr<Packet> p = sock->RecvFrom(from)) {
149 uint8_t outcome;
150 p->CopyData(&outcome, 1);
151 int m = outcome & 1;
152 TraceNodeText("Client1", StrCat("q1 outcome m=", m, " received - q0 ready"));
153 TraceSetBitColor("q0", "#44AA99");
154 std::cout << "[CLIENT1] q1 outcome m=" << m << " - q0 ready\n";
155 }
156 });
157
158 int c2OutcomesRxd = 0;
159 auto c2RxSock = Socket::CreateSocket(client2, UdpSocketFactory::GetTypeId());
160 c2RxSock->Bind(InetSocketAddress(Ipv4Address::GetAny(), kCtrlPort));
161 c2RxSock->SetRecvCallback([&](Ptr<Socket> sock) {
162 Address from;
163 while (Ptr<Packet> p = sock->RecvFrom(from)) {
164 uint8_t outcome;
165 p->CopyData(&outcome, 1);
166 int m = outcome & 1;
167 ++c2OutcomesRxd;
168 TraceNodeText("Client2", StrCat("outcome m=", m, " received (", c2OutcomesRxd, "/2)"));
169 if (c2OutcomesRxd == 2) {
170 TraceNodeText("Client2", "Both outcomes received - q2 ready");
171 TraceSetBitColor("q2", "#44AA99");
172 }
173 std::cout << "[CLIENT2] outcome m=" << m << " (" << c2OutcomesRxd << "/2) - q2\n";
174 }
175 });
176
177 auto c3RxSock = Socket::CreateSocket(client3, UdpSocketFactory::GetTypeId());
178 c3RxSock->Bind(InetSocketAddress(Ipv4Address::GetAny(), kCtrlPort));
179 c3RxSock->SetRecvCallback([&](Ptr<Socket> sock) {
180 Address from;
181 while (Ptr<Packet> p = sock->RecvFrom(from)) {
182 uint8_t outcome;
183 p->CopyData(&outcome, 1);
184 int m = outcome & 1;
185 TraceNodeText("Client3", StrCat("q3 outcome m=", m, " received - q4 ready"));
186 TraceSetBitColor("q4", "#44AA99");
187 std::cout << "[CLIENT3] q3 outcome m=" << m << " - q4 ready\n";
188 }
189 });
190
191 // Pre-create orchestrator transmit sockets before simulation runs.
192 // orchToC2 handles two sends (m1 and m3) as independent UDP datagrams.
193 auto orchToC1 = Socket::CreateSocket(orch, UdpSocketFactory::GetTypeId());
194 orchToC1->Connect(InetSocketAddress(kClient1Addr, kCtrlPort));
195 auto orchToC2 = Socket::CreateSocket(orch, UdpSocketFactory::GetTypeId());
196 orchToC2->Connect(InetSocketAddress(kClient2Addr, kCtrlPort));
197 auto orchToC3 = Socket::CreateSocket(orch, UdpSocketFactory::GetTypeId());
198 orchToC3->Connect(InetSocketAddress(kClient3Addr, kCtrlPort));
199
200 // Client qubit arrival callbacks: trace arrival and set pending color.
201 // Corrections are triggered by the classical outcome messages.
202 // The real corrections are NOT implemented in this example
203 client1->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
204 TraceSetBitColor(q->GetLabel(), "#AACCFF");
205 TraceNodeText("Client1", StrCat(q->GetLabel(), " arrived - awaiting correction basis"));
206 std::cout << "[CLIENT1] " << q->GetLabel() << " arrived\n";
207 });
208 client2->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
209 TraceSetBitColor(q->GetLabel(), "#AACCFF");
210 TraceNodeText("Client2", StrCat(q->GetLabel(), " arrived - awaiting correction basis"));
211 std::cout << "[CLIENT2] " << q->GetLabel() << " arrived\n";
212 });
213 client3->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
214 TraceSetBitColor(q->GetLabel(), "#AACCFF");
215 TraceNodeText("Client3", StrCat(q->GetLabel(), " arrived - awaiting correction basis"));
216 std::cout << "[CLIENT3] " << q->GetLabel() << " arrived\n";
217 });
218
219 // --- Qubit storage ---
220 static const int kNumQubits = 5;
221 std::vector<std::shared_ptr<Qubit>> qubits;
222
223 // --- Simulation events ---
224
225 // --- Create qubits ---
226 Simulator::Schedule(MicroSeconds(1), [&] {
227 Trace("Orchestrator creates ", kNumQubits, " qubits");
228 TraceNodeText("Orchestrator", "Preparing 5-qubit linear cluster state");
229 for (int i = 0; i < kNumQubits; ++i) {
230 auto q = orch->CreateQubit();
231 q->SetLabel("q" + std::to_string(i));
232 qubits.push_back(q);
233 TraceCreateBit("Orchestrator", q->GetLabel(), "quantum", "#88CCEE");
234 }
235
236 // Hadamard on all qubits (parallelizable)
237 Simulator::Schedule(kSingleGate, [&] {
238 Trace("Orchestrator applies H to all qubits");
239 TraceNodeText("Orchestrator", "H layer (all qubits, parallel)");
240 for (const auto& q : qubits) {
241 orch->Apply(q2ns::gates::H(), {q});
242 TraceSetBitColor(q->GetLabel(), "#bc71eb");
243 }
244
245 // CZ layer 1 - CZ(q0,q1) and CZ(q2,q3)
246 Simulator::Schedule(kTwoQGate, [&] {
247 Trace("CZ layer 1: CZ(q0,q1) CZ(q2,q3)");
248 TraceNodeText("Orchestrator", "CZ layer 1");
249 orch->Apply(q2ns::gates::CZ(), {qubits[0], qubits[1]});
250 TraceEntangle({qubits[0]->GetLabel(), qubits[1]->GetLabel()}, kTwoQGate);
251 orch->Apply(q2ns::gates::CZ(), {qubits[2], qubits[3]});
252 TraceEntangle({qubits[2]->GetLabel(), qubits[3]->GetLabel()}, kTwoQGate);
253
254 // CZ layer 2 - CZ(q1,q2) and CZ(q3,q4)
255 Simulator::Schedule(kTwoQGate, [&] {
256 Trace("CZ layer 2: CZ(q1,q2) CZ(q3,q4) - cluster state ready");
257 TraceNodeText("Orchestrator", "CZ layer 2 - cluster state ready");
258 orch->Apply(q2ns::gates::CZ(), {qubits[1], qubits[2]});
259 TraceEntangle({qubits[1]->GetLabel(), qubits[2]->GetLabel()}, kTwoQGate);
260 orch->Apply(q2ns::gates::CZ(), {qubits[3], qubits[4]});
261 TraceEntangle({qubits[3]->GetLabel(), qubits[4]->GetLabel()}, kTwoQGate);
262
263 // Send even-indexed qubits to clients
264 Simulator::Schedule(kSingleGate, [&] {
265 Trace("Orchestrator sends q0, q2, q4 to clients");
266 TraceNodeText("Orchestrator", "Sending q0/q2/q4 to clients");
267 orch->Send(qubits[0], client1->GetId());
268 orch->Send(qubits[2], client2->GetId());
269 orch->Send(qubits[4], client3->GetId());
270
271 auto t0 = Simulator::Now();
272 auto t1 = t0 + kQDelay;
273 TraceSendBit("q0", "Orchestrator", "Client1", "quantum", t0, t1);
274 TraceSendBit("q2", "Orchestrator", "Client2", "quantum", t0, t1);
275 TraceSendBit("q4", "Orchestrator", "Client3", "quantum", t0, t1);
276
277 // Measure q1 and q3 in X-basis once client qubits have arrived
278 Simulator::Schedule(kQDelay + kSingleGate, [&] {
279 Trace("Orchestrator measures q1, q3 in X-basis");
280 TraceNodeText("Orchestrator", "Measuring q1, q3 (X-basis)");
281
282 // Measure q1: neighbors are q0 (Client1) and q2 (Client2)
283 auto m1 = orch->Measure(qubits[1], Basis::X);
284 Trace("q1 X-measure outcome = ", m1);
285 TraceGraphMeasure("q1", kSingleGate, "X");
286 TraceRemoveBit("q1");
287
288 // Measure q3: neighbors are q2 (Client2) and q4 (Client3)
289 auto m3 = orch->Measure(qubits[3], Basis::X);
290 Trace("q3 X-measure outcome = ", m3);
291 TraceGraphMeasure("q3", kSingleGate, "X");
292 TraceRemoveBit("q3");
293
294 TraceNodeText("Orchestrator", "Measurements complete - sending outcomes to clients");
295 const auto tNow = Simulator::Now();
296
297 uint8_t m1b = (uint8_t) (m1 & 1);
298 uint8_t m3b = (uint8_t) (m3 & 1);
299
300 // Send m1 to Client1 (q0 is neighbor of q1)
301 orchToC1->Send(Create<Packet>(&m1b, 1));
302 TraceSendPacket("Orchestrator", "Client1", tNow, tNow + kClassical,
303 StrCat("q1 outcome: m=", m1));
304
305 // Send m1 to Client2 (q2 is neighbor of q1)
306 orchToC2->Send(Create<Packet>(&m1b, 1));
307 TraceSendPacket("Orchestrator", "Client2", tNow, tNow + kClassical,
308 StrCat("q1 outcome: m=", m1));
309
310 // Send m3 to Client2 (q2 is neighbor of q3)
311 orchToC2->Send(Create<Packet>(&m3b, 1));
312 TraceSendPacket("Orchestrator", "Client2", tNow, tNow + kClassical,
313 StrCat("q3 outcome: m=", m3));
314
315 // Send m3 to Client3 (q4 is neighbor of q3)
316 orchToC3->Send(Create<Packet>(&m3b, 1));
317 TraceSendPacket("Orchestrator", "Client3", tNow, tNow + kClassical,
318 StrCat("q3 outcome: m=", m3));
319 });
320 });
321 });
322 });
323 });
324 });
325
326 Simulator::Stop(MilliSeconds(1));
327 Simulator::Run();
328 Simulator::Destroy();
329
331 std::cout << "[DONE] Distributed graph state generation finished\n";
332 return 0;
333}
static TraceWriter & Instance()
void Open(const std::string &path)
Main user-facing facade for creating and configuring a quantum network.
ns3::Ptr< QNode > CreateNode(const std::string &label="")
Create a QNode with an optional human-readable label.
ns3::Ptr< QChannel > InstallQuantumLink(ns3::Ptr< QNode > a, ns3::Ptr< QNode > b)
Install a duplex quantum link between two nodes.
void SetQStateBackend(QStateBackend b)
Set the default backend used for newly created quantum states.
QGate CZ(ns3::Time d=ns3::Seconds(0))
Return the CZ gate descriptor.
Definition q2ns-qgate.h:419
QGate H(ns3::Time d=ns3::Seconds(0))
Return the Hadamard gate descriptor.
Definition q2ns-qgate.h:383
int main()
uint8_t m1
static const uint16_t kCtrlPort
std::string StrCat(Ts &&... parts)
void Trace(const std::string &text)
void TraceNodeText(const std::string &node, const std::string &text)
void TraceEntangle(const std::vector< std::string > &bits)
void TraceRemoveBit(const std::string &bitLabel, uint64_t t_ns=NowNs())
void TraceSendPacket(const std::string &from, const std::string &to, uint64_t t0_ns, uint64_t t1_ns, const std::string &label)
void TraceCreateBit(const std::string &node, const std::string &bitLabel, const std::string &kind, const std::string &color)
void TraceGraphMeasure(const std::string &bitLabel, const std::string &base="Z", const std::string &supportNode="")
void TraceCreateNode(const std::string &label, int xPct, int yPct)
void TraceCreateChannel(const std::string &from, const std::string &to, const std::string &kind)
void TraceSendBit(const std::string &bitLabel, const std::string &from, const std::string &to, const std::string &kind, uint64_t t0_ns, uint64_t t1_ns)
void TraceSetBitColor(const std::string &bitLabel, const std::string &color)