Anders and Briegel in Python
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
17KB

  1. // graphsim.cpp
  2. #include "graphsim.h"
  3. #include <utility>
  4. #include <ctime>
  5. //!Random coin toss. Change here to insert your favorite RNG.
  6. int bool_rand (void) {
  7. return random () > RAND_MAX/2;
  8. }
  9. //! Instantiate a quantum register with 'numQubits' qubits, initally all in state |0>.
  10. /*! If randomize > -1 the RNG will be seeded with the current time plus the value
  11. of randomize. (Otherwise, it is not seeded.) That the value of randomize is
  12. added to the seed is useful in parallel processing settings where you want to
  13. ensure different seeds. (If you call this from Python, remember, that Python's
  14. RNG is not seeded.) */
  15. GraphRegister::GraphRegister (VertexIndex numQubits, int randomize)
  16. : vertices (numQubits)
  17. {
  18. if (randomize > -1) {
  19. srandom (time(NULL) + randomize);
  20. }
  21. }
  22. //! Copy constructor
  23. /*! Clones a register */
  24. GraphRegister::GraphRegister (GraphRegister& gr)
  25. : vertices (gr.vertices)
  26. {
  27. }
  28. //! Add an edge to the graph underlying the state.
  29. void GraphRegister::add_edge (VertexIndex v1, VertexIndex v2) {
  30. assert (v1 != v2);
  31. vertices[v1].neighbors.insert (v2);
  32. vertices[v2].neighbors.insert (v1);
  33. //D cerr << "adding edge " << v1 << " - " << v2 << endl;
  34. }
  35. //! Delete an edge to the graph underlying the state.
  36. void GraphRegister::del_edge (VertexIndex v1, VertexIndex v2) {
  37. DBGOUT ("deling edge " << v1 << " - " << v2 << endl);
  38. vertices[v1].neighbors.erase (v2);
  39. vertices[v2].neighbors.erase (v1);
  40. }
  41. //! Toggle an edge to the graph underlying the state.
  42. /*! (i.e. add it if not present, and delete it if present.) */
  43. void GraphRegister::toggle_edge (VertexIndex v1, VertexIndex v2)
  44. {
  45. int n1 = vertices[v1].neighbors.erase (v2);
  46. if (n1 == 1) {
  47. vertices[v2].neighbors.erase (v1);
  48. } else {
  49. assert (n1 == 0);
  50. add_edge (v1, v2);
  51. }
  52. }
  53. //! Create the Stabilizer of the state.
  54. /*! This is useful to print out the stabilizer (or to compare with CHP).
  55. You can also use print_stabilizer.*/
  56. Stabilizer& GraphRegister::get_full_stabilizer (void) const
  57. {
  58. hash_set<VertexIndex> all_qubits;
  59. for (VertexIterConst i = vertices.begin(); i != vertices.end(); i++) {
  60. all_qubits.insert (i-vertices.begin());
  61. }
  62. Stabilizer *s = new Stabilizer (*this, all_qubits);
  63. return *s;
  64. }
  65. //! Prints out the description of the current state
  66. /*! in terms of adjacency lists of the graph and the VOps.*/
  67. void GraphRegister::print_adj_list (ostream& os) const
  68. {
  69. for (VertexIndex i = 0; i < vertices.size(); i++) {
  70. print_adj_list_line (os, i);
  71. }
  72. }
  73. //! Prints the line for Vertex i in the adjacency list representation of the state.
  74. void GraphRegister::print_adj_list_line (ostream& os, VertexIndex i) const
  75. {
  76. os << "Vertex " << i << ": VOp "
  77. << vertices[i].byprod.get_name() << ", neighbors ";
  78. for (VtxIdxIterConst j = vertices[i].neighbors.begin();
  79. j != vertices[i].neighbors.end(); j++) {
  80. os << *j << " ";
  81. }
  82. os << endl;
  83. }
  84. //! Print the current state in stabilizer representation.
  85. void GraphRegister::print_stabilizer (ostream &os) const
  86. {
  87. get_full_stabilizer().print (os);
  88. }
  89. /*! Use the cphase look-up table. This is called by cphase after VOps that do not
  90. commute with the cphase gate have been removed as far as possible. */
  91. void GraphRegister::cphase_with_table (VertexIndex v1, VertexIndex v2)
  92. {
  93. // Structure of the table:
  94. // first index: whether there was an edge between the operands before
  95. // (0=no, 1= yes)
  96. // second and third index: byprod op of v1 and v2
  97. // third index: information to obtain:
  98. // 0= whether after the cphase there is an edges
  99. // 1,2= new values of the byprod ops of v1 and v2Id
  100. static const unsigned short cphase_tbl[2][24][24][3] =
  101. #include "cphase.tbl"
  102. ;
  103. #ifdef DEBUGOUTPUT
  104. DBGOUT ("cphase_with_table called on:\n");
  105. print_adj_list_line (cout, v1);
  106. print_adj_list_line (cout, v2);
  107. #endif
  108. ConnectionInfo ci = getConnectionInfo (v1, v2);
  109. unsigned op1 = vertices[v1].byprod.op;
  110. unsigned op2 = vertices[v2].byprod.op;
  111. // The table must only be used if a vertex has either no
  112. // non-operand neighbors, or a diagonal byprod op
  113. assert ((!ci.non1) || vertices[v1].byprod.is_diagonal());
  114. assert ((!ci.non2) || vertices[v2].byprod.is_diagonal());
  115. if (cphase_tbl[ci.wasEdge][op1][op2][0]) {
  116. add_edge (v1, v2);
  117. DBGOUT ("adding edge" << endl);
  118. } else {
  119. del_edge (v1, v2);
  120. DBGOUT ("deling edge" << endl);
  121. }
  122. vertices[v1].byprod.op = cphase_tbl[ci.wasEdge][op1][op2][1];
  123. vertices[v2].byprod.op = cphase_tbl[ci.wasEdge][op1][op2][2];
  124. #ifdef DEBUGOUTPUT
  125. DBGOUT ("cphase_with_table: after:\n");
  126. print_adj_list_line (cout, v1);
  127. print_adj_list_line (cout, v2);
  128. #endif
  129. // The condition above must also hold afterwards:
  130. ci = getConnectionInfo (v1, v2);
  131. assert ((!ci.non1) || vertices[v1].byprod.is_diagonal());
  132. assert ((!ci.non2) || vertices[v2].byprod.is_diagonal());
  133. }
  134. /*! Check whether the qubits are connected to each other and to non-operand vertices.*/
  135. ConnectionInfo GraphRegister::getConnectionInfo
  136. (VertexIndex v1, VertexIndex v2) {
  137. ConnectionInfo ci;
  138. ci.wasEdge =
  139. vertices[v1].neighbors.find(v2) != vertices[v1].neighbors.end();
  140. if (! ci.wasEdge) {
  141. ci.non1 = vertices[v1].neighbors.size() >= 1;
  142. ci.non2 = vertices[v2].neighbors.size() >= 1;
  143. } else {
  144. ci.non1 = vertices[v1].neighbors.size() >= 2;
  145. ci.non2 = vertices[v2].neighbors.size() >= 2;
  146. }
  147. return ci;
  148. }
  149. //! Do a conditional phase gate between the two qubits.
  150. void GraphRegister::cphase (VertexIndex v1, VertexIndex v2)
  151. {
  152. // If there are non-operand neighbors, we can use neighborhood inversion
  153. // to remove the byprod operators.
  154. // These will store whether the operand vertices have nonoperand neighbors.
  155. #ifdef DEBUGOUTPUT
  156. DBGOUT ("before cphase between " << v1 << " and " << v2 << endl);
  157. print_adj_list_line (cout, v1);
  158. print_adj_list_line (cout, v2);
  159. #endif
  160. ConnectionInfo ci = getConnectionInfo (v1, v2);
  161. if (ci.non1) {
  162. DBGOUT ("cphase: left vertex has NONs -> putting it to Id\n");
  163. remove_byprod_op (v1, v2);
  164. }
  165. ci = getConnectionInfo (v1, v2);
  166. if (ci.non2) {
  167. DBGOUT ("cphase: right vertex has NONs -> putting it to Id\n");
  168. remove_byprod_op (v2, v1);
  169. }
  170. ci = getConnectionInfo (v1, v2);
  171. if (ci.non1 && !vertices[v1].byprod.is_diagonal()) {
  172. // this can happen if v1 was first skipped
  173. DBGOUT ("cphase: left one needs treatment again -> putting it to Id\n");
  174. remove_byprod_op (v1, v2);
  175. }
  176. cphase_with_table (v1, v2);
  177. }
  178. //! Do a controlled not gate between the vertices vc (control) and vt (target).
  179. void GraphRegister::cnot (VertexIndex vc, VertexIndex vt) {
  180. hadamard (vt);
  181. cphase (vc, vt);
  182. hadamard (vt);
  183. }
  184. //! This structure is needed only by toggle_edges
  185. struct Edge: public pair<VertexIndex, VertexIndex> {
  186. Edge (const VertexIndex a, const VertexIndex b) {
  187. if (a < b) {
  188. first = a; second = b;
  189. } else {
  190. first = b; second = a;
  191. }
  192. }
  193. };
  194. //! This structure is needed only by toggle_edges
  195. struct edge_hash {
  196. size_t operator() (const Edge& e) const
  197. {
  198. return e.first << 16 ^ e.second;
  199. };
  200. };
  201. //! Toggles the edges between the vertex sets vs1 and vs2.
  202. /*! The function takes extra care not to invert an edge twice. If vs1 and
  203. vs2 are disjunct, this cannot happen and we do not need the function.
  204. If vs1 == v2s, we can do without, too. */
  205. void GraphRegister::toggle_edges (const hash_set<VertexIndex> vs1,
  206. const hash_set<VertexIndex> vs2)
  207. {
  208. hash_set<Edge, edge_hash> procd_edges;
  209. for (VtxIdxIterConst i = vs1.begin(); i != vs1.end(); i++) {
  210. for (VtxIdxIterConst j = vs2.begin(); j != vs2.end(); j++) {
  211. if ((*i != *j) &&
  212. (procd_edges.find (Edge (*i, *j)) == procd_edges.end())) {
  213. procd_edges.insert (Edge (*i, *j));
  214. toggle_edge (*i, *j);
  215. }
  216. }
  217. }
  218. }
  219. //! Get the adjacency matrix as an edgelist
  220. std::vector<double> GraphRegister::adj_list ()
  221. {
  222. std::vector<double> out;
  223. for (int i = 0; i < 100; ++i)
  224. {
  225. out.push_back(i);
  226. }
  227. return out;
  228. }
  229. //! Measure the bare graph state in the Z basis.
  230. int GraphRegister::graph_Z_measure (VertexIndex v, int force)
  231. {
  232. int res;
  233. if (force == -1) {
  234. res = bool_rand ();
  235. } else {
  236. res = force;
  237. }
  238. DBGOUT ("gZm" << v << "," << res << " ");
  239. #ifdef DEBUGOUTPUT
  240. print_adj_list_line (cout, v);
  241. #endif
  242. hash_set<VertexIndex> nbg = vertices[v].neighbors;
  243. for (VtxIdxIter i = nbg.begin(); i != nbg.end(); i++) {
  244. del_edge (v, *i);
  245. if (res) {
  246. vertices[*i].byprod = vertices[*i].byprod * lco_Z;
  247. }
  248. }
  249. if (! res) {
  250. vertices[v].byprod = vertices[v].byprod * lco_H;
  251. } else {
  252. vertices[v].byprod = vertices[v].byprod * lco_X * lco_H;
  253. }
  254. return res;
  255. }
  256. //! Measure the bare graph state in the Y basis.
  257. int GraphRegister::graph_Y_measure (VertexIndex v, int force)
  258. {
  259. int res;
  260. if (force == -1) {
  261. res = bool_rand ();
  262. } else {
  263. res = force;
  264. }
  265. DBGOUT ("gYm" << v << "," << res << " ");
  266. hash_set<VertexIndex> vnbg = vertices[v].neighbors;
  267. for (VtxIdxIter i = vnbg.begin(); i != vnbg.end(); i++) {
  268. if (res) {
  269. vertices[*i].byprod = vertices[*i].byprod * lco_spiZ;
  270. } else {
  271. vertices[*i].byprod = vertices[*i].byprod * lco_smiZ;
  272. }
  273. }
  274. vnbg.insert (v); // Now, vnbg is the set of v and its neighbours.
  275. for (VtxIdxIter i = vnbg.begin(); i != vnbg.end(); i++) {
  276. for (VtxIdxIter j = i; j != vnbg.end(); j++) {
  277. if (i != j) {
  278. toggle_edge (*i, *j);
  279. }
  280. }
  281. }
  282. if (! res) {
  283. // Messergebnis: +|0y>
  284. vertices[v].byprod = vertices[v].byprod * lco_S;
  285. } else {
  286. // Messergebnis: -|0y>
  287. vertices[v].byprod = vertices[v].byprod * lco_S.herm_adjoint();
  288. }
  289. return res;
  290. }
  291. //! Measure the bare graph state in the X basis.
  292. int GraphRegister::graph_X_measure (VertexIndex v, bool* determined,
  293. int force)
  294. {
  295. if (vertices[v].neighbors.size () == 0) {
  296. //not entangled qubit => result always 0
  297. DBGOUT ("gXm" << v << ",D ");
  298. if (determined != NULL)
  299. *determined = true;
  300. return 0;
  301. }
  302. if (determined != NULL)
  303. *determined = false;
  304. // entangled qubit => let's get on with the complicated procedure
  305. // throw a die:
  306. int res;
  307. if (force == -1) {
  308. res = bool_rand ();
  309. } else {
  310. res = force;
  311. }
  312. DBGOUT ("gXm" << v << "," << res << " ");
  313. DBGOUT ("forced: " << force << endl);
  314. VertexIndex vb = *vertices[v].neighbors.begin(); // the choosen vertex
  315. //D cerr << "vb = " << vb << endl;
  316. // preparation step: store the neighborhood of v and vb
  317. hash_set<VertexIndex> vn = vertices[v].neighbors;
  318. hash_set<VertexIndex> vbn = vertices[vb].neighbors;
  319. // First, put the byproduct ops:
  320. if (! res) {
  321. // measured a |+>:
  322. // lco_spiY on vb
  323. vertices[vb].byprod = vertices[vb].byprod * lco_spiY;
  324. // Z on all in nbg(v) \ nbg(vb) \ {vb}
  325. for (VtxIdxIter i = vertices[v].neighbors.begin();
  326. i != vertices[v].neighbors.end(); i++) {
  327. if (*i != vb &&
  328. vertices[vb].neighbors.find(*i) == vertices[vb].neighbors.end()) {
  329. vertices[*i].byprod = vertices[*i].byprod * lco_Z;
  330. }
  331. }
  332. } else {
  333. // measured a |->:
  334. // lco_smiY on vb, and lco_Z on v:
  335. vertices[vb].byprod = vertices[vb].byprod * lco_smiY;
  336. vertices[v].byprod = vertices[v].byprod * lco_Z;
  337. // Z on all in nbg(vb) \ nbg(v) \ {v}
  338. for (VtxIdxIter i = vertices[vb].neighbors.begin();
  339. i != vertices[vb].neighbors.end(); i++) {
  340. if (*i != v &&
  341. vertices[v].neighbors.find(*i) == vertices[v].neighbors.end()) {
  342. vertices[*i].byprod = vertices[*i].byprod * lco_Z;
  343. }
  344. }
  345. }
  346. // Toggling the edges in three steps
  347. // STEP 1: complement with Edges (nbg(v), nbg(vb)):
  348. toggle_edges (vn, vbn);
  349. // STEP 2: complement with the complete subgraph induced by the
  350. // intersection of nbg(v) and nbg(vb):
  351. // First, make the intersection
  352. hash_set<VertexIndex> isc;
  353. for (VtxIdxIter i = vn.begin(); i != vn.end(); i++) {
  354. if (vbn.find(*i) != vbn.end()) {
  355. isc.insert (*i);
  356. }
  357. }
  358. // Now, toggle the edges
  359. for (VtxIdxIter i = isc.begin(); i != isc.end(); i++) {
  360. for (VtxIdxIter j = i; j != isc.end(); j++) {
  361. if (*i != *j) {
  362. toggle_edge (*i, *j);
  363. }
  364. }
  365. }
  366. // STEP 3: Toggle all edges from vb to nbg(v) \ {vb}
  367. for (VtxIdxIter i = vn.begin(); i != vn.end(); i++) {
  368. if (*i != vb) {
  369. toggle_edge (vb, *i);
  370. }
  371. }
  372. return res;
  373. }
  374. //! Measure qubit v in basis 'basis'.
  375. /* For basis, pass a LocCliffOp object which has to be equal to one of lco_X, lco_Y
  376. or lco_Z. If you want to now, whether the result was choosen at random or determined
  377. by the state, pass a bool pointer in which this information will be written.
  378. If you want to force the result to be a certain value, pass 0 or 1 to 'force'. This only
  379. works, if the result is not determined. If it is, 'force' is ignored.*/
  380. int GraphRegister::measure (VertexIndex v, LocCliffOp basis, bool* determined,
  381. int force)
  382. {
  383. assert (basis.op >= lco_X.op && basis.op <= lco_Z.op);
  384. if (determined != NULL) {
  385. *determined = false;
  386. }
  387. assert (force >= -1 && force <= 1);
  388. LocCliffOp basis_orig = basis;
  389. RightPhase rp = basis.conjugate (vertices[v].byprod.herm_adjoint());
  390. assert (rp == rp_p1 || rp == rp_m1);
  391. if (force != -1 && rp == rp_m1) {
  392. force = force ^ 0x01;
  393. }
  394. int res;
  395. switch (basis.op) {
  396. case 1 /* lco_X */: res = graph_X_measure (v, determined, force); break;
  397. case 2 /* lco_Y */: res = graph_Y_measure (v, force); break;
  398. case 3 /* lco_Z */: res = graph_Z_measure (v, force); break;
  399. default: exit (1);
  400. }
  401. if (rp == rp_m1) {
  402. res = ! res;
  403. } else {
  404. assert (rp == rp_p1);
  405. }
  406. // check: the measured vertex should be singled out:
  407. assert (vertices[v].neighbors.size() == 0);
  408. // Check that the vertex is now in the correct eigenstate:
  409. LocCliffOp assert_op = lco_X;
  410. assert (assert_op.conjugate (vertices[v].byprod) == (res ? rp_m1 : rp_p1));
  411. assert (assert_op == basis_orig);
  412. return res;
  413. }
  414. //! Do a neighborhood inversion (i.e. local complementation) about vertex v.
  415. /* This changes the state's graph representation but not the state itself, as the
  416. necessary correction to the VOps are applied. */
  417. void GraphRegister::invert_neighborhood (VertexIndex v)
  418. {
  419. // Invert the neighborhood:
  420. #ifdef DEBUGOUTPUT
  421. DBGOUT ("Inverting about ");
  422. print_adj_list_line (cout, v);
  423. #endif
  424. hash_set<VertexIndex> vn = vertices[v].neighbors;
  425. for (VtxIdxIter i = vn.begin(); i != vn.end(); i++) {
  426. for (VtxIdxIter j = i; j != vn.end(); j++) {
  427. if (*i != *j) {
  428. //cerr << "toggling " << *i << "," << *j << endl;
  429. toggle_edge (*i, *j);
  430. }
  431. }
  432. // and adjust the local Cliffords:
  433. vertices[*i].byprod = vertices[*i].byprod * lco_spiZ.herm_adjoint();
  434. }
  435. // finally, adjust the local Clifford of v:
  436. vertices[v].byprod = vertices[v].byprod * lco_smiX.herm_adjoint();
  437. }
  438. //! Do neighborhood inversions to reduce the VOp of vertex v to the identity.
  439. /* 'avoid' is avoided as swapping partner, i.e. the swapping partner will not
  440. be 'avoid' unless this is the only neighbor of v. If no neighbor is available,
  441. the program exits with error message.*/
  442. bool GraphRegister::remove_byprod_op (VertexIndex v, VertexIndex avoid)
  443. {
  444. // A lookup table on how any LC operator can be composed from them
  445. // generators lco_spiZ (denoted 'V') and lco_smiX (denoted 'U'):
  446. // (The lookup table was generated from sqrt-gen.nb and copied here manually.)
  447. const char * comp_tbl [24] =
  448. {"UUUU", "UU", "VVUU", "VV",
  449. "VUU", "V", "VVV", "UUV",
  450. "UVU", "UVUUU", "UVVVU", "UUUVU",
  451. "UVV", "VVU", "UUU", "U",
  452. "VVVU", "UUVU", "VU", "VUUU",
  453. "UUUV", "UVVV", "UV", "UVUU"};
  454. // Of course, we need a neighborhood
  455. if (vertices[v].neighbors.size() == 0) {
  456. cerr << "remove_byprod_op: called with isolated vertex. Aborting.\n";
  457. }
  458. // This will be the swapping partner:
  459. VertexIndex vb = * vertices[v].neighbors.begin ();
  460. if (vb == avoid) {
  461. // Is there an alternative to 'avoid'? If so, use it.
  462. VtxIdxIter vbi = vertices[v].neighbors.begin ();
  463. vbi++;
  464. if (vbi != vertices[v].neighbors.end()) {
  465. vb = *vbi;
  466. }
  467. }
  468. #ifdef DEBUGOUTPUT
  469. DBGOUT ("remove_byprod_op called: (v, avoid, vb):\n");
  470. print_adj_list_line (cout, v);
  471. print_adj_list_line (cout, avoid);
  472. print_adj_list_line (cout, vb);
  473. #endif
  474. const char * comp = comp_tbl[vertices[v].byprod.op];
  475. DBGOUT ("using " << comp << endl);
  476. for (int pos = strlen(comp)-1; pos >= 0; pos--) {
  477. if (comp[pos] == 'U') {
  478. // A U will vanish if we do an inversion on v
  479. DBGOUT ("U ->");
  480. invert_neighborhood (v);
  481. } else {
  482. assert (comp[pos] == 'V');
  483. DBGOUT ("V ->");
  484. // For this we need to invert on a neighbor of v
  485. invert_neighborhood (vb);
  486. }
  487. }
  488. #ifdef DEBUGOUTPUT
  489. DBGOUT ("remove_byprod_op, after: (v, avoid, vb):\n");
  490. print_adj_list_line (cout, v);
  491. print_adj_list_line (cout, avoid);
  492. print_adj_list_line (cout, vb);
  493. #endif
  494. // Now, we should have lco_Id left
  495. assert (vertices[v].byprod == lco_Id);
  496. return true;
  497. }