diff --git a/docs/source/library/Mapping.rst b/docs/source/library/Mapping.rst index 727b0d2af..f9f598f19 100644 --- a/docs/source/library/Mapping.rst +++ b/docs/source/library/Mapping.rst @@ -19,10 +19,16 @@ The following classes provide a more explicit way of initializing the respective .. currentmodule:: mqt.qmap .. autoclass:: Method + .. autoclass:: Heuristic + + .. autoclass:: LookaheadHeuristic + .. autoclass:: Layering .. autoclass:: InitialLayout + .. autoclass:: EarlyTermination + .. autoclass:: Encoding .. autoclass:: CommanderGrouping diff --git a/examples/0410184_169.qasm b/examples/0410184_169.qasm index e85dbbeec..e650f82b1 100644 --- a/examples/0410184_169.qasm +++ b/examples/0410184_169.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; cx q[4],q[3]; cx q[6],q[5]; cx q[8],q[7]; diff --git a/examples/3_17_13.qasm b/examples/3_17_13.qasm index 9481a3adb..55d211358 100644 --- a/examples/3_17_13.qasm +++ b/examples/3_17_13.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[3]; +creg c[3]; x q[2]; cx q[0],q[2]; cx q[2],q[1]; diff --git a/examples/4_49_16.qasm b/examples/4_49_16.qasm index b6815152b..07e4f5747 100644 --- a/examples/4_49_16.qasm +++ b/examples/4_49_16.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[1]; t q[3]; t q[4]; diff --git a/examples/4gt10-v1_81.qasm b/examples/4gt10-v1_81.qasm index 8db19386f..4ba7c0a32 100644 --- a/examples/4gt10-v1_81.qasm +++ b/examples/4gt10-v1_81.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; h q[1]; t q[2]; diff --git a/examples/4gt11_82.qasm b/examples/4gt11_82.qasm index 343656f8b..d5858a939 100644 --- a/examples/4gt11_82.qasm +++ b/examples/4gt11_82.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt11_83.qasm b/examples/4gt11_83.qasm index d10d1afae..2376ce29b 100644 --- a/examples/4gt11_83.qasm +++ b/examples/4gt11_83.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt11_84.qasm b/examples/4gt11_84.qasm index f20fd7a58..14efe0b9d 100644 --- a/examples/4gt11_84.qasm +++ b/examples/4gt11_84.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[0]; t q[2]; t q[1]; diff --git a/examples/4gt12-v0_86.qasm b/examples/4gt12-v0_86.qasm index 6fbdd6c5a..a5f6ebbf5 100644 --- a/examples/4gt12-v0_86.qasm +++ b/examples/4gt12-v0_86.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt12-v0_87.qasm b/examples/4gt12-v0_87.qasm index bb08b5316..edc9d32f7 100644 --- a/examples/4gt12-v0_87.qasm +++ b/examples/4gt12-v0_87.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt12-v0_88.qasm b/examples/4gt12-v0_88.qasm index 978a7d716..0f05bc7c2 100644 --- a/examples/4gt12-v0_88.qasm +++ b/examples/4gt12-v0_88.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; h q[3]; t q[2]; t q[1]; diff --git a/examples/4gt12-v1_89.qasm b/examples/4gt12-v1_89.qasm index 331148078..d327f7e2f 100644 --- a/examples/4gt12-v1_89.qasm +++ b/examples/4gt12-v1_89.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; x q[0]; cx q[4],q[0]; h q[0]; diff --git a/examples/4gt13-v1_93.qasm b/examples/4gt13-v1_93.qasm index 67776a56d..cf8e01446 100644 --- a/examples/4gt13-v1_93.qasm +++ b/examples/4gt13-v1_93.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; cx q[4],q[0]; cx q[0],q[4]; diff --git a/examples/4gt13_90.qasm b/examples/4gt13_90.qasm index e6154accb..46f62d02e 100644 --- a/examples/4gt13_90.qasm +++ b/examples/4gt13_90.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt13_91.qasm b/examples/4gt13_91.qasm index 3777a6124..8de128c42 100644 --- a/examples/4gt13_91.qasm +++ b/examples/4gt13_91.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt13_92.qasm b/examples/4gt13_92.qasm index 16210fb13..8ce106bde 100644 --- a/examples/4gt13_92.qasm +++ b/examples/4gt13_92.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[4],q[0]; h q[0]; t q[1]; diff --git a/examples/4gt4-v0_72.qasm b/examples/4gt4-v0_72.qasm index cb1bfb19a..d51467102 100644 --- a/examples/4gt4-v0_72.qasm +++ b/examples/4gt4-v0_72.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[4],q[1]; h q[0]; t q[4]; diff --git a/examples/4gt4-v0_73.qasm b/examples/4gt4-v0_73.qasm index 9f7d31b37..999968926 100644 --- a/examples/4gt4-v0_73.qasm +++ b/examples/4gt4-v0_73.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[0],q[1]; cx q[2],q[1]; cx q[1],q[2]; diff --git a/examples/4gt4-v0_78.qasm b/examples/4gt4-v0_78.qasm index f260be24a..1138abab9 100644 --- a/examples/4gt4-v0_78.qasm +++ b/examples/4gt4-v0_78.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt4-v0_79.qasm b/examples/4gt4-v0_79.qasm index e092e0aa4..7c2aa12c8 100644 --- a/examples/4gt4-v0_79.qasm +++ b/examples/4gt4-v0_79.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[2],q[1]; cx q[1],q[2]; cx q[3],q[2]; diff --git a/examples/4gt4-v0_80.qasm b/examples/4gt4-v0_80.qasm index 32cc93d9e..8aa215665 100644 --- a/examples/4gt4-v0_80.qasm +++ b/examples/4gt4-v0_80.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; h q[0]; t q[2]; t q[1]; diff --git a/examples/4gt4-v1_74.qasm b/examples/4gt4-v1_74.qasm index 1bb11c96f..18dbda4f5 100644 --- a/examples/4gt4-v1_74.qasm +++ b/examples/4gt4-v1_74.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; x q[0]; h q[1]; t q[0]; diff --git a/examples/4gt5_75.qasm b/examples/4gt5_75.qasm index 38ce1b44e..cf928c393 100644 --- a/examples/4gt5_75.qasm +++ b/examples/4gt5_75.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[4],q[1]; cx q[3],q[0]; cx q[1],q[4]; diff --git a/examples/4gt5_76.qasm b/examples/4gt5_76.qasm index e29ec26d2..55182dcd1 100644 --- a/examples/4gt5_76.qasm +++ b/examples/4gt5_76.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[0],q[1]; cx q[2],q[1]; cx q[1],q[2]; diff --git a/examples/4gt5_77.qasm b/examples/4gt5_77.qasm index 6df3bc119..a8d925e9b 100644 --- a/examples/4gt5_77.qasm +++ b/examples/4gt5_77.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; cx q[4],q[1]; cx q[1],q[4]; diff --git a/examples/4mod5-bdd_287.qasm b/examples/4mod5-bdd_287.qasm index 11d8f6586..682f1ff8b 100644 --- a/examples/4mod5-bdd_287.qasm +++ b/examples/4mod5-bdd_287.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; x q[4]; x q[6]; cx q[3],q[4]; diff --git a/examples/4mod5-v0_18.qasm b/examples/4mod5-v0_18.qasm index 3f2007bda..5c302014c 100644 --- a/examples/4mod5-v0_18.qasm +++ b/examples/4mod5-v0_18.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; x q[1]; h q[4]; diff --git a/examples/4mod5-v0_19.qasm b/examples/4mod5-v0_19.qasm index c402a6094..d75464785 100644 --- a/examples/4mod5-v0_19.qasm +++ b/examples/4mod5-v0_19.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[3]; cx q[1],q[3]; cx q[3],q[4]; diff --git a/examples/4mod5-v0_20.qasm b/examples/4mod5-v0_20.qasm index 7c145bd96..9b510865f 100644 --- a/examples/4mod5-v0_20.qasm +++ b/examples/4mod5-v0_20.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[1]; x q[0]; cx q[0],q[2]; diff --git a/examples/4mod5-v1_22.qasm b/examples/4mod5-v1_22.qasm index b32f89dd7..ac3e84353 100644 --- a/examples/4mod5-v1_22.qasm +++ b/examples/4mod5-v1_22.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[4]; cx q[0],q[2]; cx q[1],q[3]; diff --git a/examples/4mod5-v1_23.qasm b/examples/4mod5-v1_23.qasm index 58569e1d2..a417fab4d 100644 --- a/examples/4mod5-v1_23.qasm +++ b/examples/4mod5-v1_23.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[4]; h q[4]; t q[0]; diff --git a/examples/4mod5-v1_24.qasm b/examples/4mod5-v1_24.qasm index f75101120..3d6362e7f 100644 --- a/examples/4mod5-v1_24.qasm +++ b/examples/4mod5-v1_24.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[4]; cx q[3],q[1]; h q[0]; diff --git a/examples/4mod7-v0_94.qasm b/examples/4mod7-v0_94.qasm index ba4a5ee0d..a1e770153 100644 --- a/examples/4mod7-v0_94.qasm +++ b/examples/4mod7-v0_94.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[0]; t q[1]; t q[4]; diff --git a/examples/4mod7-v1_96.qasm b/examples/4mod7-v1_96.qasm index 838c97c17..87585452f 100644 --- a/examples/4mod7-v1_96.qasm +++ b/examples/4mod7-v1_96.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; x q[0]; h q[0]; diff --git a/examples/9symml_195.qasm b/examples/9symml_195.qasm index ee911faaf..f976c0dc5 100644 --- a/examples/9symml_195.qasm +++ b/examples/9symml_195.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; h q[1]; t q[6]; t q[10]; diff --git a/examples/C17_204.qasm b/examples/C17_204.qasm index f8807f5da..ee5b64cf3 100644 --- a/examples/C17_204.qasm +++ b/examples/C17_204.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; cx q[5],q[1]; cx q[5],q[0]; h q[1]; diff --git a/examples/adr4_197.qasm b/examples/adr4_197.qasm index e01f37eed..95900127c 100644 --- a/examples/adr4_197.qasm +++ b/examples/adr4_197.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; h q[4]; t q[6]; t q[12]; diff --git a/examples/aj-e11_165.qasm b/examples/aj-e11_165.qasm index b4d54ba18..aaea255d3 100644 --- a/examples/aj-e11_165.qasm +++ b/examples/aj-e11_165.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[1],q[2]; cx q[2],q[3]; h q[2]; diff --git a/examples/alu-bdd_288.qasm b/examples/alu-bdd_288.qasm index 3172aea8a..614e5225b 100644 --- a/examples/alu-bdd_288.qasm +++ b/examples/alu-bdd_288.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; cx q[0],q[5]; h q[5]; t q[1]; diff --git a/examples/alu-v0_26.qasm b/examples/alu-v0_26.qasm index 8f7ac1762..4e8ddd6d3 100644 --- a/examples/alu-v0_26.qasm +++ b/examples/alu-v0_26.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[2],q[1]; cx q[2],q[0]; h q[2]; diff --git a/examples/alu-v0_27.qasm b/examples/alu-v0_27.qasm index 7d3b15d58..95811ab13 100644 --- a/examples/alu-v0_27.qasm +++ b/examples/alu-v0_27.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[4]; cx q[2],q[1]; h q[2]; diff --git a/examples/alu-v1_28.qasm b/examples/alu-v1_28.qasm index 1aace02b6..7748feda8 100644 --- a/examples/alu-v1_28.qasm +++ b/examples/alu-v1_28.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[4],q[3]; cx q[1],q[2]; cx q[2],q[4]; diff --git a/examples/alu-v1_29.qasm b/examples/alu-v1_29.qasm index 13b0f0673..9e51d3b8f 100644 --- a/examples/alu-v1_29.qasm +++ b/examples/alu-v1_29.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[4]; x q[3]; cx q[1],q[2]; diff --git a/examples/alu-v2_30.qasm b/examples/alu-v2_30.qasm index b8d432248..b92af848d 100644 --- a/examples/alu-v2_30.qasm +++ b/examples/alu-v2_30.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[0],q[2]; cx q[2],q[0]; cx q[0],q[1]; diff --git a/examples/alu-v2_31.qasm b/examples/alu-v2_31.qasm index d1e567812..b74b1612d 100644 --- a/examples/alu-v2_31.qasm +++ b/examples/alu-v2_31.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[0],q[2]; h q[2]; t q[0]; diff --git a/examples/alu-v2_32.qasm b/examples/alu-v2_32.qasm index 2f4b09486..e760ed2b3 100644 --- a/examples/alu-v2_32.qasm +++ b/examples/alu-v2_32.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[1]; t q[0]; t q[4]; diff --git a/examples/alu-v2_33.qasm b/examples/alu-v2_33.qasm index edf7b92f8..afe8985ca 100644 --- a/examples/alu-v2_33.qasm +++ b/examples/alu-v2_33.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[4]; cx q[3],q[4]; cx q[2],q[1]; diff --git a/examples/alu-v3_34.qasm b/examples/alu-v3_34.qasm index 78d6b7769..8afe12483 100644 --- a/examples/alu-v3_34.qasm +++ b/examples/alu-v3_34.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[1]; t q[4]; t q[2]; diff --git a/examples/alu-v3_35.qasm b/examples/alu-v3_35.qasm index fb00199d2..c9fa3e3f2 100644 --- a/examples/alu-v3_35.qasm +++ b/examples/alu-v3_35.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[4],q[3]; cx q[2],q[1]; x q[2]; diff --git a/examples/alu-v4_36.qasm b/examples/alu-v4_36.qasm index 0e3544161..dbdf1603f 100644 --- a/examples/alu-v4_36.qasm +++ b/examples/alu-v4_36.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[1]; t q[2]; t q[0]; diff --git a/examples/alu-v4_37.qasm b/examples/alu-v4_37.qasm index 618f5f2e8..579d70992 100644 --- a/examples/alu-v4_37.qasm +++ b/examples/alu-v4_37.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[4]; cx q[2],q[1]; x q[2]; diff --git a/examples/clip_206.qasm b/examples/clip_206.qasm index 3ec43baaa..741a4dc64 100644 --- a/examples/clip_206.qasm +++ b/examples/clip_206.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; cx q[13],q[1]; cx q[8],q[0]; x q[7]; diff --git a/examples/cm152a_212.qasm b/examples/cm152a_212.qasm index 2f4744d36..0e0fb2e6e 100644 --- a/examples/cm152a_212.qasm +++ b/examples/cm152a_212.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[12]; +creg c[12]; h q[0]; t q[1]; t q[11]; diff --git a/examples/cm42a_207.qasm b/examples/cm42a_207.qasm index 871ea5786..c729f5bbb 100644 --- a/examples/cm42a_207.qasm +++ b/examples/cm42a_207.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; x q[9]; x q[8]; x q[7]; diff --git a/examples/cm82a_208.qasm b/examples/cm82a_208.qasm index 7a0f22ec5..115593e01 100644 --- a/examples/cm82a_208.qasm +++ b/examples/cm82a_208.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; h q[0]; t q[7]; t q[3]; diff --git a/examples/cm85a_209.qasm b/examples/cm85a_209.qasm index d66b899ba..3e61f471c 100644 --- a/examples/cm85a_209.qasm +++ b/examples/cm85a_209.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; h q[1]; t q[4]; t q[13]; diff --git a/examples/co14_215.qasm b/examples/co14_215.qasm index 833c8ccb3..0cba559a1 100644 --- a/examples/co14_215.qasm +++ b/examples/co14_215.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; x q[12]; x q[11]; x q[10]; diff --git a/examples/con1_216.qasm b/examples/con1_216.qasm index b3abcdff9..16fda177e 100644 --- a/examples/con1_216.qasm +++ b/examples/con1_216.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[9]; +creg c[9]; cx q[8],q[0]; cx q[4],q[0]; h q[1]; diff --git a/examples/cycle10_2_110.qasm b/examples/cycle10_2_110.qasm index 31b8539d1..632f329c4 100644 --- a/examples/cycle10_2_110.qasm +++ b/examples/cycle10_2_110.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[12]; +creg c[12]; h q[0]; t q[4]; t q[11]; diff --git a/examples/dc1_220.qasm b/examples/dc1_220.qasm index 2dfaaa94f..8a4fb6a11 100644 --- a/examples/dc1_220.qasm +++ b/examples/dc1_220.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; x q[10]; h q[4]; t q[10]; diff --git a/examples/dc2_222.qasm b/examples/dc2_222.qasm index e5d2d6072..eb0c770fb 100644 --- a/examples/dc2_222.qasm +++ b/examples/dc2_222.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; cx q[7],q[0]; x q[14]; h q[6]; diff --git a/examples/decod24-bdd_294.qasm b/examples/decod24-bdd_294.qasm index 1e83fd69d..b3ecbfe79 100644 --- a/examples/decod24-bdd_294.qasm +++ b/examples/decod24-bdd_294.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; x q[3]; x q[4]; h q[2]; diff --git a/examples/decod24-enable_126.qasm b/examples/decod24-enable_126.qasm index 85ce0cea6..96f3a46e0 100644 --- a/examples/decod24-enable_126.qasm +++ b/examples/decod24-enable_126.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; h q[2]; t q[1]; t q[4]; diff --git a/examples/decod24-v0_38.qasm b/examples/decod24-v0_38.qasm index f4ac0e1d4..6427c6f96 100644 --- a/examples/decod24-v0_38.qasm +++ b/examples/decod24-v0_38.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[4]; +creg c[4]; h q[0]; t q[3]; t q[2]; diff --git a/examples/decod24-v1_41.qasm b/examples/decod24-v1_41.qasm index db9d0dfc0..602331303 100644 --- a/examples/decod24-v1_41.qasm +++ b/examples/decod24-v1_41.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[1]; h q[0]; t q[1]; diff --git a/examples/decod24-v2_43.qasm b/examples/decod24-v2_43.qasm index 88702e2bc..05105b063 100644 --- a/examples/decod24-v2_43.qasm +++ b/examples/decod24-v2_43.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[4]; +creg c[4]; x q[0]; h q[0]; t q[3]; diff --git a/examples/decod24-v3_45.qasm b/examples/decod24-v3_45.qasm index 9edd82149..086a181e5 100644 --- a/examples/decod24-v3_45.qasm +++ b/examples/decod24-v3_45.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; x q[1]; h q[0]; diff --git a/examples/dist_223.qasm b/examples/dist_223.qasm index ef3fb03f6..784663697 100644 --- a/examples/dist_223.qasm +++ b/examples/dist_223.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; cx q[12],q[3]; cx q[12],q[2]; h q[1]; diff --git a/examples/ex-1_166.qasm b/examples/ex-1_166.qasm index 899aa1430..36aab83e2 100644 --- a/examples/ex-1_166.qasm +++ b/examples/ex-1_166.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[3]; +creg c[3]; cx q[0],q[1]; h q[0]; t q[1]; diff --git a/examples/ex1_226.qasm b/examples/ex1_226.qasm index 8115cd6d1..1ae17f190 100644 --- a/examples/ex1_226.qasm +++ b/examples/ex1_226.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[3],q[0]; cx q[1],q[0]; cx q[5],q[0]; diff --git a/examples/ex2_227.qasm b/examples/ex2_227.qasm index 6687064f1..7c85ea7d5 100644 --- a/examples/ex2_227.qasm +++ b/examples/ex2_227.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; h q[0]; t q[2]; t q[6]; diff --git a/examples/ex3_229.qasm b/examples/ex3_229.qasm index 4fbb4dce0..e737ba838 100644 --- a/examples/ex3_229.qasm +++ b/examples/ex3_229.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; h q[0]; t q[5]; t q[4]; diff --git a/examples/f2_232.qasm b/examples/f2_232.qasm index df66a501f..7e729ff22 100644 --- a/examples/f2_232.qasm +++ b/examples/f2_232.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; x q[7]; h q[0]; t q[7]; diff --git a/examples/graycode6_47.qasm b/examples/graycode6_47.qasm index 3e41f91c5..9ab2fa502 100644 --- a/examples/graycode6_47.qasm +++ b/examples/graycode6_47.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[1],q[0]; cx q[2],q[1]; cx q[3],q[2]; diff --git a/examples/ground_state_estimation_10.qasm b/examples/ground_state_estimation_10.qasm index 55c48cd2b..d4de234e6 100644 --- a/examples/ground_state_estimation_10.qasm +++ b/examples/ground_state_estimation_10.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; x q[3]; x q[4]; x q[5]; diff --git a/examples/ham15_107.qasm b/examples/ham15_107.qasm index 0765802e4..1b4d3d380 100644 --- a/examples/ham15_107.qasm +++ b/examples/ham15_107.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; h q[1]; t q[14]; t q[8]; diff --git a/examples/ham3_102.qasm b/examples/ham3_102.qasm index 97779313a..401a8d451 100644 --- a/examples/ham3_102.qasm +++ b/examples/ham3_102.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[3]; +creg c[3]; h q[0]; t q[1]; t q[2]; diff --git a/examples/ham7_104.qasm b/examples/ham7_104.qasm index 44bc230eb..6cd9a8d25 100644 --- a/examples/ham7_104.qasm +++ b/examples/ham7_104.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; h q[2]; t q[6]; t q[3]; diff --git a/examples/hwb4_49.qasm b/examples/hwb4_49.qasm index d00c3d0af..5ed753d1c 100644 --- a/examples/hwb4_49.qasm +++ b/examples/hwb4_49.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[1]; cx q[2],q[3]; cx q[3],q[2]; diff --git a/examples/hwb5_53.qasm b/examples/hwb5_53.qasm index 858331d8f..7e59d822e 100644 --- a/examples/hwb5_53.qasm +++ b/examples/hwb5_53.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[3],q[1]; cx q[4],q[3]; h q[1]; diff --git a/examples/hwb6_56.qasm b/examples/hwb6_56.qasm index c79820ee3..bd98f3a35 100644 --- a/examples/hwb6_56.qasm +++ b/examples/hwb6_56.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; h q[5]; t q[0]; t q[3]; diff --git a/examples/hwb7_59.qasm b/examples/hwb7_59.qasm index 78144cca1..65aa600b2 100644 --- a/examples/hwb7_59.qasm +++ b/examples/hwb7_59.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; h q[1]; t q[4]; t q[6]; diff --git a/examples/hwb8_113.qasm b/examples/hwb8_113.qasm index e1499c030..4ab0044d1 100644 --- a/examples/hwb8_113.qasm +++ b/examples/hwb8_113.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[9]; +creg c[9]; h q[1]; t q[5]; t q[8]; diff --git a/examples/hwb9_119.qasm b/examples/hwb9_119.qasm index 8038dda83..5f866044d 100644 --- a/examples/hwb9_119.qasm +++ b/examples/hwb9_119.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; cx q[8],q[1]; h q[8]; t q[0]; diff --git a/examples/ising_model_10.qasm b/examples/ising_model_10.qasm index 8fa2e4d62..8ec8c4ed2 100644 --- a/examples/ising_model_10.qasm +++ b/examples/ising_model_10.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[0]; h q[1]; h q[2]; diff --git a/examples/ising_model_13.qasm b/examples/ising_model_13.qasm index e06d7e657..8dc66cf01 100644 --- a/examples/ising_model_13.qasm +++ b/examples/ising_model_13.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; h q[0]; h q[1]; h q[2]; diff --git a/examples/life_238.qasm b/examples/life_238.qasm index bf74c097b..aa9276b22 100644 --- a/examples/life_238.qasm +++ b/examples/life_238.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; x q[7]; x q[4]; x q[3]; diff --git a/examples/majority_239.qasm b/examples/majority_239.qasm index a61d9c736..3b1f95472 100644 --- a/examples/majority_239.qasm +++ b/examples/majority_239.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; cx q[2],q[0]; x q[2]; h q[0]; diff --git a/examples/max46_240.qasm b/examples/max46_240.qasm index 3b0ca6c6a..020323e2c 100644 --- a/examples/max46_240.qasm +++ b/examples/max46_240.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; x q[3]; h q[0]; t q[1]; diff --git a/examples/miller_11.qasm b/examples/miller_11.qasm index 65fa7c9f3..a79ef1d48 100644 --- a/examples/miller_11.qasm +++ b/examples/miller_11.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[3]; +creg c[3]; cx q[2],q[1]; h q[2]; t q[1]; diff --git a/examples/mini-alu_167.qasm b/examples/mini-alu_167.qasm index d91256f7c..8c72fed31 100644 --- a/examples/mini-alu_167.qasm +++ b/examples/mini-alu_167.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[1]; t q[0]; t q[3]; diff --git a/examples/mini_alu_305.qasm b/examples/mini_alu_305.qasm index 59d9826f6..00635c27e 100644 --- a/examples/mini_alu_305.qasm +++ b/examples/mini_alu_305.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; x q[6]; x q[7]; x q[8]; diff --git a/examples/misex1_241.qasm b/examples/misex1_241.qasm index ab4e1d4d9..d35aef334 100644 --- a/examples/misex1_241.qasm +++ b/examples/misex1_241.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; x q[14]; h q[6]; t q[11]; diff --git a/examples/mod10_171.qasm b/examples/mod10_171.qasm index b0904c14c..310505ef8 100644 --- a/examples/mod10_171.qasm +++ b/examples/mod10_171.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[0],q[2]; h q[3]; t q[0]; diff --git a/examples/mod10_176.qasm b/examples/mod10_176.qasm index 8a703f9e6..698c5fd24 100644 --- a/examples/mod10_176.qasm +++ b/examples/mod10_176.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[1]; t q[3]; t q[2]; diff --git a/examples/mod5adder_127.qasm b/examples/mod5adder_127.qasm index ee11f4ae2..ee211814a 100644 --- a/examples/mod5adder_127.qasm +++ b/examples/mod5adder_127.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; x q[3]; x q[4]; x q[5]; diff --git a/examples/mod5d1_63.qasm b/examples/mod5d1_63.qasm index 6dabfbede..59153aa47 100644 --- a/examples/mod5d1_63.qasm +++ b/examples/mod5d1_63.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[1]; cx q[2],q[0]; cx q[1],q[4]; diff --git a/examples/mod5d2_64.qasm b/examples/mod5d2_64.qasm index a66c2f381..3a2398fe7 100644 --- a/examples/mod5d2_64.qasm +++ b/examples/mod5d2_64.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[2],q[1]; h q[0]; t q[3]; diff --git a/examples/mod5mils_65.qasm b/examples/mod5mils_65.qasm index bd525d094..d02d51b08 100644 --- a/examples/mod5mils_65.qasm +++ b/examples/mod5mils_65.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[1],q[3]; x q[3]; h q[4]; diff --git a/examples/mod8-10_177.qasm b/examples/mod8-10_177.qasm index 58de9d36e..d5606bc0a 100644 --- a/examples/mod8-10_177.qasm +++ b/examples/mod8-10_177.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[1],q[3]; cx q[1],q[2]; h q[5]; diff --git a/examples/mod8-10_178.qasm b/examples/mod8-10_178.qasm index 090258d1a..ab582df40 100644 --- a/examples/mod8-10_178.qasm +++ b/examples/mod8-10_178.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; h q[2]; t q[4]; t q[3]; diff --git a/examples/one-two-three-v0_97.qasm b/examples/one-two-three-v0_97.qasm index 3c5968a81..3ba56f872 100644 --- a/examples/one-two-three-v0_97.qasm +++ b/examples/one-two-three-v0_97.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[3],q[2]; cx q[4],q[1]; h q[0]; diff --git a/examples/one-two-three-v0_98.qasm b/examples/one-two-three-v0_98.qasm index ef1d21f1c..afe2690f0 100644 --- a/examples/one-two-three-v0_98.qasm +++ b/examples/one-two-three-v0_98.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; cx q[4],q[1]; h q[0]; t q[4]; diff --git a/examples/one-two-three-v1_99.qasm b/examples/one-two-three-v1_99.qasm index aa0d613fa..31f6b5264 100644 --- a/examples/one-two-three-v1_99.qasm +++ b/examples/one-two-three-v1_99.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[1]; h q[1]; t q[2]; diff --git a/examples/one-two-three-v2_100.qasm b/examples/one-two-three-v2_100.qasm index 3b0f50382..b115182f4 100644 --- a/examples/one-two-three-v2_100.qasm +++ b/examples/one-two-three-v2_100.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; cx q[4],q[1]; cx q[2],q[0]; diff --git a/examples/one-two-three-v3_101.qasm b/examples/one-two-three-v3_101.qasm index 847ac28b6..cfe44c65a 100644 --- a/examples/one-two-three-v3_101.qasm +++ b/examples/one-two-three-v3_101.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; x q[0]; x q[1]; cx q[3],q[1]; diff --git a/examples/plus63mod4096_163.qasm b/examples/plus63mod4096_163.qasm index 8811dece7..cb9cb1174 100644 --- a/examples/plus63mod4096_163.qasm +++ b/examples/plus63mod4096_163.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; h q[5]; t q[6]; t q[7]; diff --git a/examples/plus63mod8192_164.qasm b/examples/plus63mod8192_164.qasm index c084266f4..ec182af83 100644 --- a/examples/plus63mod8192_164.qasm +++ b/examples/plus63mod8192_164.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; h q[6]; t q[7]; t q[8]; diff --git a/examples/pm1_249.qasm b/examples/pm1_249.qasm index 871ea5786..c729f5bbb 100644 --- a/examples/pm1_249.qasm +++ b/examples/pm1_249.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; x q[9]; x q[8]; x q[7]; diff --git a/examples/qft_10.qasm b/examples/qft_10.qasm index 90add64a7..e02d13f23 100644 --- a/examples/qft_10.qasm +++ b/examples/qft_10.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[0]; rz(-0.7854) q[0]; cx q[0],q[1]; diff --git a/examples/radd_250.qasm b/examples/radd_250.qasm index 2014e348d..fed8abcb6 100644 --- a/examples/radd_250.qasm +++ b/examples/radd_250.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; h q[2]; t q[8]; t q[10]; diff --git a/examples/rd32-v0_66.qasm b/examples/rd32-v0_66.qasm index ca105c4c0..651b8c4ea 100644 --- a/examples/rd32-v0_66.qasm +++ b/examples/rd32-v0_66.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[4]; +creg c[4]; h q[3]; t q[1]; t q[0]; diff --git a/examples/rd32-v1_68.qasm b/examples/rd32-v1_68.qasm index a9e5f13c7..527627811 100644 --- a/examples/rd32-v1_68.qasm +++ b/examples/rd32-v1_68.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[4]; +creg c[4]; x q[3]; h q[3]; t q[1]; diff --git a/examples/rd32_270.qasm b/examples/rd32_270.qasm index 925f5bbf9..72eefc6e7 100644 --- a/examples/rd32_270.qasm +++ b/examples/rd32_270.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[5]; +creg c[5]; h q[3]; t q[0]; t q[2]; diff --git a/examples/rd53_130.qasm b/examples/rd53_130.qasm index 5dc0b939d..5eea46271 100644 --- a/examples/rd53_130.qasm +++ b/examples/rd53_130.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; x q[5]; x q[6]; h q[2]; diff --git a/examples/rd53_131.qasm b/examples/rd53_131.qasm index ae4010810..0e1ce47de 100644 --- a/examples/rd53_131.qasm +++ b/examples/rd53_131.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; h q[6]; t q[0]; t q[5]; diff --git a/examples/rd53_133.qasm b/examples/rd53_133.qasm index 442b55820..52ea94d86 100644 --- a/examples/rd53_133.qasm +++ b/examples/rd53_133.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; h q[6]; t q[3]; t q[5]; diff --git a/examples/rd53_135.qasm b/examples/rd53_135.qasm index dc1ff0472..254b5b1d1 100644 --- a/examples/rd53_135.qasm +++ b/examples/rd53_135.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; cx q[1],q[5]; cx q[2],q[1]; cx q[1],q[0]; diff --git a/examples/rd53_138.qasm b/examples/rd53_138.qasm index f743fbf14..a62333ed1 100644 --- a/examples/rd53_138.qasm +++ b/examples/rd53_138.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; h q[5]; t q[0]; t q[1]; diff --git a/examples/rd53_251.qasm b/examples/rd53_251.qasm index 0b2175a29..494cc5737 100644 --- a/examples/rd53_251.qasm +++ b/examples/rd53_251.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; cx q[6],q[1]; h q[2]; t q[3]; diff --git a/examples/rd53_311.qasm b/examples/rd53_311.qasm index 157d13a04..8882559b4 100644 --- a/examples/rd53_311.qasm +++ b/examples/rd53_311.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; x q[11]; cx q[3],q[5]; h q[5]; diff --git a/examples/rd73_140.qasm b/examples/rd73_140.qasm index 6f8638ebb..2fe3e2fd9 100644 --- a/examples/rd73_140.qasm +++ b/examples/rd73_140.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[7]; t q[0]; t q[1]; diff --git a/examples/rd73_252.qasm b/examples/rd73_252.qasm index 089397a93..723130317 100644 --- a/examples/rd73_252.qasm +++ b/examples/rd73_252.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[2]; t q[8]; t q[6]; diff --git a/examples/rd84_142.qasm b/examples/rd84_142.qasm index ad37e3cd8..8efe801ae 100644 --- a/examples/rd84_142.qasm +++ b/examples/rd84_142.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; h q[8]; t q[0]; t q[1]; diff --git a/examples/rd84_253.qasm b/examples/rd84_253.qasm index 24f1e0d46..c6af1b6f6 100644 --- a/examples/rd84_253.qasm +++ b/examples/rd84_253.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[12]; +creg c[12]; cx q[10],q[2]; cx q[8],q[2]; cx q[8],q[0]; diff --git a/examples/root_255.qasm b/examples/root_255.qasm index 81afd05c2..7a93a1dc3 100644 --- a/examples/root_255.qasm +++ b/examples/root_255.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; h q[3]; t q[12]; t q[11]; diff --git a/examples/sao2_257.qasm b/examples/sao2_257.qasm index 02481d3fb..ea0fc4dc4 100644 --- a/examples/sao2_257.qasm +++ b/examples/sao2_257.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; x q[4]; cx q[4],q[1]; x q[8]; diff --git a/examples/sf_274.qasm b/examples/sf_274.qasm index 8e0623840..a028ce918 100644 --- a/examples/sf_274.qasm +++ b/examples/sf_274.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; h q[4]; t q[2]; t q[5]; diff --git a/examples/sf_276.qasm b/examples/sf_276.qasm index 47b628c08..884456ce5 100644 --- a/examples/sf_276.qasm +++ b/examples/sf_276.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; x q[0]; h q[5]; t q[0]; diff --git a/examples/sqn_258.qasm b/examples/sqn_258.qasm index bb92597a1..5079b757b 100644 --- a/examples/sqn_258.qasm +++ b/examples/sqn_258.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; x q[5]; h q[1]; t q[4]; diff --git a/examples/sqrt8_260.qasm b/examples/sqrt8_260.qasm index 0f092683f..fe5e99a9a 100644 --- a/examples/sqrt8_260.qasm +++ b/examples/sqrt8_260.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[12]; +creg c[12]; h q[3]; t q[11]; t q[10]; diff --git a/examples/squar5_261.qasm b/examples/squar5_261.qasm index 750ebe76c..32a655f06 100644 --- a/examples/squar5_261.qasm +++ b/examples/squar5_261.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[13]; +creg c[13]; h q[5]; t q[11]; t q[10]; diff --git a/examples/square_root_7.qasm b/examples/square_root_7.qasm index c014ef480..b22bca7c3 100644 --- a/examples/square_root_7.qasm +++ b/examples/square_root_7.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; h q[0]; h q[1]; h q[2]; diff --git a/examples/sym10_262.qasm b/examples/sym10_262.qasm index b2b1aa8d4..16acb08d7 100644 --- a/examples/sym10_262.qasm +++ b/examples/sym10_262.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[12]; +creg c[12]; h q[2]; t q[6]; t q[11]; diff --git a/examples/sym6_145.qasm b/examples/sym6_145.qasm index 4e8125c0f..b4059e212 100644 --- a/examples/sym6_145.qasm +++ b/examples/sym6_145.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[7]; +creg c[7]; h q[0]; t q[1]; t q[2]; diff --git a/examples/sym6_316.qasm b/examples/sym6_316.qasm index 7806fd882..947b5b54c 100644 --- a/examples/sym6_316.qasm +++ b/examples/sym6_316.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[14]; +creg c[14]; x q[12]; cx q[0],q[6]; h q[6]; diff --git a/examples/sym9_146.qasm b/examples/sym9_146.qasm index 9ec96aef3..7ee6f9ae0 100644 --- a/examples/sym9_146.qasm +++ b/examples/sym9_146.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[12]; +creg c[12]; h q[9]; t q[0]; t q[1]; diff --git a/examples/sym9_148.qasm b/examples/sym9_148.qasm index 7821105cd..f525bc414 100644 --- a/examples/sym9_148.qasm +++ b/examples/sym9_148.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[9]; t q[8]; t q[4]; diff --git a/examples/sym9_193.qasm b/examples/sym9_193.qasm index ee911faaf..f976c0dc5 100644 --- a/examples/sym9_193.qasm +++ b/examples/sym9_193.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; h q[1]; t q[6]; t q[10]; diff --git a/examples/sys6-v0_111.qasm b/examples/sys6-v0_111.qasm index d60df6446..3d15082ba 100644 --- a/examples/sys6-v0_111.qasm +++ b/examples/sys6-v0_111.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[6]; t q[0]; t q[1]; diff --git a/examples/urf1_149.qasm b/examples/urf1_149.qasm index be1e7dd8d..bcc0913ac 100644 --- a/examples/urf1_149.qasm +++ b/examples/urf1_149.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[9]; +creg c[9]; h q[6]; t q[7]; t q[4]; diff --git a/examples/urf1_278.qasm b/examples/urf1_278.qasm index 5878d66b4..17ba2d237 100644 --- a/examples/urf1_278.qasm +++ b/examples/urf1_278.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[9]; +creg c[9]; x q[0]; cx q[0],q[2]; cx q[2],q[0]; diff --git a/examples/urf2_152.qasm b/examples/urf2_152.qasm index f3d005ee5..f9e0a19d1 100644 --- a/examples/urf2_152.qasm +++ b/examples/urf2_152.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; h q[0]; t q[1]; t q[5]; diff --git a/examples/urf2_277.qasm b/examples/urf2_277.qasm index 95e03aab3..854f8aa31 100644 --- a/examples/urf2_277.qasm +++ b/examples/urf2_277.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[8]; +creg c[8]; x q[0]; cx q[3],q[2]; cx q[2],q[3]; diff --git a/examples/urf3_155.qasm b/examples/urf3_155.qasm index a01a36029..6c03aac80 100644 --- a/examples/urf3_155.qasm +++ b/examples/urf3_155.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; h q[3]; t q[1]; t q[7]; diff --git a/examples/urf3_279.qasm b/examples/urf3_279.qasm index 14a400890..232a952b2 100644 --- a/examples/urf3_279.qasm +++ b/examples/urf3_279.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[10]; +creg c[10]; x q[0]; cx q[3],q[2]; cx q[2],q[1]; diff --git a/examples/urf4_187.qasm b/examples/urf4_187.qasm index 11c5a01d9..e27df672e 100644 --- a/examples/urf4_187.qasm +++ b/examples/urf4_187.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; h q[8]; t q[10]; t q[9]; diff --git a/examples/urf5_158.qasm b/examples/urf5_158.qasm index 9e0a8d011..d5ee64cd2 100644 --- a/examples/urf5_158.qasm +++ b/examples/urf5_158.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[9]; +creg c[9]; h q[1]; t q[8]; t q[7]; diff --git a/examples/urf5_280.qasm b/examples/urf5_280.qasm index 28368402a..cefb85f97 100644 --- a/examples/urf5_280.qasm +++ b/examples/urf5_280.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[9]; +creg c[9]; x q[0]; cx q[2],q[1]; cx q[2],q[3]; diff --git a/examples/urf6_160.qasm b/examples/urf6_160.qasm index 451e9463a..23b20d649 100644 --- a/examples/urf6_160.qasm +++ b/examples/urf6_160.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[15]; +creg c[15]; h q[1]; t q[14]; t q[12]; diff --git a/examples/wim_266.qasm b/examples/wim_266.qasm index 4b479b1d3..1b270d91b 100644 --- a/examples/wim_266.qasm +++ b/examples/wim_266.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; x q[6]; x q[5]; x q[4]; diff --git a/examples/xor5_254.qasm b/examples/xor5_254.qasm index 8115cd6d1..1ae17f190 100644 --- a/examples/xor5_254.qasm +++ b/examples/xor5_254.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[6]; +creg c[6]; cx q[3],q[0]; cx q[1],q[0]; cx q[5],q[0]; diff --git a/examples/z4_268.qasm b/examples/z4_268.qasm index 3fbb32159..aca704cc0 100644 --- a/examples/z4_268.qasm +++ b/examples/z4_268.qasm @@ -1,7 +1,7 @@ OPENQASM 2.0; include "qelib1.inc"; -qreg q[16]; -creg c[16]; +qreg q[11]; +creg c[11]; cx q[4],q[0]; cx q[10],q[0]; h q[3]; diff --git a/include/Architecture.hpp b/include/Architecture.hpp index b924e3618..b6a9c3777 100644 --- a/include/Architecture.hpp +++ b/include/Architecture.hpp @@ -240,8 +240,18 @@ class Architecture { createDistanceTable(); } - [[nodiscard]] bool isEdgeConnected(const Edge& edge) const { - return couplingMap.find(edge) != couplingMap.end(); + [[nodiscard]] bool + isEdgeConnected(const Edge& edge, const bool considerDirection = true) const { + if (considerDirection) { + return couplingMap.find(edge) != couplingMap.end(); + } + return couplingMap.find(edge) != couplingMap.end() || + couplingMap.find({edge.second, edge.first}) != couplingMap.end(); + } + + [[nodiscard]] bool isEdgeBidirectional(const Edge& edge) const { + return couplingMap.find(edge) != couplingMap.end() && + couplingMap.find({edge.second, edge.first}) != couplingMap.end(); } CouplingMap& getCurrentTeleportations() { return currentTeleportations; } @@ -249,7 +259,13 @@ class Architecture { return teleportationQubits; } - [[nodiscard]] const Matrix& getDistanceTable() const { return distanceTable; } + [[nodiscard]] const Matrix& + getDistanceTable(bool includeReversalCost = true) const { + if (includeReversalCost) { + return distanceTableReversals; + } + return distanceTable; + } [[nodiscard]] const Properties& getProperties() const { return properties; } @@ -381,8 +397,12 @@ class Architecture { return swapFidelityCosts.at(q1).at(q2); } + /** true if the coupling map contains no unidirectional edges */ [[nodiscard]] bool bidirectional() const { return isBidirectional; } + /** true if the coupling map contains no bidirectional edges */ + [[nodiscard]] bool unidirectional() const { return isUnidirectional; } + [[nodiscard]] bool isArchitectureAvailable() const { return !(name.empty()) && nqubits != 0; } @@ -395,7 +415,9 @@ class Architecture { nqubits = 0; couplingMap.clear(); distanceTable.clear(); - isBidirectional = true; + distanceTableReversals.clear(); + isBidirectional = true; + isUnidirectional = true; properties.clear(); fidelityAvailable = false; fidelityTable.clear(); @@ -406,9 +428,12 @@ class Architecture { fidelityDistanceTables.clear(); } - [[nodiscard]] double distance(std::uint16_t control, - std::uint16_t target) const { + [[nodiscard]] double distance(std::uint16_t control, std::uint16_t target, + bool includeReversalCost = true) const { if (currentTeleportations.empty()) { + if (includeReversalCost) { + return distanceTableReversals.at(control).at(target); + } return distanceTable.at(control).at(target); } return static_cast(bfs(control, target, currentTeleportations)); @@ -476,12 +501,21 @@ class Architecture { static void printCouplingMap(const CouplingMap& cm, std::ostream& os); protected: - std::string name; - std::uint16_t nqubits = 0; - CouplingMap couplingMap = {}; - CouplingMap currentTeleportations = {}; - bool isBidirectional = true; - Matrix distanceTable = {}; + std::string name; + std::uint16_t nqubits = 0; + CouplingMap couplingMap = {}; + CouplingMap currentTeleportations = {}; + + /** true if the coupling map contains no unidirectional edges */ + bool isBidirectional = true; + /** true if the coupling map contains no bidirectional edges */ + bool isUnidirectional = true; + // by this definition the empty coupling map is both bidirectional and + // unidirectional, and coupling maps containing both bidirectional and + // unidirectional edges are neither bidirectional nor unidirectional + + Matrix distanceTable = {}; + Matrix distanceTableReversals = {}; std::vector> teleportationQubits{}; Properties properties = {}; bool fidelityAvailable = false; diff --git a/include/DataLogger.hpp b/include/DataLogger.hpp index 417214cab..501e3c0a2 100644 --- a/include/DataLogger.hpp +++ b/include/DataLogger.hpp @@ -36,9 +36,8 @@ class DataLogger { std::size_t parentId, double costFixed, double costHeur, double lookaheadPenalty, const std::array& qubits, - bool validMapping, - const std::vector>& swaps, - std::size_t depth); + bool validMapping, const std::vector& swaps, + std::size_t depth); void logFinalizeLayer( std::size_t layer, const qc::CompoundOperation& ops, const std::vector& singleQubitMultiplicity, @@ -49,8 +48,7 @@ class DataLogger { std::size_t finalNodeId, double finalCostFixed, double finalCostHeur, double finalLookaheadPenalty, const std::array& finalLayout, - const std::vector>& finalSwaps, - std::size_t finalSearchDepth); + const std::vector& finalSwaps, std::size_t finalSearchDepth); void splitLayer(); void logMappingResult(MappingResults& result); void logInputCircuit(qc::QuantumComputation& qc) { diff --git a/include/Mapper.hpp b/include/Mapper.hpp index 8d09e4c72..109c7781a 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -14,9 +14,10 @@ #include #include #include +#include #include +#include #include -#include /** * number of two-qubit gates acting on pairs of logical qubits in some layer @@ -41,7 +42,6 @@ using TwoQubitMultiplicity = using SingleQubitMultiplicity = std::vector; constexpr std::int16_t DEFAULT_POSITION = -1; -constexpr double INITIAL_FIDELITY = 1.0; class Mapper { protected: @@ -101,19 +101,19 @@ class Mapper { * @brief For each layer the set of all logical qubits, which are acted on by * a gate in the layer */ - std::vector> activeQubits{}; + std::vector> activeQubits{}; /** * @brief For each layer the set of all logical qubits, which are acted on by * a 1Q-gate in the layer */ - std::vector> activeQubits1QGates{}; + std::vector> activeQubits1QGates{}; /** * @brief For each layer the set of all logical qubits, which are acted on by * a 2Q-gate in the layer */ - std::vector> activeQubits2QGates{}; + std::vector> activeQubits2QGates{}; /** * @brief containing the logical qubit currently mapped to each physical @@ -129,9 +129,6 @@ class Mapper { * The inverse of `qubits` */ std::array locations{}; - std::array fidelities{}; - - std::unordered_set usedDeviceQubits{}; MappingResults results{}; @@ -361,7 +358,6 @@ class Mapper { layers.clear(); qubits.fill(DEFAULT_POSITION); locations.fill(DEFAULT_POSITION); - usedDeviceQubits.clear(); results = MappingResults(); } diff --git a/include/configuration/Configuration.hpp b/include/configuration/Configuration.hpp index b9822db93..3c7908c2b 100644 --- a/include/configuration/Configuration.hpp +++ b/include/configuration/Configuration.hpp @@ -8,8 +8,10 @@ #include "CommanderGrouping.hpp" #include "EarlyTermination.hpp" #include "Encoding.hpp" +#include "Heuristic.hpp" #include "InitialLayout.hpp" #include "Layering.hpp" +#include "LookaheadHeuristic.hpp" #include "Method.hpp" #include "SwapReduction.hpp" #include "nlohmann/json.hpp" @@ -21,9 +23,8 @@ struct Configuration { Configuration() = default; // which method to use - Method method = Method::Heuristic; - bool admissibleHeuristic = true; - bool considerFidelity = false; + Method method = Method::Heuristic; + Heuristic heuristic = Heuristic::GateCountMaxDistance; bool preMappingOptimizations = true; bool postMappingOptimizations = true; @@ -59,7 +60,8 @@ struct Configuration { std::size_t iterativeBidirectionalRoutingPasses = 0; // lookahead scheme settings - bool lookahead = true; + LookaheadHeuristic lookaheadHeuristic = + LookaheadHeuristic::GateCountMaxDistance; std::size_t nrLookaheads = 15; double firstLookaheadFactor = 0.75; double lookaheadFactor = 0.5; diff --git a/include/configuration/Heuristic.hpp b/include/configuration/Heuristic.hpp new file mode 100644 index 000000000..7d6f54c60 --- /dev/null +++ b/include/configuration/Heuristic.hpp @@ -0,0 +1,158 @@ +// +// This file is part of the MQT QMAP library released under the MIT license. +// See README.md or go to https://github.com/cda-tum/qmap for more information. +// + +#pragma once + +#include + +enum class Heuristic { + /** maximum over all distances between any virtual qubit pair in the current + layer; optimizing gate-count; admissible; tight */ + GateCountMaxDistance, + /** sum over all distances between any virtual qubit pair in the current + layer; optimizing gate-count; not admissible; tight */ + GateCountSumDistance, + /** sum over all distances between any virtual qubit pair in the current layer + minus the upper limit of viable shared swaps; optimizing gate-count; + admissible; tight */ + GateCountSumDistanceMinusSharedSwaps, + /** maximum of `Heuristic::GateCountMaxDistance` and + `Heuristic::GateCountSumDistanceMinusSharedSwaps`; optimizing gate-count; + admissible; tight */ + GateCountMaxDistanceOrSumDistanceMinusSharedSwaps, + /** minimum cost if each virtual qubit pair/qubit is mapped to its + individually best physical edge/qubit; optimizing fidelity; admissible; + tight */ + FidelityBestLocation +}; + +/** + * A heuristic is admissible if it never overestimates the cost of the best + * reachable goal node, i.e. c(n*) <= c(n) + h(n) for cost function c, + * heuristic h, any node n in the search graph, and n* the best reachable goal + * node from n. + */ +[[maybe_unused]] static inline bool isAdmissible(const Heuristic heuristic) { + switch (heuristic) { + case Heuristic::GateCountMaxDistance: + case Heuristic::FidelityBestLocation: + return true; + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + case Heuristic::GateCountSumDistance: + return false; + } + return false; +} + +/** + * A heuristic is non-decreasing if the estimated cost (i.e. c(n) + h(n)) is + * non-decreasing along any path. + */ +[[maybe_unused]] static inline bool isNonDecreasing(const Heuristic heuristic) { + switch (heuristic) { + case Heuristic::GateCountMaxDistance: + case Heuristic::FidelityBestLocation: + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + return true; + case Heuristic::GateCountSumDistance: + return false; + } + return false; +} + +/** + * A heuristic is principally admissible if it never overestimates the cost of + * the globally optimal solution along the solution path, i.e. c(n*) <= c(n) + + * h(n) for cost function c, heuristic h, any node n along the optimal solution + * path, and n* the globally optimal solution node. + */ +[[maybe_unused]] static inline bool +isPrincipallyAdmissible(const Heuristic heuristic) { + switch (heuristic) { + case Heuristic::GateCountMaxDistance: + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + case Heuristic::FidelityBestLocation: + return true; + case Heuristic::GateCountSumDistance: + return false; + } + return false; +} + +/** + * A heuristic is tight if it is 0 in all goal nodes, i.e. h(n*) = 0 for any + * goal node n*. + */ +[[maybe_unused]] static inline bool isTight(const Heuristic heuristic) { + switch (heuristic) { + case Heuristic::GateCountMaxDistance: + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + case Heuristic::GateCountSumDistance: + return true; + case Heuristic::FidelityBestLocation: + return false; + } + return false; +} + +/** + * A heuristic is fidelity aware if it takes into account the error rates of + * physical qubits and minimizes the total error of the mapped circuit. + */ +[[maybe_unused]] static inline bool isFidelityAware(const Heuristic heuristic) { + switch (heuristic) { + case Heuristic::FidelityBestLocation: + return true; + case Heuristic::GateCountMaxDistance: + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + case Heuristic::GateCountSumDistance: + return false; + } + return false; +} + +[[maybe_unused]] static inline std::string toString(const Heuristic heuristic) { + switch (heuristic) { + case Heuristic::GateCountMaxDistance: + return "gate_count_max_distance"; + case Heuristic::GateCountSumDistance: + return "gate_count_sum_distance"; + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + return "gate_count_sum_distance_minus_shared_swaps"; + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + return "gate_count_max_distance_or_sum_distance_minus_shared_swaps"; + case Heuristic::FidelityBestLocation: + return "fidelity_best_location"; + } + return " "; +} + +[[maybe_unused]] static Heuristic +heuristicFromString(const std::string& heuristic) { + if (heuristic == "gate_count_max_distance" || heuristic == "0") { + return Heuristic::GateCountMaxDistance; + } + if (heuristic == "gate_count_sum_distance" || heuristic == "1") { + return Heuristic::GateCountSumDistance; + } + if (heuristic == "gate_count_sum_distance_minus_shared_swaps" || + heuristic == "2") { + return Heuristic::GateCountSumDistanceMinusSharedSwaps; + } + if (heuristic == + "gate_count_max_distance_or_sum_distance_minus_shared_swaps" || + heuristic == "3") { + return Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps; + } + if (heuristic == "fidelity_best_location" || heuristic == "4") { + return Heuristic::FidelityBestLocation; + } + throw std::invalid_argument("Invalid heuristic value: " + heuristic); +} diff --git a/include/configuration/LookaheadHeuristic.hpp b/include/configuration/LookaheadHeuristic.hpp new file mode 100644 index 000000000..9a2a373db --- /dev/null +++ b/include/configuration/LookaheadHeuristic.hpp @@ -0,0 +1,62 @@ +// +// This file is part of the MQT QMAP library released under the MIT license. +// See README.md or go to https://github.com/cda-tum/qmap for more information. +// + +#pragma once + +#include + +enum class LookaheadHeuristic { + /** no lookahead */ + None, + /** maximum over all distances between any virtual qubit pair in the given + layer; optimizing gate-count */ + GateCountMaxDistance, + /** sum over all distances between any virtual qubit pair in the given layer; + optimizing gate-count */ + GateCountSumDistance +}; + +/** + * A heuristic is fidelity aware if it takes into account the error rates of + * physical qubits and minimizes the total error of the mapped circuit. + */ +[[maybe_unused]] static inline bool +isFidelityAware(const LookaheadHeuristic heuristic) { + switch (heuristic) { + case LookaheadHeuristic::None: + case LookaheadHeuristic::GateCountMaxDistance: + case LookaheadHeuristic::GateCountSumDistance: + return false; + } + return false; +} + +[[maybe_unused]] static inline std::string +toString(const LookaheadHeuristic heuristic) { + switch (heuristic) { + case LookaheadHeuristic::None: + return "none"; + case LookaheadHeuristic::GateCountMaxDistance: + return "gate_count_max_distance"; + case LookaheadHeuristic::GateCountSumDistance: + return "gate_count_sum_distance"; + } + return " "; +} + +[[maybe_unused]] static LookaheadHeuristic +lookaheadHeuristicFromString(const std::string& heuristic) { + if (heuristic == "none" || heuristic == "0") { + return LookaheadHeuristic::None; + } + if (heuristic == "gate_count_max_distance" || heuristic == "1") { + return LookaheadHeuristic::GateCountMaxDistance; + } + if (heuristic == "gate_count_sum_distance" || heuristic == "2") { + return LookaheadHeuristic::GateCountSumDistance; + } + throw std::invalid_argument("Invalid lookahead heuristic value: " + + heuristic); +} diff --git a/include/heuristic/HeuristicMapper.hpp b/include/heuristic/HeuristicMapper.hpp index 8df353fc4..74d282dc9 100644 --- a/include/heuristic/HeuristicMapper.hpp +++ b/include/heuristic/HeuristicMapper.hpp @@ -32,9 +32,9 @@ class HeuristicMapper : public Mapper { struct Node { /** gates (pair of logical qubits) currently mapped next to each other */ std::set validMappedTwoQubitGates = {}; - /** swaps used to get from mapping after last layer to the current mapping; - * each search node begins a new entry in the outer vector */ - std::vector> swaps = {}; + /** swaps used so far to get from the initial mapping of the current layer + * to the current mapping in this node */ + std::vector swaps = {}; /** * containing the logical qubit currently mapped to each physical qubit. * `qubits[physical_qubit] = logical_qubit` @@ -49,117 +49,85 @@ class HeuristicMapper : public Mapper { * The inverse of `qubits` */ std::array locations{}; - /** current fixed cost (for non-fidelity-aware mapping cost of all swaps - * already added) */ - double costFixed = 0; - /** heuristic cost expected for future swaps needed in current circuit layer + /** current fixed cost + * + * non-fidelity-aware: cost of all swaps used in the node + * + * fidelity-aware: fidelity cost of all swaps used in the node + fidelity + * cost of all validly mapped gates at their current position + */ + double costFixed = 0.; + /** current fixed cost of reversals (only for non-fidelity-aware mapping + * and only in goal nodes)*/ + double costFixedReversals = 0.; + /** heuristic cost (i.e. expected difference from current cost to cost of + * the best reachable goal node) */ double costHeur = 0.; /** heuristic cost expected for future swaps needed in later circuit layers * (further layers contribute less) */ double lookaheadPenalty = 0.; - /** number of swaps used to get from mapping after last layer to the current - * mapping */ - std::size_t nswaps = 0; + /** number of swaps that were shared with another considered qubit such + * that both qubits got closer to being validly mapped*/ + std::size_t sharedSwaps = 0; /** depth in search tree (starting with 0 at the root) */ std::size_t depth = 0; std::size_t parent = 0; - std::size_t id; + std::size_t id = 0; /** true if all qubit pairs are mapped next to each other on the * architecture */ - bool done = true; - /** controls if fidelity-aware heuristic should be used */ - bool considerFidelity = false; - /** controls if admissible heuristic should be used */ - bool admissibleHeuristic = true; - - explicit Node(std::size_t nodeId, const bool considerFid = false, - const bool admissibleHeur = true) - : id(nodeId), considerFidelity(considerFid), - admissibleHeuristic(admissibleHeur){}; + bool validMapping = true; + + explicit Node() { + qubits.fill(DEFAULT_POSITION); + locations.fill(DEFAULT_POSITION); + }; + explicit Node(std::size_t nodeId) : id(nodeId) { + qubits.fill(DEFAULT_POSITION); + locations.fill(DEFAULT_POSITION); + }; Node(std::size_t nodeId, std::size_t parentId, const std::array& q, const std::array& loc, - const std::vector>& sw = {}, - const double initCostFixed = 0, const std::size_t searchDepth = 0, - const bool considerFid = false, const bool admissibleHeur = true) - : costFixed(initCostFixed), depth(searchDepth), parent(parentId), - id(nodeId), considerFidelity(considerFid), - admissibleHeuristic(admissibleHeur) { - std::copy(q.begin(), q.end(), qubits.begin()); - std::copy(loc.begin(), loc.end(), locations.begin()); - std::copy(sw.begin(), sw.end(), std::back_inserter(swaps)); - } + const std::vector& sw = {}, + const std::set& valid2QGates = {}, + const double initCostFixed = 0, + const double initCostFixedReversals = 0, + const std::size_t searchDepth = 0, + const std::size_t initSharedSwaps = 0) + : validMappedTwoQubitGates(valid2QGates), swaps(sw), qubits(q), + locations(loc), costFixed(initCostFixed), + costFixedReversals(initCostFixedReversals), + sharedSwaps(initSharedSwaps), depth(searchDepth), parent(parentId), + id(nodeId) {} /** * @brief returns costFixed + costHeur + lookaheadPenalty */ [[nodiscard]] double getTotalCost() const { - return costFixed + costHeur + lookaheadPenalty; + return costFixed + costFixedReversals + costHeur + lookaheadPenalty; } /** * @brief returns costFixed + lookaheadPenalty */ [[nodiscard]] double getTotalFixedCost() const { - return costFixed + lookaheadPenalty; + return costFixed + costFixedReversals + lookaheadPenalty; } - /** - * @brief applies an in-place swap of 2 qubits in `qubits` and `locations` - * of the node - */ - void applySWAP(const Edge& swap, Architecture& arch, - const SingleQubitMultiplicity& singleQubitGateMultiplicity, - const TwoQubitMultiplicity& twoQubitGateMultiplicity); - - /** - * @brief applies an in-place teleportation of 2 qubits in `qubits` and - * `locations` of the node - */ - void applyTeleportation(const Edge& swap, Architecture& arch); - - /** - * @brief recalculates the fixed cost of the node from current mapping and - * swaps - * - * @param arch the architecture for calculating distances between physical - * qubits and supplying qubit information such as fidelity - */ - void recalculateFixedCost( - const Architecture& arch, - const SingleQubitMultiplicity& singleQubitGateMultiplicity, - const TwoQubitMultiplicity& twoQubitGateMultiplicity); - - /** - * @brief calculates the heuristic cost of the current mapping in the node - * for some given layer and writes it to `Node::costHeur` additional - * `Node::done` is set to true if all qubits shared by a gate in the layer - * are mapped next to each other - * - * @param arch the architecture for calculating distances between physical - * qubits and supplying qubit information such as fidelity - * @param twoQubitGateMultiplicity number of two qubit gates acting on pairs - * of logical qubits in the current layer - * @param admissibleHeuristic controls if the heuristic should be calculated - * such that it is admissible (i.e. A*-search should yield the optimal - * solution using this heuristic) - */ - void updateHeuristicCost( - const Architecture& arch, - const SingleQubitMultiplicity& singleQubitGateMultiplicity, - const TwoQubitMultiplicity& twoQubitGateMultiplicity, - const std::unordered_set& consideredQubits); - std::ostream& print(std::ostream& out) const { out << "{\n"; - out << "\t\"done\": " << done << ",\n"; + out << "\t\"valid_mapping\": " << validMapping << ",\n"; out << "\t\"cost\": {\n"; out << "\t\t\"fixed\": " << costFixed << ",\n"; out << "\t\t\"heuristic\": " << costHeur << ",\n"; out << "\t\t\"lookahead_penalty\": " << lookaheadPenalty << "\n"; out << "\t},\n"; - out << "\t\"nswaps\": " << nswaps << "\n}\n"; + out << "\t\"swaps\": "; + for (const auto& swap : swaps) { + out << "(" << swap.first << " " << swap.second << ") "; + } + out << "\n}\n"; return out; } }; @@ -167,7 +135,15 @@ class HeuristicMapper : public Mapper { protected: UniquePriorityQueue nodes{}; std::unique_ptr dataLogger; - std::size_t nextNodeId = 0; + std::size_t nextNodeId = 0; + bool principallyAdmissibleHeur = true; + bool tightHeur = true; + bool fidelityAwareHeur = false; + + /** + * @brief check the `results.config` for any invalid settings + */ + virtual void checkParameters(); /** * @brief creates an initial mapping of logical qubits to physical qubits with @@ -183,26 +159,6 @@ class HeuristicMapper : public Mapper { */ virtual void staticInitialMapping(); - /** - * @brief returns distance of the given logical qubit pair according to the - * current mapping - */ - double distanceOnArchitectureOfLogicalQubits(std::uint16_t control, - std::uint16_t target) { - return architecture->distance( - static_cast(locations.at(control)), - static_cast(locations.at(target))); - } - - /** - * @brief returns distance of the given physical qubit pair on the - * architecture - */ - double distanceOnArchitectureOfPhysicalQubits(std::uint16_t control, - std::uint16_t target) { - return architecture->distance(control, target); - } - /** * @brief map the logical qubit `target` to a free physical qubit, that is * nearest to the physical qubit `source` is mapped to @@ -246,27 +202,35 @@ class HeuristicMapper : public Mapper { * * uses `HeuristicMapper::nodes` as a priority queue for the A*-search, * assumed to be empty (or at least containing only nodes compliant with the - * current layer in their fields `costHeur` and `done`) + * current layer in their fields `costHeur` and `validMapping`) * * @param layer index of the current circuit layer * @param reverse if true, the circuit is mapped from the end to the beginning */ virtual Node aStarMap(std::size_t layer, bool reverse); + /** + * @brief Get all qubits that are acted on by a relevant gate in the given + * layer + * + * @param layer the layer for which to get the considered qubits + */ + const std::set& getConsideredQubits(std::size_t layer) const { + if (fidelityAwareHeur) { + return activeQubits.at(layer); + } + return activeQubits2QGates.at(layer); + } + /** * @brief expand the given node by calling `expand_node_add_one_swap` for all * possible swaps, which creates new search nodes and adds them to * `HeuristicMapper::nodes` * - * @param consideredQubits set of all qubits that are acted on by a - * 2-qubit-gate in the respective layer * @param node current search node * @param layer index of current circuit layer - * @param twoQubitGateMultiplicity number of two qubit gates acting on pairs - * of logical qubits in the current layer */ - void expandNode(const std::unordered_set& consideredQubits, - Node& node, std::size_t layer); + void expandNode(Node& node, std::size_t layer); /** * @brief creates a new node with a swap on the given edge and adds it to @@ -275,28 +239,174 @@ class HeuristicMapper : public Mapper { * @param swap edge on which to perform a swap * @param node current search node * @param layer index of current circuit layer - * @param twoQubitGateMultiplicity number of two qubit gates acting on pairs - * of logical qubits in the current layer */ - void expandNodeAddOneSwap( - const Edge& swap, Node& node, std::size_t layer, - const std::unordered_set& consideredQubits); + void expandNodeAddOneSwap(const Edge& swap, Node& node, std::size_t layer); + + /** + * @brief applies an in-place swap of 2 virtual qubits in the given node and + * recalculates all costs accordingly + * + * @param swap physical edge on which to perform a swap + * @param layer index of current circuit layer + * @param node search node in which to apply the swap + */ + void applySWAP(const Edge& swap, std::size_t layer, Node& node); + + /** + * @brief applies an in-place teleportation of 2 virtual qubits in the given + * node and recalculates all costs accordingly + * + * @param swap pair of physical qubits on which to perform a teleportation + * @param layer index of current circuit layer + * @param node search node in which to apply the swap + */ + void applyTeleportation(const Edge& swap, std::size_t layer, Node& node); + + /** + * @brief increments `node.sharedSwaps` if the given swap is shared with + * another qubit such that both qubits get closer to being validly mapped + * + * @param swap the swap to check + * @param layer index of current circuit layer + * @param node search node in which to update `sharedSwaps` + */ + void updateSharedSwaps(const Edge& swap, std::size_t layer, Node& node); + + /** + * @brief recalculates the fixed cost of the node from the current mapping and + * swaps + * + * @param layer index of current circuit layer + * @param node search node for which to recalculate the fixed cost + */ + void recalculateFixedCost(std::size_t layer, Node& node); + + /** + * @brief recalculates the fidelity-aware fixed cost of the node from the + * current mapping and swaps + * + * @param layer index of current circuit layer + * @param node search node for which to recalculate the fixed cost + */ + void recalculateFixedCostFidelity(std::size_t layer, Node& node); + + /** + * @brief recalculates the gate-count-optimizing fixed cost of the node from + * the current mapping and swaps + * + * @param node search node for which to recalculate the fixed cost + */ + void recalculateFixedCostNonFidelity(Node& node); + + /** + * @brief recalculates the gate-count-optimizing fixed cost of all reversals + * in the current mapping of a goal node or sets it to 0 otherwise + * + * @param layer index of current circuit layer + * @param node search node for which to recalculate the fixed cost + */ + void recalculateFixedCostReversals(std::size_t layer, Node& node); + + /** + * @brief calculates the heuristic cost of the current mapping in the node + * for some given layer and writes it to `Node::costHeur`, additionally + * `Node::validMapping` is set to true if all qubit pairs sharing a gate in + * the current layer are mapped next to each other + * + * @param layer index of current circuit layer + * @param node search node for which to calculate the heuristic cost + */ + void updateHeuristicCost(std::size_t layer, Node& node); + + /** + * @brief calculates the heuristic using `Heuristic::GateCountMaxDistance` + * + * @param layer index of current circuit layer + * @param node search node for which to calculate the heuristic cost + * + * @return heuristic cost + */ + double heuristicGateCountMaxDistance(std::size_t layer, Node& node); + + /** + * @brief calculates the heuristic using `Heuristic::GateCountSumDistance` + * + * @param layer index of current circuit layer + * @param node search node for which to calculate the heuristic cost + * + * @return heuristic cost + */ + double heuristicGateCountSumDistance(std::size_t layer, Node& node); + + /** + * @brief calculates the heuristic using + * `Heuristic::GateCountSumDistanceMinusSharedSwaps` + * + * @param layer index of current circuit layer + * @param node search node for which to calculate the heuristic cost + * + * @return heuristic cost + */ + double heuristicGateCountSumDistanceMinusSharedSwaps(std::size_t layer, + Node& node); + + /** + * @brief calculates the heuristic using + * `Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps` + * + * @param layer index of current circuit layer + * @param node search node for which to calculate the heuristic cost + * + * @return heuristic cost + */ + double + heuristicGateCountMaxDistanceOrSumDistanceMinusSharedSwaps(std::size_t layer, + Node& node); + + /** + * @brief calculates the heuristic using + * `Heuristic::FidelityBestLocation` + * + * @param layer index of current circuit layer + * @param node search node for which to calculate the heuristic cost + * + * @return heuristic cost + */ + double heuristicFidelityBestLocation(std::size_t layer, Node& node); /** - * @brief calculates the heuristic cost for the following layers and saves it - * in the node as `lookaheadPenalty` + * @brief calculates an estimation of the heuristic cost for the following + * layers (depreciated by a constant factor growing with each layer) and + * saves it in the node as `Node::lookaheadPenalty` * * @param layer index of current circuit layer * @param node search node for which to calculate lookahead penalty */ - void lookahead(std::size_t layer, Node& node); + void updateLookaheadPenalty(std::size_t layer, Node& node); - double heuristicAddition(const double currentCost, const double newCost) { - if (results.config.admissibleHeuristic) { - return std::max(currentCost, newCost); - } - return currentCost + newCost; - } + /** + * @brief calculates the lookahead penalty for one layer using + * `LookaheadHeuristic::GateCountMaxDistance` + * + * @param layer index of the circuit layer for which to calculate the + * lookahead penalty + * @param node search node for which to calculate the heuristic cost + * + * @return lookahead penalty + */ + double lookaheadGateCountMaxDistance(std::size_t layer, Node& node); + + /** + * @brief calculates the lookahead penalty for one layer using + * `LookaheadHeuristic::GateCountSumDistance` + * + * @param layer index of the circuit layer for which to calculate the + * lookahead penalty + * @param node search node for which to calculate the heuristic cost + * + * @return lookahead penalty + */ + double lookaheadGateCountSumDistance(std::size_t layer, Node& node); static double computeEffectiveBranchingRate(std::size_t nodesProcessed, const std::size_t solutionDepth) { @@ -341,17 +451,21 @@ inline bool operator<(const HeuristicMapper::Node& x, inline bool operator>(const HeuristicMapper::Node& x, const HeuristicMapper::Node& y) { + // order nodes by costFixed + costHeur + lookaheadPenalty (increasing) + // then by validMapping (true before false) + // then by costHeur + lookaheadPenalty (increasing), + // equivalent to ordering by costFixed (decreasing) + // then by the amount of validly mapped 2q gates (decreasing) + // then by the qubit mapping (lexicographically) as an arbitrary but + // consistent tie-breaker const auto xcost = x.getTotalCost(); const auto ycost = y.getTotalCost(); if (std::abs(xcost - ycost) > 1e-6) { return xcost > ycost; } - if (x.done) { - return false; - } - if (y.done) { - return true; + if (x.validMapping != y.validMapping) { + return y.validMapping; } const auto xheur = x.costHeur + x.lookaheadPenalty; @@ -359,5 +473,11 @@ inline bool operator>(const HeuristicMapper::Node& x, if (std::abs(xheur - yheur) > 1e-6) { return xheur > yheur; } + + if (x.validMappedTwoQubitGates.size() != y.validMappedTwoQubitGates.size()) { + return x.validMappedTwoQubitGates.size() < + y.validMappedTwoQubitGates.size(); + } + return x < y; } diff --git a/include/utils.hpp b/include/utils.hpp index 75d019299..d333d96c9 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include using Matrix = std::vector>; @@ -55,86 +53,82 @@ class QMAPException : public std::runtime_error { class Dijkstra { public: struct Node { - /** true if the path contains any forward edge - (i.e. an edge on which a CNOT with directionality q1->q2 - can be applied without the need of a CNOT reversal) */ - bool containsCorrectEdge = false; /** true if the node has already been expanded */ bool visited = false; /** current qubit */ std::optional pos = std::nullopt; /** current cost of the path */ double cost = -1.; - /** current cost of the path with the last swap removed */ - double prevCost = -1.; }; /** * @brief builds a distance table containing the minimal costs for moving - * logical qubits from one physical qubit to/next to another (along the - * cheapest path) + * logical qubits from one physical qubit to another (along the cheapest path) * * e.g. cost of moving qubit q1 onto q2: * distanceTable[q1][q2] * - * @param n size of the distance table (i.e. number of qubits) * @param couplingMap coupling map specifying all edges in the architecture * @param distanceTable target table * @param edgeWeights matrix containing costs for swapping any two, connected * qubits (this might be uniform for all edges or different for each edge, as * e.g. in the case of fidelity-aware distances or distances on * mixed bi/unidirectional architectures) - * @param reversalCost cost added if path consists only of back edges, i.e. - * the 2-qubit gate to be mapped would need to be reversed in any case - * (if reversal costs are handled elsewhere this should be set to 0) - * @param removeLastEdge if set to true, the cost for the last swap on any - * path is removed (i.e. distance is given for moving "next to" qubit) */ - static void buildTable(std::uint16_t n, const CouplingMap& couplingMap, - Matrix& distanceTable, const Matrix& edgeWeights, - double reversalCost, bool removeLastEdge); + static void buildTable(const CouplingMap& couplingMap, Matrix& distanceTable, + const Matrix& edgeWeights); /** * @brief builds a 3d matrix containing the distance tables giving the minimal * distances between 2 qubit when upto k edges can be skipped. * * e.g. cost of moving qubit q1 onto q2 skipping upto 3 edges: - * edgeSkipDistanceTable[3][q1][q2] + * distanceTables[3][q1][q2] * - * if k > edgeSkipDistanceTable.size() a cost of 0 can be assumed + * if k > distanceTables.size() a cost of 0 can be assumed * - * this implementation does not work with distance tables containing CNOT - * reversal costs or the last edge removed - * (only pure distances between 2 qubits) + * @param couplingMap coupling map specifying all edges in the architecture + * @param distanceTables vector to fill with target tables (from 0 skips in + * the first entry to k skips in the last entry, where k is the last index + * not containing a matrix of pure 0s i.e. k+1 = diameter of the coupling + * graph) + * @param edgeWeights matrix containing costs for swapping any two, connected + * qubits (this might be uniform for all edges or different for each edge, as + * e.g. in the case of fidelity-aware distances or distances on + * mixed bi/unidirectional architectures) + */ + static void buildEdgeSkipTable(const CouplingMap& couplingMap, + std::vector& distanceTables, + const Matrix& edgeWeights); + /** + * @brief builds a distance table containing the minimal costs for moving + * logical qubits from one physical qubit to another (along the cheapest path) + * while skipping a single edge, i.e. equivalent to buildEdgeSkipTable(...)[1] + * + * An additional reversal cost can be specified, which is added to the cost if + * the skipped edge is a back edge * - * @param distanceTable 2d matrix containing pure distances between any 2 - * qubits: distanceTable[source][target] + * @param distanceTable 2d matrix containing distances between any 2 qubits: + * distanceTable[source][target] * @param couplingMap coupling map specifying all edges in the architecture - * @param edgeSkipDistanceTable 3d target table + * @param reversalCost cost for reversing an edge + * @param edgeSkipDistanceTable target distance table */ - static void buildEdgeSkipTable(const Matrix& distanceTable, - const CouplingMap& couplingMap, - std::vector& edgeSkipDistanceTable); + static void buildSingleEdgeSkipTable(const Matrix& distanceTable, + const CouplingMap& couplingMap, + double reversalCost, + Matrix& edgeSkipDistanceTable); protected: static void dijkstra(const CouplingMap& couplingMap, std::vector& nodes, - std::uint16_t start, const Matrix& edgeWeights, - double reversalCost); + std::uint16_t start, const Matrix& edgeWeights); struct NodeComparator { - bool operator()(const Node* x, const Node* y) { - if (x->cost != y->cost) { - return x->cost > y->cost; - } - return !x->containsCorrectEdge && y->containsCorrectEdge; - } + bool operator()(const Node* x, const Node* y) { return x->cost > y->cost; } }; }; inline bool operator<(const Dijkstra::Node& x, const Dijkstra::Node& y) { - if (x.cost != y.cost) { - return x.cost < y.cost; - } - return x.containsCorrectEdge && !y.containsCorrectEdge; + return x.cost < y.cost; } /// Iterating routine through all combinations diff --git a/src/Architecture.cpp b/src/Architecture.cpp index 23cec6287..e613d1c93 100644 --- a/src/Architecture.cpp +++ b/src/Architecture.cpp @@ -185,21 +185,34 @@ Architecture::Architecture(const std::uint16_t nQ, const CouplingMap& cm, } void Architecture::createDistanceTable() { - isBidirectional = true; + isBidirectional = true; + isUnidirectional = true; Matrix edgeWeights(nqubits, std::vector( nqubits, std::numeric_limits::max())); for (const auto& edge : couplingMap) { if (couplingMap.find({edge.second, edge.first}) == couplingMap.end()) { + // unidirectional edge isBidirectional = false; edgeWeights.at(edge.second).at(edge.first) = COST_UNIDIRECTIONAL_SWAP; edgeWeights.at(edge.first).at(edge.second) = COST_UNIDIRECTIONAL_SWAP; } else { + // bidirectional edge + isUnidirectional = false; edgeWeights.at(edge.first).at(edge.second) = COST_BIDIRECTIONAL_SWAP; } } - Dijkstra::buildTable(nqubits, couplingMap, distanceTable, edgeWeights, - COST_DIRECTION_REVERSE, true); + Matrix simpleDistanceTable{}; + Dijkstra::buildTable(couplingMap, simpleDistanceTable, edgeWeights); + Dijkstra::buildSingleEdgeSkipTable(simpleDistanceTable, couplingMap, 0., + distanceTable); + if (bidirectional()) { + distanceTableReversals = distanceTable; + } else { + Dijkstra::buildSingleEdgeSkipTable(simpleDistanceTable, couplingMap, + COST_DIRECTION_REVERSE, + distanceTableReversals); + } } void Architecture::createFidelityTable() { @@ -263,11 +276,8 @@ void Architecture::createFidelityTable() { fidelityDistanceTables.clear(); if (fidelityAvailable) { - Matrix distances = {}; - Dijkstra::buildTable(nqubits, couplingMap, distances, swapFidelityCosts, 0., - false); - Dijkstra::buildEdgeSkipTable(distances, couplingMap, - fidelityDistanceTables); + Dijkstra::buildEdgeSkipTable(couplingMap, fidelityDistanceTables, + swapFidelityCosts); } } diff --git a/src/DataLogger.cpp b/src/DataLogger.cpp index 7b5708f1e..b354da480 100644 --- a/src/DataLogger.cpp +++ b/src/DataLogger.cpp @@ -15,7 +15,7 @@ void DataLogger::initLog() { } const std::filesystem::path dirPath(dataLoggingPath); if (!std::filesystem::exists(dirPath)) { - std::filesystem::create_directory(dirPath); + std::filesystem::create_directories(dirPath); } clearLog(); }; @@ -87,8 +87,7 @@ void DataLogger::logFinalizeLayer( std::size_t finalNodeId, double finalCostFixed, double finalCostHeur, double finalLookaheadPenalty, const std::array& finalLayout, - const std::vector>& finalSwaps, - std::size_t finalSearchDepth) { + const std::vector& finalSwaps, std::size_t finalSearchDepth) { if (deactivated) { return; } @@ -141,14 +140,12 @@ void DataLogger::logFinalizeLayer( if (finalSwaps.empty()) { json["final_swaps"] = nlohmann::json::array(); } else { - auto& finalSwapsJSON = json["final_swaps"]; - for (const auto& swaps : finalSwaps) { - std::size_t i = 0; - for (const auto& swap : swaps) { - finalSwapsJSON[i][0] = swap.first; - finalSwapsJSON[i][1] = swap.second; - ++i; - } + auto& finalSwapsJSON = json["final_swaps"]; + std::size_t i = 0; + for (const auto& swap : finalSwaps) { + finalSwapsJSON[i][0] = swap.first; + finalSwapsJSON[i][1] = swap.second; + ++i; } } json["final_search_depth"] = finalSearchDepth; @@ -188,8 +185,7 @@ void DataLogger::logSearchNode( std::size_t layerIndex, std::size_t nodeId, std::size_t parentId, double costFixed, double costHeur, double lookaheadPenalty, const std::array& qubits, - bool validMapping, const std::vector>& swaps, - std::size_t depth) { + bool validMapping, const std::vector& swaps, std::size_t depth) { if (deactivated) { return; } @@ -214,18 +210,16 @@ void DataLogger::logSearchNode( of.seekp(-1, std::ios_base::cur); // remove last comma } of << ";"; - for (const auto& sw : swaps) { - for (const auto& s : sw) { - of << s.first << " " << s.second; - if (s.op != qc::OpType::SWAP) { - of << " " << s.op; - if (s.middleAncilla != - std::numeric_limits::max()) { - of << " " << s.middleAncilla; - } + for (const auto& s : swaps) { + of << s.first << " " << s.second; + if (s.op != qc::OpType::SWAP) { + of << " " << s.op; + if (s.middleAncilla != + std::numeric_limits::max()) { + of << " " << s.middleAncilla; } - of << ","; } + of << ","; } if (!swaps.empty()) { of.seekp(-1, std::ios_base::cur); // remove last comma diff --git a/src/Mapper.cpp b/src/Mapper.cpp index ec8ade790..c6026ee40 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -24,7 +24,6 @@ Mapper::Mapper(qc::QuantumComputation quantumComputation, Architecture& arch) : qc(std::move(quantumComputation)), architecture(&arch) { qubits.fill(DEFAULT_POSITION); locations.fill(DEFAULT_POSITION); - fidelities.fill(INITIAL_FIDELITY); // strip away qubits that are not used in the circuit qc.stripIdleQubits(true, true); @@ -217,12 +216,12 @@ void Mapper::createLayers() { layers.size(), SingleQubitMultiplicity(architecture->getNqubits(), 0)); twoQubitMultiplicities = std::vector(layers.size(), TwoQubitMultiplicity{}); - activeQubits = std::vector>( - layers.size(), std::unordered_set{}); - activeQubits1QGates = std::vector>( - layers.size(), std::unordered_set{}); - activeQubits2QGates = std::vector>( - layers.size(), std::unordered_set{}); + activeQubits = std::vector>( + layers.size(), std::set{}); + activeQubits1QGates = std::vector>( + layers.size(), std::set{}); + activeQubits2QGates = std::vector>( + layers.size(), std::set{}); for (std::size_t i = 0; i < layers.size(); ++i) { for (const auto& gate : layers[i]) { @@ -289,12 +288,12 @@ void Mapper::splitLayer(std::size_t index, Architecture& arch) { SingleQubitMultiplicity singleQubitMultiplicity1(arch.getNqubits(), 0); TwoQubitMultiplicity twoQubitMultiplicity0{}; TwoQubitMultiplicity twoQubitMultiplicity1{}; - std::unordered_set activeQubits0{}; - std::unordered_set activeQubits1QGates0{}; - std::unordered_set activeQubits2QGates0{}; - std::unordered_set activeQubits1{}; - std::unordered_set activeQubits1QGates1{}; - std::unordered_set activeQubits2QGates1{}; + std::set activeQubits0{}; + std::set activeQubits1QGates0{}; + std::set activeQubits2QGates0{}; + std::set activeQubits1{}; + std::set activeQubits1QGates1{}; + std::set activeQubits2QGates1{}; // 2Q-gates bool even = false; @@ -392,24 +391,21 @@ void Mapper::splitLayer(std::size_t index, Architecture& arch) { activeQubits[index] = activeQubits0; activeQubits.insert( activeQubits.begin() + - static_cast< - std::vector>::difference_type>( + static_cast>::difference_type>( index) + 1, activeQubits1); activeQubits1QGates[index] = activeQubits1QGates0; activeQubits1QGates.insert( activeQubits1QGates.begin() + - static_cast< - std::vector>::difference_type>( + static_cast>::difference_type>( index) + 1, activeQubits1QGates1); activeQubits2QGates[index] = activeQubits2QGates0; activeQubits2QGates.insert( activeQubits2QGates.begin() + - static_cast< - std::vector>::difference_type>( + static_cast>::difference_type>( index) + 1, activeQubits2QGates1); diff --git a/src/configuration/Configuration.cpp b/src/configuration/Configuration.cpp index 475fdb302..0cf126101 100644 --- a/src/configuration/Configuration.cpp +++ b/src/configuration/Configuration.cpp @@ -19,18 +19,28 @@ nlohmann::json Configuration::json() const { config["debug"] = debug; if (method == Method::Heuristic) { - auto& heuristic = config["settings"]; - heuristic["initial_layout"] = ::toString(initialLayout); - heuristic["admissible_heuristic"] = admissibleHeuristic; - heuristic["consider_fidelity"] = considerFidelity; - if (lookahead) { - auto& lookaheadSettings = heuristic["lookahead"]; + auto& heuristicJson = config["settings"]; + heuristicJson["heuristic"] = ::toString(heuristic); + auto& heuristicPropertiesJson = heuristicJson["heuristic_properties"]; + heuristicPropertiesJson["admissible"] = isAdmissible(heuristic); + heuristicPropertiesJson["principally_admissible"] = + isPrincipallyAdmissible(heuristic); + heuristicPropertiesJson["tight"] = isTight(heuristic); + heuristicPropertiesJson["fidelity_aware"] = isFidelityAware(heuristic); + heuristicJson["initial_layout"] = ::toString(initialLayout); + if (lookaheadHeuristic != LookaheadHeuristic::None) { + auto& lookaheadSettings = heuristicJson["lookahead"]; + lookaheadSettings["heuristic"] = ::toString(lookaheadHeuristic); + auto& lookaheadHeuristicPropertiesJson = + lookaheadSettings["heuristic_properties"]; + lookaheadHeuristicPropertiesJson["fidelity_aware"] = + isFidelityAware(lookaheadHeuristic); lookaheadSettings["lookaheads"] = nrLookaheads; lookaheadSettings["first_factor"] = firstLookaheadFactor; lookaheadSettings["factor"] = lookaheadFactor; } if (useTeleportation) { - auto& teleportation = heuristic["teleportation"]; + auto& teleportation = heuristicJson["teleportation"]; teleportation["qubits"] = teleportationQubits; teleportation["seed"] = teleportationSeed; teleportation["fake"] = teleportationFake; diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index 33196eabd..5d78a0c66 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -7,6 +7,7 @@ #include "utils.hpp" +#include #include void HeuristicMapper::map(const Configuration& configuration) { @@ -14,25 +15,14 @@ void HeuristicMapper::map(const Configuration& configuration) { dataLogger = std::make_unique(configuration.dataLoggingPath, *architecture, qc); } - results = MappingResults{}; - results.config = configuration; - auto& config = results.config; - if (config.layering == Layering::OddGates || - config.layering == Layering::QubitTriangle) { - throw QMAPException("Layering strategy " + toString(config.layering) + - " not suitable for heuristic mapper!"); - } - if (config.considerFidelity && !architecture->isFidelityAvailable()) { - throw QMAPException("No calibration data available for this architecture!"); - } - if (config.considerFidelity && config.lookahead) { - throw QMAPException("Lookahead is not yet supported for heuristic mapper " - "using fidelity-aware mapping!"); - } - if (config.considerFidelity && config.teleportationQubits > 0) { - throw QMAPException("Teleportation is not yet supported for heuristic " - "mapper using fidelity-aware mapping!"); - } + + tightHeur = isTight(configuration.heuristic); + fidelityAwareHeur = isFidelityAware(configuration.heuristic); + + results = MappingResults{}; + results.config = configuration; + const auto& config = results.config; + checkParameters(); const auto start = std::chrono::steady_clock::now(); initResults(); @@ -128,6 +118,30 @@ void HeuristicMapper::staticInitialMapping() { } } +void HeuristicMapper::checkParameters() { + const auto& config = results.config; + if (config.layering == Layering::OddGates || + config.layering == Layering::QubitTriangle) { + throw QMAPException("Layering strategy " + toString(config.layering) + + " not suitable for heuristic mapper!"); + } + if (fidelityAwareHeur && !architecture->isFidelityAvailable()) { + throw QMAPException("Fidelity aware heuristic chosen, but no or " + "insufficient calibration data available for this " + "architecture!"); + } + if (fidelityAwareHeur && !isFidelityAware(config.lookaheadHeuristic) && + config.lookaheadHeuristic != LookaheadHeuristic::None) { + throw QMAPException("Fidelity-aware heuristics may only be used with " + "fidelity-aware lookahead heuristics (or no " + "lookahead)!"); + } + if (fidelityAwareHeur && config.teleportationQubits > 0) { + throw QMAPException("Teleportation is not yet supported for heuristic " + "mapper using fidelity-aware mapping!"); + } +} + void HeuristicMapper::createInitialMapping() { auto& config = results.config; @@ -149,7 +163,9 @@ void HeuristicMapper::createInitialMapping() { mt.seed(config.teleportationSeed); } - std::uniform_int_distribution<> dis(0, architecture->getNqubits() - 1); + std::uniform_int_distribution<> dis( + 0, + static_cast(architecture->getCouplingMap().size() - 1)); for (std::size_t i = 0; i < config.teleportationQubits; i += 2) { Edge e{}; @@ -193,7 +209,7 @@ void HeuristicMapper::createInitialMapping() { } void HeuristicMapper::mapUnmappedGates(std::size_t layer) { - if (results.config.considerFidelity) { + if (fidelityAwareHeur) { for (std::size_t q = 0; q < singleQubitMultiplicities.at(layer).size(); ++q) { if (singleQubitMultiplicities.at(layer).at(q) == 0) { @@ -217,8 +233,8 @@ void HeuristicMapper::mapUnmappedGates(std::size_t layer) { for (const auto& [logEdge, _] : twoQubitMultiplicities.at(layer)) { const auto& [q1, q2] = logEdge; - auto q1Location = locations.at(q1); - auto q2Location = locations.at(q2); + const auto q1Location = locations.at(q1); + const auto q2Location = locations.at(q2); if (q1Location == DEFAULT_POSITION && q2Location == DEFAULT_POSITION) { std::set possibleEdges{}; @@ -279,7 +295,7 @@ void HeuristicMapper::mapToMinDistance(const std::uint16_t source, for (std::uint16_t i = 0; i < architecture->getNqubits(); ++i) { if (qubits.at(i) == DEFAULT_POSITION) { // TODO: Consider fidelity here if available - auto distance = distanceOnArchitectureOfPhysicalQubits( + const auto distance = architecture->distance( static_cast(locations.at(source)), i); if (distance < min) { min = distance; @@ -309,7 +325,7 @@ void HeuristicMapper::pseudoRouteCircuit(bool reverse) { config.debug = false; for (std::size_t i = 0; i < layers.size(); ++i) { - auto layerIndex = (reverse ? layers.size() - i - 1 : i); + const auto layerIndex = (reverse ? layers.size() - i - 1 : i); const Node result = aStarMap(layerIndex, reverse); qubits = result.qubits; @@ -332,7 +348,7 @@ void HeuristicMapper::pseudoRouteCircuit(bool reverse) { } void HeuristicMapper::routeCircuit() { - auto& config = results.config; + const auto& config = results.config; std::size_t gateidx = 0; std::vector gatesToAdjust{}; @@ -355,36 +371,31 @@ void HeuristicMapper::routeCircuit() { // initial layer needs no swaps if (layerIndex != 0 || config.swapOnFirstLayer) { - for (const auto& swaps : result.swaps) { - for (const auto& swap : swaps) { - if (swap.op == qc::SWAP) { - if (config.verbose) { - std::clog << "SWAP: " << swap.first << " <-> " << swap.second - << "\n"; - } - if (!architecture->isEdgeConnected({swap.first, swap.second}) && - !architecture->isEdgeConnected({swap.second, swap.first})) { - throw QMAPException( - "Invalid SWAP: " + std::to_string(swap.first) + "<->" + - std::to_string(swap.second)); - } - qcMapped.swap(swap.first, swap.second); - results.output.swaps++; - } else if (swap.op == qc::Teleportation) { - if (config.verbose) { - std::clog << "TELE: " << swap.first << " <-> " << swap.second - << "\n"; - } - qcMapped.emplace_back( - qcMapped.getNqubits(), - qc::Targets{static_cast(swap.first), - static_cast(swap.second), - static_cast(swap.middleAncilla)}, - qc::Teleportation); - results.output.teleportations++; + for (const auto& swap : result.swaps) { + if (swap.op == qc::SWAP) { + if (config.verbose) { + std::clog << "SWAP: " << swap.first << " <-> " << swap.second + << "\n"; } - gateidx++; + // check if SWAP is placed on a valid edge + assert( + architecture->isEdgeConnected({swap.first, swap.second}, false)); + qcMapped.swap(swap.first, swap.second); + results.output.swaps++; + } else if (swap.op == qc::Teleportation) { + if (config.verbose) { + std::clog << "TELE: " << swap.first << " <-> " << swap.second + << "\n"; + } + qcMapped.emplace_back( + qcMapped.getNqubits(), + qc::Targets{static_cast(swap.first), + static_cast(swap.second), + static_cast(swap.middleAncilla)}, + qc::Teleportation); + results.output.teleportations++; } + gateidx++; } } @@ -416,11 +427,8 @@ void HeuristicMapper::routeCircuit() { locations.at(gate.target)}; if (!architecture->isEdgeConnected(cnot)) { const Edge reversed = {cnot.second, cnot.first}; - if (!architecture->isEdgeConnected(reversed)) { - throw QMAPException( - "Invalid CNOT: " + std::to_string(reversed.first) + "-" + - std::to_string(reversed.second)); - } + // check if CNOT is placed on a valid edge + assert(architecture->isEdgeConnected(reversed)); qcMapped.h(reversed.first); qcMapped.h(reversed.second); qcMapped.cx(qc::Control{static_cast(reversed.first)}, @@ -495,8 +503,8 @@ void HeuristicMapper::routeCircuit() { } if (!gatesToAdjust.empty() && gatesToAdjust.back() == gateidx) { gatesToAdjust.pop_back(); - auto target = op->getTargets().at(0); - auto targetLocation = locations.at(target); + const auto target = op->getTargets().at(0); + const auto targetLocation = locations.at(target); if (targetLocation == -1) { // qubit only occurs in single qubit gates, can be mapped to an @@ -530,33 +538,30 @@ void HeuristicMapper::routeCircuit() { } HeuristicMapper::Node HeuristicMapper::aStarMap(size_t layer, bool reverse) { - auto& config = results.config; - const bool considerFidelity = config.considerFidelity; - nextNodeId = 0; + const auto& config = results.config; + nextNodeId = 0; - const std::unordered_set& consideredQubits = - (considerFidelity ? activeQubits[layer] : activeQubits2QGates[layer]); const SingleQubitMultiplicity& singleQubitMultiplicity = singleQubitMultiplicities.at(layer); const TwoQubitMultiplicity& twoQubitMultiplicity = twoQubitMultiplicities.at(layer); - Node node(nextNodeId++, considerFidelity, config.admissibleHeuristic); + Node node(nextNodeId++); Node bestDoneNode(0); - bool done = false; + bool validMapping = false; mapUnmappedGates(layer); node.locations = locations; node.qubits = qubits; - node.recalculateFixedCost(*architecture, singleQubitMultiplicity, - twoQubitMultiplicity); - node.updateHeuristicCost(*architecture, singleQubitMultiplicity, - twoQubitMultiplicity, consideredQubits); + recalculateFixedCost(layer, node); + updateHeuristicCost(layer, node); + updateLookaheadPenalty(layer, node); if (config.dataLoggingEnabled()) { - dataLogger->logSearchNode(layer, node.id, node.parent, node.costFixed, + dataLogger->logSearchNode(layer, node.id, node.parent, + node.costFixed + node.costFixedReversals, node.costHeur, node.lookaheadPenalty, node.qubits, - node.done, node.swaps, node.depth); + node.validMapping, node.swaps, node.depth); } nodes.push(node); @@ -571,8 +576,9 @@ HeuristicMapper::Node HeuristicMapper::aStarMap(size_t layer, bool reverse) { const bool splittable = config.automaticLayerSplits ? isLayerSplittable(layer) : false; - while (!nodes.empty() && (!done || nodes.top().getTotalCost() < - bestDoneNode.getTotalFixedCost())) { + while (!nodes.empty() && + (!validMapping || + nodes.top().getTotalCost() < bestDoneNode.getTotalFixedCost())) { if (splittable && expandedNodes >= config.automaticLayerSplitsNodeLimit) { if (config.dataLoggingEnabled()) { qc::CompoundOperation compOp(architecture->getNqubits()); @@ -596,9 +602,9 @@ HeuristicMapper::Node HeuristicMapper::aStarMap(size_t layer, bool reverse) { return aStarMap(reverse ? layer + 1 : layer, reverse); } Node current = nodes.top(); - if (current.done) { + if (current.validMapping) { ++solutionNodes; - if (!done || + if (!validMapping || current.getTotalFixedCost() < bestDoneNode.getTotalFixedCost()) { bestDoneNode = current; expandedNodesAfterOptimalSolution = 0; @@ -606,15 +612,15 @@ HeuristicMapper::Node HeuristicMapper::aStarMap(size_t layer, bool reverse) { } else { ++solutionNodesAfterOptimalSolution; } - done = true; - if (!considerFidelity) { + validMapping = true; + if (tightHeur) { break; } } nodes.pop(); - expandNode(consideredQubits, current, layer); + expandNode(current, layer); ++expandedNodes; - if (done) { + if (validMapping) { ++expandedNodesAfterFirstSolution; ++expandedNodesAfterOptimalSolution; @@ -647,7 +653,7 @@ HeuristicMapper::Node HeuristicMapper::aStarMap(size_t layer, bool reverse) { } } - if (!done) { + if (!validMapping) { throw QMAPException("No viable mapping found."); } @@ -708,9 +714,8 @@ HeuristicMapper::Node HeuristicMapper::aStarMap(size_t layer, bool reverse) { return result; } -void HeuristicMapper::expandNode( - const std::unordered_set& consideredQubits, Node& node, - std::size_t layer) { +void HeuristicMapper::expandNode(Node& node, std::size_t layer) { + const auto& consideredQubits = getConsideredQubits(layer); std::vector> usedSwaps; usedSwaps.reserve(architecture->getNqubits()); for (int p = 0; p < architecture->getNqubits(); ++p) { @@ -766,148 +771,236 @@ void HeuristicMapper::expandNode( for (const auto& edge : perms) { if (edge.first == node.locations.at(q) || edge.second == node.locations.at(q)) { - auto q1 = node.qubits.at(edge.first); - auto q2 = node.qubits.at(edge.second); + const auto q1 = node.qubits.at(edge.first); + const auto q2 = node.qubits.at(edge.second); if (q2 == -1 || q1 == -1) { - expandNodeAddOneSwap(edge, node, layer, consideredQubits); + expandNodeAddOneSwap(edge, node, layer); } else if (!usedSwaps.at(static_cast(q1)) .at(static_cast(q2))) { usedSwaps.at(static_cast(q1)) .at(static_cast(q2)) = true; usedSwaps.at(static_cast(q2)) .at(static_cast(q1)) = true; - expandNodeAddOneSwap(edge, node, layer, consideredQubits); + expandNodeAddOneSwap(edge, node, layer); } } } } } -void HeuristicMapper::expandNodeAddOneSwap( - const Edge& swap, Node& node, const std::size_t layer, - const std::unordered_set& consideredQubits) { - const auto& config = results.config; - - Node newNode = Node(nextNodeId++, node.id, node.qubits, node.locations, - node.swaps, node.costFixed, node.depth + 1, - node.considerFidelity, node.admissibleHeuristic); +void HeuristicMapper::expandNodeAddOneSwap(const Edge& swap, Node& node, + const std::size_t layer) { + Node newNode = + Node(nextNodeId++, node.id, node.qubits, node.locations, node.swaps, + node.validMappedTwoQubitGates, node.costFixed, + node.costFixedReversals, node.depth + 1, node.sharedSwaps); - if (architecture->isEdgeConnected(swap) || - architecture->isEdgeConnected(Edge{swap.second, swap.first})) { - newNode.applySWAP(swap, *architecture, singleQubitMultiplicities.at(layer), - twoQubitMultiplicities.at(layer)); + if (architecture->isEdgeConnected(swap, false)) { + applySWAP(swap, layer, newNode); } else { - newNode.applyTeleportation(swap, *architecture); - } - - newNode.updateHeuristicCost( - *architecture, singleQubitMultiplicities.at(layer), - twoQubitMultiplicities.at(layer), consideredQubits); - - // calculate heuristics for the cost of the following layers - if (config.lookahead) { - lookahead(getNextLayer(layer), newNode); + applyTeleportation(swap, layer, newNode); } nodes.push(newNode); if (results.config.dataLoggingEnabled()) { dataLogger->logSearchNode(layer, newNode.id, newNode.parent, - newNode.costFixed, newNode.costHeur, - newNode.lookaheadPenalty, newNode.qubits, - newNode.done, newNode.swaps, newNode.depth); + newNode.costFixed + newNode.costFixedReversals, + newNode.costHeur, newNode.lookaheadPenalty, + newNode.qubits, newNode.validMapping, + newNode.swaps, newNode.depth); } } -void HeuristicMapper::lookahead(const std::size_t layer, - HeuristicMapper::Node& node) { - const auto& config = results.config; - auto nextLayer = layer; - double factor = config.firstLookaheadFactor; +void HeuristicMapper::recalculateFixedCost(std::size_t layer, Node& node) { + node.validMappedTwoQubitGates.clear(); + for (const auto& [edge, mult] : twoQubitMultiplicities.at(layer)) { + const auto [q1, q2] = edge; + const auto physQ1 = static_cast(node.locations.at(q1)); + const auto physQ2 = static_cast(node.locations.at(q2)); - for (std::size_t i = 0; i < config.nrLookaheads; ++i) { - if (nextLayer == std::numeric_limits::max()) { - break; + if (architecture->isEdgeConnected({physQ1, physQ2}, false)) { + // validly mapped + node.validMappedTwoQubitGates.emplace(q1, q2); } + } - double penalty = 0.; - for (const auto& gate : layers.at(nextLayer)) { - if (gate.singleQubit()) { - continue; - } + if (fidelityAwareHeur) { + recalculateFixedCostFidelity(layer, node); + } else { + recalculateFixedCostNonFidelity(node); + } + recalculateFixedCostReversals(layer, node); +} - auto loc1 = node.locations.at(static_cast(gate.control)); - auto loc2 = node.locations.at(gate.target); - if (loc1 == DEFAULT_POSITION && loc2 == DEFAULT_POSITION) { - // no penalty - } else if (loc1 == DEFAULT_POSITION) { - auto min = std::numeric_limits::max(); - for (std::uint16_t j = 0; j < architecture->getNqubits(); ++j) { - if (node.qubits.at(j) == DEFAULT_POSITION) { - // TODO: Consider fidelity here if available - min = std::min(min, distanceOnArchitectureOfPhysicalQubits( - j, static_cast( - node.locations.at(gate.target)))); - } - } - penalty = heuristicAddition(penalty, min); - } else if (loc2 == DEFAULT_POSITION) { - auto min = std::numeric_limits::max(); - for (std::uint16_t j = 0; j < architecture->getNqubits(); ++j) { - if (node.qubits.at(j) == DEFAULT_POSITION) { - // TODO: Consider fidelity here if available - min = std::min(min, - distanceOnArchitectureOfPhysicalQubits( - static_cast(node.locations.at( - static_cast(gate.control))), - j)); - } - } - penalty = heuristicAddition(penalty, min); +void HeuristicMapper::recalculateFixedCostReversals(std::size_t layer, + Node& node) { + node.costFixedReversals = 0.; + if (architecture->bidirectional() || fidelityAwareHeur || + node.validMappedTwoQubitGates.size() != + twoQubitMultiplicities.at(layer).size()) { + // costFixedReversals should only be non-zero in goal nodes for + // non-fidelity-aware heuristics and if there are unidirectional + // edges in the architecture + return; + } + + // only consider reversal costs as fixed in goal nodes + for (const auto& [edge, mult] : twoQubitMultiplicities.at(layer)) { + const auto [q1, q2] = edge; + const auto [forwardMult, reverseMult] = mult; + const auto physQ1 = static_cast(node.locations.at(q1)); + const auto physQ2 = static_cast(node.locations.at(q2)); + + if (!architecture->isEdgeConnected({physQ1, physQ2})) { + node.costFixedReversals += forwardMult * COST_DIRECTION_REVERSE; + } else if (!architecture->isEdgeConnected({physQ2, physQ1})) { + node.costFixedReversals += reverseMult * COST_DIRECTION_REVERSE; + } + } +} + +void HeuristicMapper::recalculateFixedCostNonFidelity(Node& node) { + node.costFixed = 0; + + // swap costs + for (auto& swap : node.swaps) { + if (swap.op == qc::SWAP) { + // branch clone intended for performance reasons (checking edge-wise for + // bidirectionality is not O(1)) + // NOLINTBEGIN(bugprone-branch-clone) + if (architecture->bidirectional()) { + node.costFixed += COST_BIDIRECTIONAL_SWAP; + } else if (architecture->unidirectional() || + !architecture->isEdgeBidirectional( + {swap.first, swap.second})) { + node.costFixed += COST_UNIDIRECTIONAL_SWAP; } else { - auto cost = architecture->distance( - static_cast( - node.locations.at(static_cast(gate.control))), - static_cast(node.locations.at(gate.target))); - penalty = heuristicAddition(penalty, cost); + node.costFixed += COST_BIDIRECTIONAL_SWAP; } + // NOLINTEND(bugprone-branch-clone) + } else if (swap.op == qc::Teleportation) { + node.costFixed += COST_TELEPORTATION; } + } +} - node.lookaheadPenalty += factor * penalty; - factor *= config.lookaheadFactor; - nextLayer = getNextLayer(nextLayer); // TODO: consider single qubits here - // for better fidelity lookahead +void HeuristicMapper::recalculateFixedCostFidelity(std::size_t layer, + Node& node) { + const auto& singleQubitGateMultiplicity = singleQubitMultiplicities.at(layer); + const auto& twoQubitGateMultiplicity = twoQubitMultiplicities.at(layer); + + node.costFixed = 0; + // adding costs of single qubit gates + for (std::uint16_t i = 0U; i < architecture->getNqubits(); ++i) { + if (singleQubitGateMultiplicity.at(i) == 0) { + continue; + } + node.costFixed += singleQubitGateMultiplicity.at(i) * + architecture->getSingleQubitFidelityCost( + static_cast(node.locations.at(i))); + } + // adding cost of the swap gates + for (auto& swap : node.swaps) { + if (swap.op == qc::SWAP) { + node.costFixed += + architecture->getSwapFidelityCost(swap.first, swap.second); + } else if (swap.op == qc::Teleportation) { + throw QMAPException("Teleportation currently not supported for " + "noise-aware mapping"); + } + } + // adding cost of two qubit gates that are already mapped next to each other + for (const auto& [edge, mult] : twoQubitGateMultiplicity) { + if (node.validMappedTwoQubitGates.find(edge) == + node.validMappedTwoQubitGates.end()) { + // 2-qubit-gates not yet validly mapped are handled in the heuristic + continue; + } + const auto [q1, q2] = edge; + const auto [forwardMult, reverseMult] = mult; + const auto physQ1 = static_cast(node.locations.at(q1)); + const auto physQ2 = static_cast(node.locations.at(q2)); + + node.costFixed += + (forwardMult * architecture->getTwoQubitFidelityCost(physQ1, physQ2) + + reverseMult * architecture->getTwoQubitFidelityCost(physQ2, physQ1)); } } -void HeuristicMapper::Node::applySWAP( - const Edge& swap, Architecture& arch, - const SingleQubitMultiplicity& singleQubitGateMultiplicity, - const TwoQubitMultiplicity& twoQubitGateMultiplicity) { - ++nswaps; - swaps.emplace_back(); - const auto q1 = qubits.at(swap.first); - const auto q2 = qubits.at(swap.second); +void HeuristicMapper::applySWAP(const Edge& swap, std::size_t layer, + Node& node) { + assert(architecture->isEdgeConnected(swap, false)); + const auto& singleQubitGateMultiplicity = singleQubitMultiplicities.at(layer); + + const auto q1 = node.qubits.at(swap.first); + const auto q2 = node.qubits.at(swap.second); + + updateSharedSwaps(swap, layer, node); - qubits.at(swap.first) = q2; - qubits.at(swap.second) = q1; + node.qubits.at(swap.first) = q2; + node.qubits.at(swap.second) = q1; if (q1 != -1) { - locations.at(static_cast(q1)) = + node.locations.at(static_cast(q1)) = static_cast(swap.second); } if (q2 != -1) { - locations.at(static_cast(q2)) = + node.locations.at(static_cast(q2)) = static_cast(swap.first); } - if (arch.isEdgeConnected(swap) || - arch.isEdgeConnected(Edge{swap.second, swap.first})) { - swaps.back().emplace_back(swap.first, swap.second, qc::SWAP); - } else { - throw QMAPException("Something wrong in applySWAP."); + node.swaps.emplace_back(swap.first, swap.second, qc::SWAP); + + // check if swap created or destroyed any valid mappings of qubit pairs + for (const auto& [edge, mult] : twoQubitMultiplicities.at(layer)) { + const auto [q3, q4] = edge; + if (q3 == q1 || q3 == q2 || q4 == q1 || q4 == q2) { + const auto physQ3 = static_cast(node.locations.at(q3)); + const auto physQ4 = static_cast(node.locations.at(q4)); + if (architecture->isEdgeConnected({physQ3, physQ4}, false)) { + // validly mapped now + if (fidelityAwareHeur && node.validMappedTwoQubitGates.find(edge) == + node.validMappedTwoQubitGates.end()) { + // not mapped validly before + // add cost of newly validly mapped gates + node.costFixed += + mult.first * + architecture->getTwoQubitFidelityCost(physQ3, physQ4) + + mult.second * + architecture->getTwoQubitFidelityCost(physQ4, physQ3); + } + node.validMappedTwoQubitGates.emplace(edge); + } else { + // not mapped validly now + if (fidelityAwareHeur && node.validMappedTwoQubitGates.find(edge) != + node.validMappedTwoQubitGates.end()) { + // mapped validly before + // remove cost of now no longer validly mapped gates + auto prevPhysQ3 = physQ3; + if (prevPhysQ3 == swap.first) { + prevPhysQ3 = swap.second; + } else if (prevPhysQ3 == swap.second) { + prevPhysQ3 = swap.first; + } + auto prevPhysQ4 = physQ4; + if (prevPhysQ4 == swap.first) { + prevPhysQ4 = swap.second; + } else if (prevPhysQ4 == swap.second) { + prevPhysQ4 = swap.first; + } + + node.costFixed -= + mult.first * architecture->getTwoQubitFidelityCost(prevPhysQ3, + prevPhysQ4) + + mult.second * + architecture->getTwoQubitFidelityCost(prevPhysQ4, prevPhysQ3); + } + node.validMappedTwoQubitGates.erase(edge); + } + } } - if (considerFidelity) { + if (fidelityAwareHeur) { std::uint16_t q1Mult = 0; std::uint16_t q2Mult = 0; if (q1 != -1) { @@ -918,82 +1011,57 @@ void HeuristicMapper::Node::applySWAP( } // accounting for fidelity difference of single qubit gates (two qubit // gates are handled in the heuristic) - costFixed += - ((q2Mult - q1Mult) * arch.getSingleQubitFidelityCost(swap.first) + - (q1Mult - q2Mult) * arch.getSingleQubitFidelityCost(swap.second)); + node.costFixed += + ((q2Mult - q1Mult) * + architecture->getSingleQubitFidelityCost(swap.first) + + (q1Mult - q2Mult) * + architecture->getSingleQubitFidelityCost(swap.second)); // adding cost of the swap gate itself - costFixed += arch.getSwapFidelityCost(swap.first, swap.second); - // add cost of newly validly mapped gates and - // remove cost of now no longer validly mapped gates - for (const auto& [edge, mult] : twoQubitGateMultiplicity) { - auto [q3, q4] = edge; - if (q3 == q1 || q3 == q2 || q4 == q1 || q4 == q2) { - auto physQ3 = static_cast(locations.at(q3)); - auto physQ4 = static_cast(locations.at(q4)); - if (arch.isEdgeConnected(Edge{physQ3, physQ4}) || - arch.isEdgeConnected(Edge{physQ4, physQ3})) { - // validly mapped now - if (validMappedTwoQubitGates.find(edge) == - validMappedTwoQubitGates.end()) { // not mapped validly before - costFixed += - mult.first * arch.getTwoQubitFidelityCost(physQ3, physQ4) + - mult.second * arch.getTwoQubitFidelityCost(physQ4, physQ3); - validMappedTwoQubitGates.emplace(edge); - } - } else { // not mapped validly now - if (validMappedTwoQubitGates.find(edge) != - validMappedTwoQubitGates.end()) { // mapped validly before - auto prevPhysQ3 = physQ3; - if (prevPhysQ3 == swap.first) { - prevPhysQ3 = swap.second; - } else if (prevPhysQ3 == swap.second) { - prevPhysQ3 = swap.first; - } - auto prevPhysQ4 = physQ4; - if (prevPhysQ4 == swap.first) { - prevPhysQ4 = swap.second; - } else if (prevPhysQ4 == swap.second) { - prevPhysQ4 = swap.first; - } - costFixed -= mult.first * arch.getTwoQubitFidelityCost(prevPhysQ3, - prevPhysQ4) + - mult.second * arch.getTwoQubitFidelityCost(prevPhysQ4, - prevPhysQ3); - validMappedTwoQubitGates.erase(edge); - } - } - } - } + node.costFixed += + architecture->getSwapFidelityCost(swap.first, swap.second); } else { - if (arch.bidirectional()) { - costFixed += COST_BIDIRECTIONAL_SWAP; + // branch clone intended for performance reasons (checking edge-wise for + // bidirectionality is not O(1)) + // NOLINTBEGIN(bugprone-branch-clone) + if (architecture->bidirectional()) { + node.costFixed += COST_BIDIRECTIONAL_SWAP; + } else if (architecture->unidirectional() || + !architecture->isEdgeBidirectional({swap.first, swap.second})) { + node.costFixed += COST_UNIDIRECTIONAL_SWAP; } else { - costFixed += COST_UNIDIRECTIONAL_SWAP; + node.costFixed += COST_BIDIRECTIONAL_SWAP; } + // NOLINTEND(bugprone-branch-clone) + } + + recalculateFixedCostReversals(layer, node); + updateHeuristicCost(layer, node); + if (results.config.lookaheadHeuristic != LookaheadHeuristic::None) { + updateLookaheadPenalty(layer, node); } } -void HeuristicMapper::Node::applyTeleportation(const Edge& swap, - Architecture& arch) { - nswaps++; - swaps.emplace_back(); - const auto q1 = qubits.at(swap.first); - const auto q2 = qubits.at(swap.second); +void HeuristicMapper::applyTeleportation(const Edge& swap, std::size_t layer, + Node& node) { + const auto q1 = node.qubits.at(swap.first); + const auto q2 = node.qubits.at(swap.second); + + updateSharedSwaps(swap, layer, node); - qubits.at(swap.first) = q2; - qubits.at(swap.second) = q1; + node.qubits.at(swap.first) = q2; + node.qubits.at(swap.second) = q1; if (q1 != -1) { - locations.at(static_cast(q1)) = + node.locations.at(static_cast(q1)) = static_cast(swap.second); } if (q2 != -1) { - locations.at(static_cast(q2)) = + node.locations.at(static_cast(q2)) = static_cast(swap.first); } std::uint16_t middleAnc = std::numeric_limits::max(); - for (const auto& qpair : arch.getTeleportationQubits()) { + for (const auto& qpair : architecture->getTeleportationQubits()) { if (swap.first == qpair.first || swap.second == qpair.first) { middleAnc = static_cast(qpair.second); } else if (swap.first == qpair.second || swap.second == qpair.second) { @@ -1001,16 +1069,12 @@ void HeuristicMapper::Node::applyTeleportation(const Edge& swap, } } - if (middleAnc == std::numeric_limits::max()) { - throw QMAPException("Teleportation between seemingly wrong qubits: " + - std::to_string(swap.first) + " <--> " + - std::to_string(swap.second)); - } + // Teleportation between wrong qubits + assert(middleAnc != std::numeric_limits::max()); std::uint16_t source = std::numeric_limits::max(); std::uint16_t target = std::numeric_limits::max(); - if (arch.isEdgeConnected({swap.first, middleAnc}) || - arch.isEdgeConnected({middleAnc, swap.first})) { + if (architecture->isEdgeConnected({swap.first, middleAnc}, false)) { source = swap.first; target = swap.second; } else { @@ -1025,198 +1089,569 @@ void HeuristicMapper::Node::applyTeleportation(const Edge& swap, "ancillary in teleportation."); } - swaps.back().emplace_back(source, target, middleAnc, qc::Teleportation); + node.swaps.emplace_back(source, target, middleAnc, qc::Teleportation); - costFixed += COST_TELEPORTATION; -} + node.costFixed += COST_TELEPORTATION; -void HeuristicMapper::Node::recalculateFixedCost( - const Architecture& arch, - const SingleQubitMultiplicity& singleQubitGateMultiplicity, - const TwoQubitMultiplicity& twoQubitGateMultiplicity) { - costFixed = 0; - if (considerFidelity) { - // adding costs of single qubit gates - for (std::uint16_t i = 0U; i < arch.getNqubits(); ++i) { - if (singleQubitGateMultiplicity.at(i) == 0) { - continue; + // check if swap created or destroyed any valid mappings of qubit pairs + for (const auto& [edge, mult] : twoQubitMultiplicities.at(layer)) { + const auto [q3, q4] = edge; + if (q3 == q1 || q3 == q2 || q4 == q1 || q4 == q2) { + const auto physQ3 = static_cast(node.locations.at(q3)); + const auto physQ4 = static_cast(node.locations.at(q4)); + if (architecture->isEdgeConnected({physQ3, physQ4}, false)) { + // validly mapped now + node.validMappedTwoQubitGates.emplace(edge); + } else { + // not mapped validly now + node.validMappedTwoQubitGates.erase(edge); } - costFixed += singleQubitGateMultiplicity.at(i) * - arch.getSingleQubitFidelityCost( - static_cast(locations.at(i))); } - // adding cost of the swap gates - for (auto& swapNode : swaps) { - for (auto& swap : swapNode) { - if (swap.op == qc::SWAP) { - costFixed += arch.getSwapFidelityCost(swap.first, swap.second); - } else if (swap.op == qc::Teleportation) { - throw QMAPException("Teleportation currently not supported for " - "noise-aware mapping"); - } + } + + recalculateFixedCostReversals(layer, node); + updateHeuristicCost(layer, node); + if (results.config.lookaheadHeuristic != LookaheadHeuristic::None) { + updateLookaheadPenalty(layer, node); + } +} + +void HeuristicMapper::updateSharedSwaps(const Edge& swap, std::size_t layer, + Node& node) { + const auto& consideredQubits = getConsideredQubits(layer); + const auto& twoQubitGateMultiplicity = twoQubitMultiplicities.at(layer); + + const auto q1 = node.qubits.at(swap.first); + const auto q2 = node.qubits.at(swap.second); + if (q1 == -1 || q2 == -1 || + consideredQubits.find(static_cast(q1)) == + consideredQubits.end() || + consideredQubits.find(static_cast(q2)) == + consideredQubits.end()) { + // the given swap can only be a shared swap if both qubits are active in + // the current layer + return; + } + + // TODO: handle single qubit gates for fidelity aware heuristic, if + // `Node::sharedSwaps` is ever used in a fidelity aware heuristic + Edge logEdge1 = {q1, q1}; + Edge logEdge2 = {q2, q2}; + for (const auto& [edge, multiplicity] : twoQubitGateMultiplicity) { + if (edge.first == q1) { + logEdge1.second = edge.second; + } else if (edge.second == q1) { + logEdge1.second = edge.first; + } + if (edge.first == q2) { + logEdge2.second = edge.second; + } else if (edge.second == q2) { + logEdge2.second = edge.first; + } + } + if ( // if both swapped qubits are acted on by a 2q gate + logEdge1.second != q1 && logEdge2.second != q2 && + // if it is not the same 2q gate acting on both qubits + logEdge1.second != q2) { + auto physQ3 = + static_cast(node.locations.at(logEdge1.second)); + auto physQ4 = + static_cast(node.locations.at(logEdge2.second)); + + double logEdge1DistanceBefore = 0.; + double logEdge1DistanceNew = 0.; + double logEdge2DistanceBefore = 0.; + double logEdge2DistanceNew = 0.; + if (fidelityAwareHeur) { + logEdge1DistanceBefore = + std::min(architecture->fidelityDistance(swap.first, physQ3), + architecture->fidelityDistance(physQ3, swap.first)); + logEdge1DistanceNew = + std::min(architecture->fidelityDistance(swap.second, physQ3), + architecture->fidelityDistance(physQ3, swap.second)); + logEdge2DistanceBefore = + std::min(architecture->fidelityDistance(swap.second, physQ4), + architecture->fidelityDistance(physQ4, swap.second)); + logEdge2DistanceNew = + std::min(architecture->fidelityDistance(swap.first, physQ4), + architecture->fidelityDistance(physQ4, swap.first)); + } else { + logEdge1DistanceBefore = + std::min(architecture->distance(swap.first, physQ3, false), + architecture->distance(physQ3, swap.first, false)); + logEdge1DistanceNew = + std::min(architecture->distance(swap.second, physQ3, false), + architecture->distance(physQ3, swap.second, false)); + logEdge2DistanceBefore = + std::min(architecture->distance(swap.second, physQ4, false), + architecture->distance(physQ4, swap.second, false)); + logEdge2DistanceNew = + std::min(architecture->distance(swap.first, physQ4, false), + architecture->distance(physQ4, swap.first, false)); + } + if (logEdge1DistanceNew < logEdge1DistanceBefore && + logEdge2DistanceNew < logEdge2DistanceBefore) { + ++node.sharedSwaps; + } + } +} + +void HeuristicMapper::updateHeuristicCost(std::size_t layer, Node& node) { + // the mapping is valid, only if all qubit pairs are mapped next to each other + node.validMapping = (node.validMappedTwoQubitGates.size() == + twoQubitMultiplicities.at(layer).size()); + + switch (results.config.heuristic) { + case Heuristic::GateCountMaxDistance: + node.costHeur = heuristicGateCountMaxDistance(layer, node); + break; + case Heuristic::GateCountSumDistance: + node.costHeur = heuristicGateCountSumDistance(layer, node); + break; + case Heuristic::GateCountSumDistanceMinusSharedSwaps: + node.costHeur = heuristicGateCountSumDistanceMinusSharedSwaps(layer, node); + break; + case Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps: + node.costHeur = + heuristicGateCountMaxDistanceOrSumDistanceMinusSharedSwaps(layer, node); + break; + case Heuristic::FidelityBestLocation: + node.costHeur = heuristicFidelityBestLocation(layer, node); + break; + default: + throw QMAPException("Unknown heuristic."); + } +} + +double HeuristicMapper::heuristicGateCountMaxDistance(std::size_t layer, + Node& node) { + if (node.validMapping) { + return 0.; + } + double costHeur = 0.; + + for (const auto& [edge, multiplicity] : twoQubitMultiplicities.at(layer)) { + const auto& [q1, q2] = edge; + const auto [forwardMult, reverseMult] = multiplicity; + const auto physQ1 = static_cast(node.locations.at(q1)); + const auto physQ2 = static_cast(node.locations.at(q2)); + + if (!architecture->bidirectional() && + node.validMappedTwoQubitGates.find(edge) != + node.validMappedTwoQubitGates.end()) { + // validly mapped 2-qubit-gates + if (!architecture->isEdgeConnected({physQ1, physQ2})) { + costHeur = + std::max(costHeur, + static_cast(forwardMult * COST_DIRECTION_REVERSE)); + } else if (!architecture->isEdgeConnected({physQ2, physQ1})) { + costHeur = + std::max(costHeur, + static_cast(reverseMult * COST_DIRECTION_REVERSE)); + } + } else { + // not validly mapped 2-qubit-gates + if (forwardMult > 0) { + costHeur = std::max(costHeur, architecture->distance(physQ1, physQ2)); + } + if (reverseMult > 0) { + costHeur = std::max(costHeur, architecture->distance(physQ2, physQ1)); } } - validMappedTwoQubitGates.clear(); - // adding cost of two qubit gates that are already mapped next to each other - for (const auto& edgeMultiplicity : twoQubitGateMultiplicity) { - const auto& q1 = edgeMultiplicity.first.first; - const auto& q2 = edgeMultiplicity.first.second; - const auto& straightMultiplicity = edgeMultiplicity.second.first; - const auto& reverseMultiplicity = edgeMultiplicity.second.second; - - if (arch.isEdgeConnected( - {static_cast(locations.at(q1)), - static_cast(locations.at(q2))}) || - arch.isEdgeConnected({static_cast(locations.at(q2)), - static_cast( - locations.at(q1))})) { // validly mapped - costFixed += (straightMultiplicity * - arch.getTwoQubitFidelityCost( - static_cast(locations.at(q1)), - static_cast(locations.at(q2))) + - reverseMultiplicity * - arch.getTwoQubitFidelityCost( - static_cast(locations.at(q2)), - static_cast(locations.at(q1)))); - validMappedTwoQubitGates.emplace(q1, q2); + } + + return costHeur; +} + +double HeuristicMapper::heuristicGateCountSumDistance(std::size_t layer, + Node& node) { + if (node.validMapping) { + return 0.; + } + double costHeur = 0.; + + for (const auto& [edge, multiplicity] : twoQubitMultiplicities.at(layer)) { + const auto& [q1, q2] = edge; + const auto [forwardMult, reverseMult] = multiplicity; + const auto physQ1 = static_cast(node.locations.at(q1)); + const auto physQ2 = static_cast(node.locations.at(q2)); + + if (!architecture->bidirectional() && + node.validMappedTwoQubitGates.find(edge) != + node.validMappedTwoQubitGates.end()) { + // validly mapped 2-qubit-gates + if (!architecture->isEdgeConnected({physQ1, physQ2})) { + costHeur += forwardMult * COST_DIRECTION_REVERSE; + } else if (!architecture->isEdgeConnected({physQ2, physQ1})) { + costHeur += reverseMult * COST_DIRECTION_REVERSE; } + } else { + // not validly mapped 2-qubit-gates + double swapCost = 0.; + + if (forwardMult == 0) { + // forwardMult == 0 && reverseMult > 0 + swapCost = architecture->distance(physQ2, physQ1); + } else if (reverseMult == 0) { + // forwardMult > 0 && reverseMult == 0 + swapCost = architecture->distance(physQ1, physQ2); + } else { + // forwardMult > 0 && reverseMult > 0 + swapCost = std::max(architecture->distance(physQ1, physQ2), + architecture->distance(physQ2, physQ1)); + } + costHeur += swapCost; } - // 2-qubit-gates not yet mapped next to each other are handled in the - // heuristic - } else { - for (auto& swapNode : swaps) { - for (auto& swap : swapNode) { - if (swap.op == qc::SWAP) { - if (arch.bidirectional()) { - costFixed += COST_BIDIRECTIONAL_SWAP; - } else { - costFixed += COST_UNIDIRECTIONAL_SWAP; - } - } else if (swap.op == qc::Teleportation) { - costFixed += COST_TELEPORTATION; - } + } + + return costHeur; +} + +double HeuristicMapper::heuristicGateCountSumDistanceMinusSharedSwaps( + std::size_t layer, Node& node) { + if (node.validMapping) { + return 0.; + } + const auto& twoQubitGateMultiplicity = twoQubitMultiplicities.at(layer); + double costHeur = 0.; + double costReversals = 0.; + std::vector nSwaps{}; + nSwaps.reserve(twoQubitGateMultiplicity.size()); + + for (const auto& [edge, multiplicity] : twoQubitGateMultiplicity) { + const auto& [q1, q2] = edge; + const auto [forwardMult, reverseMult] = multiplicity; + const auto physQ1 = static_cast(node.locations.at(q1)); + const auto physQ2 = static_cast(node.locations.at(q2)); + + if (architecture->unidirectional()) { + // only for purely unidirectional architectures is it certain that at + // least one of the two directions has to be reversed + costReversals += + std::min(forwardMult, reverseMult) * COST_DIRECTION_REVERSE; + } + + if (node.validMappedTwoQubitGates.find(edge) != + node.validMappedTwoQubitGates.end()) { + // validly mapped 2-qubit-gates + continue; + } + + double swapCost = 0.; + if (forwardMult == 0) { + // forwardMult == 0 && reverseMult > 0 + swapCost = architecture->distance(physQ2, physQ1, false); + } else if (reverseMult == 0) { + // forwardMult > 0 && reverseMult == 0 + swapCost = architecture->distance(physQ1, physQ2, false); + } else { + // forwardMult > 0 && reverseMult > 0 + swapCost = std::min(architecture->distance(physQ1, physQ2, false), + architecture->distance(physQ2, physQ1, false)); + } + costHeur += swapCost; + + // infer maximum number of swaps in this distance + if (architecture->unidirectional()) { + nSwaps.emplace_back( + static_cast(swapCost / COST_UNIDIRECTIONAL_SWAP)); + } else { + nSwaps.emplace_back( + static_cast(swapCost / COST_BIDIRECTIONAL_SWAP)); + } + } + + // sort number of swaps in descending order + std::sort(nSwaps.begin(), nSwaps.end(), std::greater<>()); + + // infer maximum number of shared swaps + std::size_t maxSharedSwaps = 0; + for (std::size_t i = 0; i < nSwaps.size() - 1; ++i) { + std::size_t maxSharedSwapsEdge = + 0; // maximum number of shared swaps for this edge + for (std::size_t j = i + 1; + j < nSwaps.size() && maxSharedSwapsEdge < nSwaps[i]; ++j) { + if (nSwaps[j] > 0) { + ++maxSharedSwapsEdge; + --nSwaps[j]; } } + maxSharedSwaps += maxSharedSwapsEdge; + } + if (node.sharedSwaps < maxSharedSwaps) { + maxSharedSwaps -= node.sharedSwaps; + } else { + maxSharedSwaps = 0; } + + double sharedSwapCostReduction = 0; + if (architecture->bidirectional()) { + sharedSwapCostReduction = + static_cast(maxSharedSwaps * COST_BIDIRECTIONAL_SWAP); + } else { + sharedSwapCostReduction = + static_cast(maxSharedSwaps * COST_UNIDIRECTIONAL_SWAP); + } + + return std::max(0., costHeur - sharedSwapCostReduction) + costReversals; } -void HeuristicMapper::Node::updateHeuristicCost( - const Architecture& arch, - const SingleQubitMultiplicity& singleQubitGateMultiplicity, - const TwoQubitMultiplicity& twoQubitGateMultiplicity, - const std::unordered_set& consideredQubits) { - costHeur = 0.; - done = true; +double +HeuristicMapper::heuristicGateCountMaxDistanceOrSumDistanceMinusSharedSwaps( + std::size_t layer, Node& node) { + return std::max(heuristicGateCountMaxDistance(layer, node), + heuristicGateCountSumDistanceMinusSharedSwaps(layer, node)); +} + +double HeuristicMapper::heuristicFidelityBestLocation(std::size_t layer, + Node& node) { + const auto& consideredQubits = getConsideredQubits(layer); + const auto& singleQubitGateMultiplicity = singleQubitMultiplicities.at(layer); + const auto& twoQubitGateMultiplicity = twoQubitMultiplicities.at(layer); + + double costHeur = 0.; // single qubit gate savings potential by moving them to different physical // qubits with higher fidelity double savingsPotential = 0.; - if (considerFidelity) { - for (std::uint16_t logQbit = 0U; logQbit < arch.getNqubits(); ++logQbit) { - if (singleQubitGateMultiplicity.at(logQbit) == 0) { + for (std::uint16_t logQbit = 0U; logQbit < architecture->getNqubits(); + ++logQbit) { + if (singleQubitGateMultiplicity.at(logQbit) == 0) { + continue; + } + double qbitSavings = 0; + const double currFidelity = architecture->getSingleQubitFidelityCost( + static_cast(node.locations.at(logQbit))); + for (std::uint16_t physQbit = 0U; physQbit < architecture->getNqubits(); + ++physQbit) { + if (architecture->getSingleQubitFidelityCost(physQbit) >= currFidelity) { continue; } - double qbitSavings = 0; - const double currFidelity = arch.getSingleQubitFidelityCost( - static_cast(locations.at(logQbit))); - for (std::uint16_t physQbit = 0U; physQbit < arch.getNqubits(); - ++physQbit) { - if (arch.getSingleQubitFidelityCost(physQbit) >= currFidelity) { - continue; - } - const double curSavings = - singleQubitGateMultiplicity.at(logQbit) * - (currFidelity - arch.getSingleQubitFidelityCost(physQbit)) - - arch.fidelityDistance( - static_cast(locations.at(logQbit)), physQbit, - consideredQubits.size()); - qbitSavings = std::max(qbitSavings, curSavings); - } - savingsPotential += qbitSavings; + const double curSavings = + singleQubitGateMultiplicity.at(logQbit) * + (currFidelity - + architecture->getSingleQubitFidelityCost(physQbit)) - + architecture->fidelityDistance( + static_cast(node.locations.at(logQbit)), physQbit, + consideredQubits.size() - 1); + qbitSavings = std::max(qbitSavings, curSavings); } + savingsPotential += qbitSavings; } // iterating over all virtual qubit pairs, that share a gate on the // current layer - for (const auto& [edge, multiplicity] : twoQubitGateMultiplicity) { - const auto& [q1, q2] = edge; - - const auto& [straightMultiplicity, reverseMultiplicity] = multiplicity; - - const bool edgeDone = - (arch.isEdgeConnected({static_cast(locations.at(q1)), - static_cast(locations.at(q2))}) || - arch.isEdgeConnected({static_cast(locations.at(q2)), - static_cast(locations.at(q1))})); - // only if all qubit pairs are mapped next to each other the mapping - // is complete - if (!edgeDone) { - done = false; + for (const auto& [edge, mult] : twoQubitGateMultiplicity) { + const auto [q1, q2] = edge; + const auto [forwardMult, reverseMult] = mult; + + const bool edgeDone = (node.validMappedTwoQubitGates.find(edge) != + node.validMappedTwoQubitGates.end() || + node.validMappedTwoQubitGates.find({q1, q2}) != + node.validMappedTwoQubitGates.end()); + + // find the optimal edge, to which to remap the given virtual qubit + // pair and take the cost of moving it there via swaps plus the + // fidelity cost of executing all their shared gates on that edge + // as the qubit pairs cost + double swapCost = std::numeric_limits::max(); + for (const auto& [q3, q4] : architecture->getCouplingMap()) { + swapCost = std::min( + swapCost, + forwardMult * architecture->getTwoQubitFidelityCost(q3, q4) + + reverseMult * architecture->getTwoQubitFidelityCost(q4, q3) + + architecture->fidelityDistance( + static_cast(node.locations.at(q1)), q3, + consideredQubits.size() - 1) + + architecture->fidelityDistance( + static_cast(node.locations.at(q2)), q4, + consideredQubits.size() - 1)); + swapCost = std::min( + swapCost, + forwardMult * architecture->getTwoQubitFidelityCost(q4, q3) + + reverseMult * architecture->getTwoQubitFidelityCost(q3, q4) + + architecture->fidelityDistance( + static_cast(node.locations.at(q2)), q3, + consideredQubits.size() - 1) + + architecture->fidelityDistance( + static_cast(node.locations.at(q1)), q4, + consideredQubits.size() - 1)); } - if (considerFidelity) { - // find the optimal edge, to which to remap the given virtual qubit - // pair and take the cost of moving it there via swaps plus the - // fidelity cost of executing all their shared gates on that edge - // as the qubit pairs cost - double swapCost = std::numeric_limits::max(); - for (const auto& [q3, q4] : arch.getCouplingMap()) { - swapCost = std::min( - swapCost, - straightMultiplicity * arch.getTwoQubitFidelityCost(q3, q4) + - reverseMultiplicity * arch.getTwoQubitFidelityCost(q4, q3) + - arch.fidelityDistance( - static_cast(locations.at(q1)), q3, - consideredQubits.size()) + - arch.fidelityDistance( - static_cast(locations.at(q2)), q4, - consideredQubits.size())); - swapCost = std::min( - swapCost, - straightMultiplicity * arch.getTwoQubitFidelityCost(q4, q3) + - reverseMultiplicity * arch.getTwoQubitFidelityCost(q3, q4) + - arch.fidelityDistance( - static_cast(locations.at(q2)), q3, - consideredQubits.size()) + - arch.fidelityDistance( - static_cast(locations.at(q1)), q4, - consideredQubits.size())); - } + if (edgeDone) { + const double currEdgeCost = + (forwardMult * + architecture->getTwoQubitFidelityCost( + static_cast(node.locations.at(q1)), + static_cast(node.locations.at(q2))) + + reverseMult * + architecture->getTwoQubitFidelityCost( + static_cast(node.locations.at(q2)), + static_cast(node.locations.at(q1)))); + savingsPotential += (currEdgeCost - swapCost); + } else { + costHeur += swapCost; + } + } - if (edgeDone) { - const double currEdgeCost = - (straightMultiplicity * - arch.getTwoQubitFidelityCost( - static_cast(locations.at(q1)), - static_cast(locations.at(q2))) + - reverseMultiplicity * - arch.getTwoQubitFidelityCost( - static_cast(locations.at(q2)), - static_cast(locations.at(q1)))); - savingsPotential += (currEdgeCost - swapCost); - } else { - costHeur += swapCost; + return costHeur - savingsPotential; +} + +void HeuristicMapper::updateLookaheadPenalty(const std::size_t layer, + HeuristicMapper::Node& node) { + const auto& config = results.config; + node.lookaheadPenalty = 0.; + auto nextLayer = getNextLayer(layer); + double factor = config.firstLookaheadFactor; + + for (std::size_t i = 0; i < config.nrLookaheads; ++i) { + if (nextLayer == std::numeric_limits::max()) { + break; + } + + double penalty = 0.; + switch (config.lookaheadHeuristic) { + case LookaheadHeuristic::GateCountMaxDistance: + penalty = lookaheadGateCountMaxDistance(nextLayer, node); + break; + case LookaheadHeuristic::GateCountSumDistance: + penalty = lookaheadGateCountSumDistance(nextLayer, node); + break; + default: + break; + } + + node.lookaheadPenalty += factor * penalty; + factor *= config.lookaheadFactor; + nextLayer = getNextLayer(nextLayer); // TODO: consider single qubits here + // for better fidelity lookahead + } +} + +double +HeuristicMapper::lookaheadGateCountMaxDistance(const std::size_t layer, + HeuristicMapper::Node& node) { + double penalty = 0.; + + for (const auto& [edge, multiplicity] : twoQubitMultiplicities.at(layer)) { + const auto& [q1, q2] = edge; + const auto [forwardMult, reverseMult] = multiplicity; + + const auto loc1 = node.locations.at(q1); + const auto loc2 = node.locations.at(q2); + if (loc1 == DEFAULT_POSITION && loc2 == DEFAULT_POSITION) { + // no penalty + } else if (loc1 == DEFAULT_POSITION) { + auto min = std::numeric_limits::max(); + for (std::uint16_t j = 0; j < architecture->getNqubits(); ++j) { + if (node.qubits.at(j) == DEFAULT_POSITION) { + // TODO: Consider fidelity here if available + if (forwardMult > 0) { + min = std::min(min, architecture->distance( + j, static_cast(loc2))); + } + if (reverseMult > 0) { + min = std::min(min, architecture->distance( + static_cast(loc2), j)); + } + } } + penalty = std::max(penalty, min); + } else if (loc2 == DEFAULT_POSITION) { + auto min = std::numeric_limits::max(); + for (std::uint16_t j = 0; j < architecture->getNqubits(); ++j) { + if (node.qubits.at(j) == DEFAULT_POSITION) { + // TODO: Consider fidelity here if available + if (forwardMult > 0) { + min = std::min(min, architecture->distance( + static_cast(loc1), j)); + } + if (reverseMult > 0) { + min = std::min(min, architecture->distance( + j, static_cast(loc1))); + } + } + } + penalty = std::max(penalty, min); } else { - const double swapCostStraight = - arch.distance(static_cast(locations.at(q1)), - static_cast(locations.at(q2))); - const double swapCostReverse = - arch.distance(static_cast(locations.at(q2)), - static_cast(locations.at(q1))); - - if (admissibleHeuristic) { - if (straightMultiplicity > 0) { - costHeur = std::max(costHeur, swapCostStraight); + double cost = std::numeric_limits::max(); + if (forwardMult > 0) { + cost = std::min( + cost, architecture->distance(static_cast(loc1), + static_cast(loc2))); + } + if (reverseMult > 0) { + cost = std::min( + cost, architecture->distance(static_cast(loc2), + static_cast(loc1))); + } + penalty = std::max(penalty, cost); + } + } + + return penalty; +} + +double +HeuristicMapper::lookaheadGateCountSumDistance(const std::size_t layer, + HeuristicMapper::Node& node) { + double penalty = 0.; + + for (const auto& [edge, multiplicity] : twoQubitMultiplicities.at(layer)) { + const auto& [q1, q2] = edge; + const auto [forwardMult, reverseMult] = multiplicity; + + const auto loc1 = node.locations.at(q1); + const auto loc2 = node.locations.at(q2); + if (loc1 == DEFAULT_POSITION && loc2 == DEFAULT_POSITION) { + // no penalty + } else if (loc1 == DEFAULT_POSITION) { + auto min = std::numeric_limits::max(); + for (std::uint16_t j = 0; j < architecture->getNqubits(); ++j) { + if (node.qubits.at(j) == DEFAULT_POSITION) { + // TODO: Consider fidelity here if available + if (forwardMult > 0) { + min = std::min(min, architecture->distance( + j, static_cast(loc2))); + } + if (reverseMult > 0) { + min = std::min(min, architecture->distance( + static_cast(loc2), j)); + } } - if (reverseMultiplicity > 0) { - costHeur = std::max(costHeur, swapCostReverse); + } + penalty += min; + } else if (loc2 == DEFAULT_POSITION) { + auto min = std::numeric_limits::max(); + for (std::uint16_t j = 0; j < architecture->getNqubits(); ++j) { + if (node.qubits.at(j) == DEFAULT_POSITION) { + // TODO: Consider fidelity here if available + if (forwardMult > 0) { + min = std::min(min, architecture->distance( + static_cast(loc1), j)); + } + if (reverseMult > 0) { + min = std::min(min, architecture->distance( + j, static_cast(loc1))); + } } - } else { - costHeur += swapCostStraight * straightMultiplicity + - swapCostReverse * reverseMultiplicity; } + penalty += min; + } else { + double cost = std::numeric_limits::max(); + if (forwardMult > 0) { + cost = std::min( + cost, architecture->distance(static_cast(loc1), + static_cast(loc2))); + } + if (reverseMult > 0) { + cost = std::min( + cost, architecture->distance(static_cast(loc2), + static_cast(loc1))); + } + penalty += cost; } } - costHeur -= savingsPotential; + + return penalty; } diff --git a/src/mqt/qmap/__init__.py b/src/mqt/qmap/__init__.py index 7d6e6d6a7..92996363d 100644 --- a/src/mqt/qmap/__init__.py +++ b/src/mqt/qmap/__init__.py @@ -29,8 +29,10 @@ CommanderGrouping, Configuration, Encoding, + Heuristic, InitialLayout, Layering, + LookaheadHeuristic, MappingResults, Method, QuantumComputation, @@ -56,6 +58,8 @@ "Configuration", "MappingResults", "Architecture", + "Heuristic", + "LookaheadHeuristic", "SubarchitectureOrder", "SynthesisConfiguration", "SynthesisResults", diff --git a/src/mqt/qmap/compile.py b/src/mqt/qmap/compile.py index c4d02d96e..128c3c35d 100644 --- a/src/mqt/qmap/compile.py +++ b/src/mqt/qmap/compile.py @@ -23,8 +23,10 @@ Configuration, EarlyTermination, Encoding, + Heuristic, InitialLayout, Layering, + LookaheadHeuristic, MappingResults, Method, SwapReduction, @@ -61,14 +63,15 @@ def compile( # noqa: A001 arch: str | Arch | Architecture | Backend | None, calibration: str | BackendProperties | Target | None = None, method: str | Method = "heuristic", - consider_fidelity: bool = False, + heuristic: str | Heuristic = "gate_count_max_distance", initial_layout: str | InitialLayout = "dynamic", iterative_bidirectional_routing_passes: int | None = None, layering: str | Layering = "individual_gates", automatic_layer_splits_node_limit: int | None = 5000, early_termination: str | EarlyTermination = "none", early_termination_limit: int = 0, - lookaheads: int | None = 15, + lookahead_heuristic: str | LookaheadHeuristic | None = "gate_count_max_distance", + lookaheads: int = 15, lookahead_factor: float = 0.5, use_teleportation: bool = False, teleportation_fake: bool = False, @@ -96,13 +99,14 @@ def compile( # noqa: A001 arch: The architecture to map to. calibration: The calibration to use. method: The mapping method to use. Either "heuristic" or "exact". Defaults to "heuristic". - consider_fidelity: Whether to consider the fidelity of the gates. Defaults to False. + heuristic: The heuristic function to use for the routing search. Defaults to "gate_count_max_distance". initial_layout: The initial layout to use. Defaults to "dynamic". iterative_bidirectional_routing_passes: Number of iterative bidirectional routing passes to perform or None to disable. Defaults to None. layering: The layering strategy to use. Defaults to "individual_gates". automatic_layer_splits_node_limit: The number of expanded nodes after which to split a layer or None to disable automatic layer splitting. Defaults to 5000. early_termination: The early termination strategy to use, i.e. terminating the search after a goal node has been found, but before it is guarantueed to be optimal. Defaults to "none". early_termination_limit: The number of nodes (counted according to the early termination strategy) after which to terminate the search early. Defaults to 0. + lookahead_heuristic: The heuristic function to use as a lookahead penalty during search or None to disable lookahead. Defaults to "gate_count_max_distance". lookaheads: The number of lookaheads to be used or None if no lookahead should be used. Defaults to 15. lookahead_factor: The rate at which the contribution of future layers to the lookahead decreases. Defaults to 0.5. encoding: The encoding to use for the AMO and exactly one constraints. Defaults to "naive". @@ -139,7 +143,7 @@ def compile( # noqa: A001 config = Configuration() config.method = Method(method) - config.consider_fidelity = consider_fidelity + config.heuristic = Heuristic(heuristic) config.initial_layout = InitialLayout(initial_layout) if iterative_bidirectional_routing_passes is None: config.iterative_bidirectional_routing = False @@ -173,12 +177,12 @@ def compile( # noqa: A001 config.debug = debug if visualizer is not None and visualizer.data_logging_path is not None: config.data_logging_path = visualizer.data_logging_path - if lookaheads is None: + if lookahead_heuristic is None: + config.lookahead_heuristic = LookaheadHeuristic.none config.lookaheads = 0 - config.lookahead = False else: + config.lookahead_heuristic = LookaheadHeuristic(lookahead_heuristic) config.lookaheads = lookaheads - config.lookahead = True config.lookahead_factor = lookahead_factor results = map(circ, architecture, config) diff --git a/src/mqt/qmap/pyqmap.pyi b/src/mqt/qmap/pyqmap.pyi index 6ce56e9d9..15b2822d3 100644 --- a/src/mqt/qmap/pyqmap.pyi +++ b/src/mqt/qmap/pyqmap.pyi @@ -111,8 +111,7 @@ class CommanderGrouping: class Configuration: add_measurements_to_mapped_circuit: bool add_barriers_between_layers: bool - admissible_heuristic: bool - consider_fidelity: bool + heuristic: Heuristic commander_grouping: CommanderGrouping enable_limits: bool encoding: Encoding @@ -126,7 +125,7 @@ class Configuration: automatic_layer_splits_node_limit: int early_termination: EarlyTermination early_termination_limit: int - lookahead: bool + lookahead_heuristic: LookaheadHeuristic lookahead_factor: float lookaheads: int method: Method @@ -220,6 +219,54 @@ class InitialLayout: @property def value(self) -> int: ... +class Heuristic: + __members__: ClassVar[dict[Heuristic, int]] = ... # read-only + gate_count_max_distance: ClassVar[Heuristic] = ... + gate_count_sum_distance: ClassVar[Heuristic] = ... + gate_count_sum_distance_minus_shared_swaps: ClassVar[Heuristic] = ... + gate_count_max_distance_or_sum_distance_minus_shared_swaps: ClassVar[Heuristic] = ... + fidelity_best_location: ClassVar[Heuristic] = ... + @overload + def __init__(self, value: int) -> None: ... + @overload + def __init__(self, arg0: str) -> None: ... + @overload + def __init__(self, arg0: Heuristic) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __setstate__(self, state: int) -> None: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class LookaheadHeuristic: + __members__: ClassVar[dict[LookaheadHeuristic, int]] = ... # read-only + none: ClassVar[LookaheadHeuristic] = ... + gate_count_max_distance: ClassVar[LookaheadHeuristic] = ... + gate_count_sum_distance: ClassVar[LookaheadHeuristic] = ... + @overload + def __init__(self, value: int) -> None: ... + @overload + def __init__(self, arg0: str) -> None: ... + @overload + def __init__(self, arg0: LookaheadHeuristic) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + def __setstate__(self, state: int) -> None: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + class Layering: __members__: ClassVar[dict[Layering, int]] = ... # read-only disjoint_qubits: ClassVar[Layering] = ... diff --git a/src/python/bindings.cpp b/src/python/bindings.cpp index c76de2eec..38832e9b8 100644 --- a/src/python/bindings.cpp +++ b/src/python/bindings.cpp @@ -123,6 +123,34 @@ PYBIND11_MODULE(pyqmap, m) { return initialLayoutFromString(str); })); + // Heuristic function + py::enum_(m, "Heuristic") + .value("gate_count_max_distance", Heuristic::GateCountMaxDistance) + .value("gate_count_sum_distance", Heuristic::GateCountSumDistance) + .value("gate_count_sum_distance_minus_shared_swaps", + Heuristic::GateCountSumDistanceMinusSharedSwaps) + .value("gate_count_max_distance_or_sum_distance_minus_shared_swaps", + Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps) + .value("fidelity_best_location", Heuristic::FidelityBestLocation) + .export_values() + // allow construction from string + .def(py::init([](const std::string& str) -> Heuristic { + return heuristicFromString(str); + })); + + // Lookahead heuristic function + py::enum_(m, "LookaheadHeuristic") + .value("none", LookaheadHeuristic::None) + .value("gate_count_max_distance", + LookaheadHeuristic::GateCountMaxDistance) + .value("gate_count_sum_distance", + LookaheadHeuristic::GateCountSumDistance) + .export_values() + // allow construction from string + .def(py::init([](const std::string& str) -> LookaheadHeuristic { + return lookaheadHeuristicFromString(str); + })); + // Gate clustering / layering strategy py::enum_(m, "Layering") .value("individual_gates", Layering::IndividualGates) @@ -195,6 +223,7 @@ PYBIND11_MODULE(pyqmap, m) { "Configuration options for the MQT QMAP quantum circuit mapping tool") .def(py::init<>()) .def_readwrite("method", &Configuration::method) + .def_readwrite("heuristic", &Configuration::heuristic) .def_readwrite("verbose", &Configuration::verbose) .def_readwrite("debug", &Configuration::debug) .def_readwrite("data_logging_path", &Configuration::dataLoggingPath) @@ -211,10 +240,7 @@ PYBIND11_MODULE(pyqmap, m) { &Configuration::iterativeBidirectionalRouting) .def_readwrite("iterative_bidirectional_routing_passes", &Configuration::iterativeBidirectionalRoutingPasses) - .def_readwrite("lookahead", &Configuration::lookahead) - .def_readwrite("admissible_heuristic", - &Configuration::admissibleHeuristic) - .def_readwrite("consider_fidelity", &Configuration::considerFidelity) + .def_readwrite("lookahead_heuristic", &Configuration::lookaheadHeuristic) .def_readwrite("lookaheads", &Configuration::nrLookaheads) .def_readwrite("first_lookahead_factor", &Configuration::firstLookaheadFactor) diff --git a/src/utils.cpp b/src/utils.cpp index 3e4b08133..55e664031 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -7,40 +7,31 @@ #include -void Dijkstra::buildTable(const std::uint16_t n, const CouplingMap& couplingMap, - Matrix& distanceTable, const Matrix& edgeWeights, - const double reversalCost, - const bool removeLastEdge) { +void Dijkstra::buildTable(const CouplingMap& couplingMap, Matrix& distanceTable, + const Matrix& edgeWeights) { + // number of qubits + const auto n = static_cast(edgeWeights.size()); + distanceTable.clear(); distanceTable.resize(n, std::vector(n, -1.)); for (std::uint16_t i = 0; i < n; ++i) { std::vector nodes(n); for (std::uint16_t j = 0; j < n; ++j) { - nodes.at(j).containsCorrectEdge = false; - nodes.at(j).visited = false; - nodes.at(j).pos = j; - nodes.at(j).cost = -1.; - nodes.at(j).prevCost = -1.; + nodes.at(j).visited = false; + nodes.at(j).pos = j; + nodes.at(j).cost = -1.; } - // initially all paths assume that a CNOT reversal will be necessary, - // as soon as a forward edge is encountered along the path, the cost - // for the reversal is removed - nodes.at(i).cost = reversalCost; - nodes.at(i).prevCost = reversalCost; + nodes.at(i).cost = 0; - dijkstra(couplingMap, nodes, i, edgeWeights, reversalCost); + dijkstra(couplingMap, nodes, i, edgeWeights); for (std::uint16_t j = 0; j < n; ++j) { if (i == j) { distanceTable.at(i).at(j) = 0; } else { - if (removeLastEdge) { - distanceTable.at(i).at(j) = nodes.at(j).prevCost; - } else { - distanceTable.at(i).at(j) = nodes.at(j).cost; - } + distanceTable.at(i).at(j) = nodes.at(j).cost; } } } @@ -48,7 +39,7 @@ void Dijkstra::buildTable(const std::uint16_t n, const CouplingMap& couplingMap, void Dijkstra::dijkstra(const CouplingMap& couplingMap, std::vector& nodes, const std::uint16_t start, - const Matrix& edgeWeights, const double reversalCost) { + const Matrix& edgeWeights) { std::priority_queue, NodeComparator> queue{}; queue.push(&nodes.at(start)); while (!queue.empty()) { @@ -59,14 +50,8 @@ void Dijkstra::dijkstra(const CouplingMap& couplingMap, for (const auto& edge : couplingMap) { std::optional to = std::nullopt; - // if the path up to here already contains a forward edge, we do not care - // about the directionality of other edges anymore; the value of the last - // node is therefore kept and only overwritten with true if the current - // edge is a forward edge (but never with false) - bool correctEdge = current->containsCorrectEdge; if (pos == edge.first) { // forward edge - to = edge.second; - correctEdge = true; + to = edge.second; } else if (pos == edge.second) { // back edge to = edge.first; } @@ -76,16 +61,8 @@ void Dijkstra::dijkstra(const CouplingMap& couplingMap, } Node newNode; - newNode.cost = current->cost + edgeWeights.at(*pos).at(*to); - newNode.prevCost = current->cost; - newNode.pos = to; - newNode.containsCorrectEdge = correctEdge; - if (newNode.containsCorrectEdge && !current->containsCorrectEdge) { - // when encountering the first forward edge along the path, the - // reversal costs need to be removed - newNode.cost -= reversalCost; - newNode.prevCost -= reversalCost; - } + newNode.cost = current->cost + edgeWeights.at(*pos).at(*to); + newNode.pos = to; if (nodes.at(*to).cost < 0 || newNode < nodes.at(*to)) { nodes.at(*to) = newNode; queue.push(&nodes.at(*to)); @@ -95,9 +72,9 @@ void Dijkstra::dijkstra(const CouplingMap& couplingMap, } } -void Dijkstra::buildEdgeSkipTable(const Matrix& distanceTable, - const CouplingMap& couplingMap, - std::vector& edgeSkipDistanceTable) { +void Dijkstra::buildEdgeSkipTable(const CouplingMap& couplingMap, + std::vector& distanceTables, + const Matrix& edgeWeights) { /* to find the cheapest distance between 2 qubits skipping any 1 edge, we iterate over all edges, for each assume the current edge to be the one skipped and are thereby able to retrieve the distance by just adding the distances @@ -108,14 +85,15 @@ void Dijkstra::buildEdgeSkipTable(const Matrix& distanceTable, edge taking not the regular distance but the previously calculated distance skipping 1 edge. The same approach can be used for skipping any 3 edges, etc. */ - edgeSkipDistanceTable.clear(); - edgeSkipDistanceTable.emplace_back(distanceTable); - const std::size_t n = distanceTable.size(); + distanceTables.clear(); + distanceTables.emplace_back(); + buildTable(couplingMap, distanceTables.back(), edgeWeights); + const std::size_t n = edgeWeights.size(); for (std::size_t k = 1; k <= n; ++k) { // k...number of edges to be skipped along each path - edgeSkipDistanceTable.emplace_back( + distanceTables.emplace_back( n, std::vector(n, std::numeric_limits::max())); - Matrix* currentTable = &edgeSkipDistanceTable.back(); + Matrix* currentTable = &distanceTables.back(); for (std::size_t q = 0; q < n; ++q) { currentTable->at(q).at(q) = 0.; } @@ -128,12 +106,12 @@ void Dijkstra::buildEdgeSkipTable(const Matrix& distanceTable, for (std::size_t q2 = q1 + 1; q2 < n; ++q2) { // q2 ... target qubit currentTable->at(q1).at(q2) = std::min(currentTable->at(q1).at(q2), - edgeSkipDistanceTable.at(l).at(q1).at(e1) + - edgeSkipDistanceTable.at(k - l - 1).at(e2).at(q2)); + distanceTables.at(l).at(q1).at(e1) + + distanceTables.at(k - l - 1).at(e2).at(q2)); currentTable->at(q1).at(q2) = std::min(currentTable->at(q1).at(q2), - edgeSkipDistanceTable.at(l).at(q1).at(e2) + - edgeSkipDistanceTable.at(k - l - 1).at(e1).at(q2)); + distanceTables.at(l).at(q1).at(e2) + + distanceTables.at(k - l - 1).at(e1).at(q2)); currentTable->at(q2).at(q1) = currentTable->at(q1).at(q2); if (done && currentTable->at(q2).at(q1) > 0) { done = false; @@ -144,12 +122,50 @@ void Dijkstra::buildEdgeSkipTable(const Matrix& distanceTable, } if (done) { // all distances of the last matrix where 0 - edgeSkipDistanceTable.pop_back(); + distanceTables.pop_back(); break; } } } +void Dijkstra::buildSingleEdgeSkipTable(const Matrix& distanceTable, + const CouplingMap& couplingMap, + const double reversalCost, + Matrix& edgeSkipDistanceTable) { + const std::size_t n = distanceTable.size(); + edgeSkipDistanceTable.clear(); + edgeSkipDistanceTable.resize( + n, std::vector(n, std::numeric_limits::max())); + for (std::size_t q = 0; q < n; ++q) { + edgeSkipDistanceTable.at(q).at(q) = 0.; + } + for (const auto& [e1, e2] : couplingMap) { // edge to be skipped + for (std::size_t q1 = 0; q1 < n; ++q1) { // q1 ... source qubit + for (std::size_t q2 = q1 + 1; q2 < n; ++q2) { // q2 ... target qubit + edgeSkipDistanceTable.at(q1).at(q2) = + std::min(edgeSkipDistanceTable.at(q1).at(q2), + distanceTable.at(q1).at(e1) + distanceTable.at(e2).at(q2)); + edgeSkipDistanceTable.at(q1).at(q2) = + std::min(edgeSkipDistanceTable.at(q1).at(q2), + distanceTable.at(q1).at(e2) + distanceTable.at(e1).at(q2) + + reversalCost); + if (reversalCost == 0.) { + edgeSkipDistanceTable.at(q2).at(q1) = + edgeSkipDistanceTable.at(q1).at(q2); + } else { + edgeSkipDistanceTable.at(q2).at(q1) = std::min( + edgeSkipDistanceTable.at(q2).at(q1), + distanceTable.at(q2).at(e1) + distanceTable.at(e2).at(q1)); + edgeSkipDistanceTable.at(q2).at(q1) = + std::min(edgeSkipDistanceTable.at(q2).at(q1), + distanceTable.at(q2).at(e2) + + distanceTable.at(e1).at(q1) + reversalCost); + } + } + } + } +} + /// Create a string representation of a given permutation /// \param pi permutation /// \return string representation of pi diff --git a/test/python/test_compile.py b/test/python/test_compile.py index 513e49875..fc52b13a0 100644 --- a/test/python/test_compile.py +++ b/test/python/test_compile.py @@ -154,11 +154,12 @@ def test_parameters(example_circuit: QuantumCircuit) -> None: example_circuit, arch=arch, method="heuristic", - consider_fidelity=False, + heuristic="gate_count_max_distance", initial_layout="dynamic", iterative_bidirectional_routing_passes=1, layering="individual_gates", automatic_layer_splits_node_limit=5000, + lookahead_heuristic="gate_count_max_distance", lookaheads=15, lookahead_factor=0.5, use_teleportation=True, @@ -171,7 +172,8 @@ def test_parameters(example_circuit: QuantumCircuit) -> None: visualizer=visualizer, ) assert results.configuration.method == qmap.Method.heuristic - assert results.configuration.consider_fidelity is False + assert results.configuration.heuristic == qmap.Heuristic.gate_count_max_distance + assert results.configuration.lookahead_heuristic == qmap.LookaheadHeuristic.gate_count_max_distance assert results.configuration.initial_layout == qmap.InitialLayout.dynamic assert results.configuration.iterative_bidirectional_routing is True assert results.configuration.iterative_bidirectional_routing_passes == 1 @@ -179,7 +181,6 @@ def test_parameters(example_circuit: QuantumCircuit) -> None: assert results.configuration.automatic_layer_splits is True assert results.configuration.automatic_layer_splits_node_limit == 5000 assert results.configuration.lookaheads == 15 - assert results.configuration.lookahead is True assert results.configuration.lookahead_factor == 0.5 assert results.configuration.use_teleportation is True assert results.configuration.teleportation_fake is False @@ -194,12 +195,12 @@ def test_parameters(example_circuit: QuantumCircuit) -> None: example_circuit, arch=arch, method="heuristic", - consider_fidelity=True, + heuristic="fidelity_best_location", initial_layout="identity", iterative_bidirectional_routing_passes=None, layering="disjoint_qubits", automatic_layer_splits_node_limit=None, - lookaheads=None, + lookahead_heuristic=None, use_teleportation=False, pre_mapping_optimizations=False, post_mapping_optimizations=False, @@ -207,13 +208,13 @@ def test_parameters(example_circuit: QuantumCircuit) -> None: debug=False, ) assert results.configuration.method == qmap.Method.heuristic - assert results.configuration.consider_fidelity is True + assert results.configuration.heuristic == qmap.Heuristic.fidelity_best_location + assert results.configuration.lookahead_heuristic == qmap.LookaheadHeuristic.none assert results.configuration.initial_layout == qmap.InitialLayout.identity assert results.configuration.iterative_bidirectional_routing is False assert results.configuration.layering == qmap.Layering.disjoint_qubits assert results.configuration.automatic_layer_splits is False assert results.configuration.lookaheads == 0 - assert results.configuration.lookahead is False assert results.configuration.use_teleportation is False assert results.configuration.pre_mapping_optimizations is False assert results.configuration.post_mapping_optimizations is False diff --git a/test/test_general.cpp b/test/test_general.cpp index fb82c65fd..2440f273f 100644 --- a/test/test_general.cpp +++ b/test/test_general.cpp @@ -72,14 +72,8 @@ TEST(General, Dijkstra) { const Matrix targetTable1 = { {0, 1, 3, 6}, {1, 0, 2, 5}, {10, 9, 0, 3}, {7, 6, 3, 0}}; Matrix distanceTable{}; - Dijkstra::buildTable(4, cm, distanceTable, edgeWeights, 0, false); + Dijkstra::buildTable(cm, distanceTable, edgeWeights); EXPECT_EQ(distanceTable, targetTable1); - - const Matrix targetTable2 = { - {0, 0, 1, 3}, {0, 0, 0, 2}, {9, 3, 0, 0}, {6, 0, 0, 0}}; - distanceTable = {}; - Dijkstra::buildTable(4, cm, distanceTable, edgeWeights, 0, true); - EXPECT_EQ(distanceTable, targetTable2); } TEST(General, DijkstraCNOTReversal) { @@ -94,24 +88,18 @@ TEST(General, DijkstraCNOTReversal) { {0, 3, 0, 3, 0}, {0, 0, 3, 0, 3}, {0, 0, 0, 3, 0}}; - - const Matrix targetTable1 = {{0, 3, 6, 9, 12}, - {4, 0, 4, 6, 9}, - {6, 3, 0, 3, 6}, - {9, 6, 4, 0, 3}, - {12, 9, 7, 4, 0}}; - Matrix distanceTable{}; - Dijkstra::buildTable(5, cm, distanceTable, edgeWeights, 1, false); - EXPECT_EQ(distanceTable, targetTable1); + Matrix simpleDistanceTable{}; + Dijkstra::buildTable(cm, simpleDistanceTable, edgeWeights); + Matrix fullDistanceTable{}; + Dijkstra::buildSingleEdgeSkipTable(simpleDistanceTable, cm, 1., + fullDistanceTable); const Matrix targetTable2 = {{0, 0, 3, 6, 9}, {1, 0, 1, 3, 6}, {3, 0, 0, 0, 3}, {6, 3, 1, 0, 0}, {9, 6, 4, 1, 0}}; - distanceTable = {}; - Dijkstra::buildTable(5, cm, distanceTable, edgeWeights, 1, true); - EXPECT_EQ(distanceTable, targetTable2); + EXPECT_EQ(fullDistanceTable, targetTable2); } TEST(General, DijkstraSkipEdges) { @@ -147,7 +135,7 @@ TEST(General, DijkstraSkipEdges) { {6, 8, 7, 6, 4, 2, 0, 1, 2, 3}, {7, 9, 8, 7, 5, 3, 1, 0, 1, 2}, {8, 10, 9, 8, 6, 4, 2, 1, 0, 1}, {9, 11, 10, 9, 7, 5, 3, 2, 1, 0}}; Matrix distanceTable{}; - Dijkstra::buildTable(10, cm, distanceTable, edgeWeights, 0, false); + Dijkstra::buildTable(cm, distanceTable, edgeWeights); EXPECT_EQ(distanceTable, targetTable); const std::vector edgeSkipTargetTable = { @@ -193,6 +181,6 @@ TEST(General, DijkstraSkipEdges) { {0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}; std::vector edgeSkipDistanceTable = {}; - Dijkstra::buildEdgeSkipTable(distanceTable, cm, edgeSkipDistanceTable); + Dijkstra::buildEdgeSkipTable(cm, edgeSkipDistanceTable, edgeWeights); EXPECT_EQ(edgeSkipDistanceTable, edgeSkipTargetTable); } diff --git a/test/test_heuristic.cpp b/test/test_heuristic.cpp index 363afd44a..2f64c10d0 100644 --- a/test/test_heuristic.cpp +++ b/test/test_heuristic.cpp @@ -12,64 +12,935 @@ #include #include #include +#include + +constexpr qc::OpType SWAP = qc::OpType::SWAP; +constexpr double FLOAT_TOLERANCE = 1e-6; + +/** + * @brief Get id of the final node in a given layer from a data log. + */ +std::size_t getFinalNodeFromDatalog(std::string dataLoggingPath, + std::size_t layer) { + if (dataLoggingPath.back() != '/') { + dataLoggingPath += '/'; + } + auto layerFile = std::ifstream(dataLoggingPath + "/layer_" + + std::to_string(layer) + ".json"); + if (!layerFile.is_open()) { + throw std::runtime_error("Could not open file " + dataLoggingPath + + "/layer_" + std::to_string(layer) + ".json"); + } + const auto layerJson = nlohmann::json::parse(layerFile); + if (layerJson.find("final_node_id") == layerJson.end()) { + throw std::runtime_error("Missing key \"final_node_id\" in " + + dataLoggingPath + "/layer_" + + std::to_string(layer) + ".json"); + } + const std::size_t finalNodeId = layerJson["final_node_id"]; + return finalNodeId; +} + +/** + * @brief parses all nodes in a given layer from a data log and enter them + * into `nodes` with each node at the position corresponding to its id. + * + * Only logged values are entered into the nodes, all other values are left at + * default (e.g. `validMappedTwoQubitGates` and `sharedSwaps`) + */ +void parseNodesFromDatalog(std::string dataLoggingPath, std::size_t layer, + std::vector& nodes) { + if (dataLoggingPath.back() != '/') { + dataLoggingPath += '/'; + } + const std::string layerNodeFilePath = + dataLoggingPath + "/nodes_layer_" + std::to_string(layer) + ".csv"; + auto layerNodeFile = std::ifstream(layerNodeFilePath); + if (!layerNodeFile.is_open()) { + throw std::runtime_error("Could not open file " + layerNodeFilePath); + } + // iterating over the lines in the csv file and then over the entries + // separated by ';' + std::string line; + while (std::getline(layerNodeFile, line)) { + if (line.empty()) { + continue; + } + std::string col; + std::size_t nodeId = 0; + + std::stringstream lineStream(line); + if (std::getline(lineStream, col, ';')) { + nodeId = std::stoull(col); + if (nodeId >= nodes.size()) { + throw std::runtime_error("Node id " + std::to_string(nodeId) + + " out of range in " + layerNodeFilePath); + } + nodes[nodeId].id = nodeId; + } else { + throw std::runtime_error("Missing value for node id in " + + layerNodeFilePath); + } + auto& node = nodes[nodeId]; + if (std::getline(lineStream, col, ';')) { + node.parent = std::stoull(col); + } else { + throw std::runtime_error("Missing value for parent node id in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + node.costFixed = std::stod(col); + } else { + throw std::runtime_error("Missing value for fixed cost in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + node.costHeur = std::stod(col); + } else { + throw std::runtime_error("Missing value for heuristic cost in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + node.lookaheadPenalty = std::stod(col); + } else { + throw std::runtime_error("Missing value for lookahead penalty in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + const std::size_t validMapping = std::stoull(col); + if (validMapping > 1) { + throw std::runtime_error("Non-boolean value " + + std::to_string(validMapping) + + " for validMapping in " + layerNodeFilePath); + } + node.validMapping = static_cast(validMapping); + } else { + throw std::runtime_error("Missing value for validMapping in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + node.depth = std::stoull(col); + } else { + throw std::runtime_error("Missing value for depth in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + std::stringstream qubitMapBuffer(col); + std::string entry; + for (std::size_t i = 0; std::getline(qubitMapBuffer, entry, ','); ++i) { + auto qubit = static_cast(std::stoi(entry)); + node.qubits.at(i) = qubit; + if (qubit >= 0) { + node.locations.at(static_cast(qubit)) = + static_cast(i); + } + } + } else { + throw std::runtime_error("Missing value for qubit layout in " + + layerNodeFilePath); + } + if (std::getline(lineStream, col, ';')) { + std::stringstream swapBuffer(col); + std::string entry; + while (std::getline(swapBuffer, entry, ',')) { + std::uint16_t q1 = 0; + std::uint16_t q2 = 0; + std::string opTypeStr; + std::stringstream(entry) >> q1 >> q2 >> opTypeStr; + qc::OpType opType = SWAP; + if (!opTypeStr.empty()) { + // if no opType is given, the default value is SWAP + opType = qc::opTypeFromString(opTypeStr); + } + node.swaps.emplace_back(q1, q2, opType); + } + } + } +} + +/** + * @brief Get the path from a node to the root node (id of the given node is the + * first element, id of the root is last) + * + * @param nodes vector of all nodes (each at the position corresponding to its + * id) + * @param nodeId id of the node from which to find the path to the root + */ +std::vector +getPathToRoot(std::vector& nodes, std::size_t nodeId) { + std::vector path{}; + if (nodeId >= nodes.size() || nodes[nodeId].id != nodeId) { + throw std::runtime_error("Invalid node id " + std::to_string(nodeId)); + } + auto* node = &nodes[nodeId]; + while (node->parent != node->id) { + path.push_back(node->id); + if (node->parent >= nodes.size() || + nodes[node->parent].id != node->parent) { + throw std::runtime_error("Invalid parent id " + + std::to_string(node->parent) + " for node " + + std::to_string(node->id)); + } + node = &nodes[node->parent]; + } + path.push_back(node->id); + return path; +} + +class InternalsTest : public HeuristicMapper, public testing::Test { +protected: + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + static Architecture defaultArch; + + InternalsTest() : HeuristicMapper(qc::QuantumComputation{1}, defaultArch) {} + void SetUp() override { results = MappingResults{}; } +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +Architecture InternalsTest::defaultArch{1, {}}; + +TEST_F(InternalsTest, NodeCostCalculation) { + results.config.heuristic = Heuristic::GateCountMaxDistance; + results.config.lookaheadHeuristic = LookaheadHeuristic::None; + results.config.layering = Layering::Disjoint2qBlocks; + + architecture->loadCouplingMap(5, {{0, 1}, {1, 2}, {3, 1}, {4, 3}}); + qc = qc::QuantumComputation{5}; + // layer 0 distances: + // 0-1: 2 swaps & 2 reversals + // 2-3: 0 swaps & 1 reversals + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(1, 0); + qc.cx(1, 0); + qc.cx(3, 2); + // Architecture::distance currently counts at most 1 reversal per qubit pair + createLayers(); + + EXPECT_EQ(layers.size(), 1) + << "layering failed, not able to test node cost calculation"; + + const std::vector swaps{Exchange(0, 1, qc::OpType::Teleportation), + Exchange(1, 2, SWAP)}; -TEST(Functionality, NodeCostCalculation) { - const double tolerance = 1e-6; - const CouplingMap cm = {{0, 1}, {1, 2}, {3, 1}, {4, 3}}; - Architecture arch{5, cm}; - const SingleQubitMultiplicity empty1Mult = {}; - const std::unordered_set& consideredQubits = {0, 1, 2, 3, 5}; - const TwoQubitMultiplicity multiplicity = {{{0, 1}, {5, 2}}, - {{2, 3}, {0, 1}}}; - const std::array qubits = {4, 3, 1, 2, 0}; - const std::array locations = {4, 2, 3, 1, 0}; - - const std::vector> swaps = { - {Exchange(0, 1, qc::OpType::Teleportation)}, - {Exchange(1, 2, qc::OpType::SWAP)}}; - - HeuristicMapper::Node node(0, 0, qubits, locations, swaps, 5., 0, false, - false); - node.updateHeuristicCost(arch, empty1Mult, multiplicity, consideredQubits); + HeuristicMapper::Node node(0, 0, {4, 3, 1, 2, 0}, {4, 2, 3, 1, 0}, swaps, + {{2, 3}}, 5., 0); + EXPECT_NEAR(node.costFixed, 5., FLOAT_TOLERANCE); + EXPECT_NEAR(node.lookaheadPenalty, 0., FLOAT_TOLERANCE); + EXPECT_EQ(node.validMappedTwoQubitGates.size(), 1); + + results.config.heuristic = Heuristic::GateCountSumDistance; + updateHeuristicCost(0, node); EXPECT_NEAR(node.costHeur, - COST_UNIDIRECTIONAL_SWAP * 14 + COST_DIRECTION_REVERSE * 3, - tolerance); + COST_UNIDIRECTIONAL_SWAP * 2 + COST_DIRECTION_REVERSE * 2, + FLOAT_TOLERANCE); + EXPECT_NEAR(node.costFixed, 5., FLOAT_TOLERANCE) + << "updateHeuristicCost should not change costFixed"; - node = - HeuristicMapper::Node(0, 0, qubits, locations, swaps, 5., 0, false, true); - EXPECT_NEAR(node.costFixed, 5., tolerance); - node.updateHeuristicCost(arch, empty1Mult, multiplicity, consideredQubits); + results.config.heuristic = Heuristic::GateCountMaxDistance; + updateHeuristicCost(0, node); EXPECT_NEAR(node.costHeur, - COST_UNIDIRECTIONAL_SWAP * 2 + COST_DIRECTION_REVERSE, tolerance); - node.applySWAP({3, 4}, arch, empty1Mult, multiplicity); - node.updateHeuristicCost(arch, empty1Mult, multiplicity, consideredQubits); - EXPECT_NEAR(node.costFixed, 5. + COST_UNIDIRECTIONAL_SWAP, tolerance); + COST_UNIDIRECTIONAL_SWAP * 2 + COST_DIRECTION_REVERSE, + FLOAT_TOLERANCE); + EXPECT_NEAR(node.costFixed, 5., FLOAT_TOLERANCE) + << "updateHeuristicCost should not change costFixed"; + + applySWAP({3, 4}, 0, node); + EXPECT_NEAR(node.costFixed, 5. + COST_UNIDIRECTIONAL_SWAP, FLOAT_TOLERANCE); EXPECT_NEAR(node.costHeur, COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE, - tolerance); + FLOAT_TOLERANCE); + EXPECT_EQ(node.validMappedTwoQubitGates.size(), 0); + node.lookaheadPenalty = 0.; EXPECT_NEAR(node.getTotalCost(), 5. + COST_UNIDIRECTIONAL_SWAP * 2 + COST_DIRECTION_REVERSE, - tolerance); + FLOAT_TOLERANCE); EXPECT_NEAR(node.getTotalFixedCost(), 5. + COST_UNIDIRECTIONAL_SWAP, - tolerance); + FLOAT_TOLERANCE); + node.lookaheadPenalty = 2.; EXPECT_NEAR(node.getTotalCost(), 7. + COST_UNIDIRECTIONAL_SWAP * 2 + COST_DIRECTION_REVERSE, - tolerance); + FLOAT_TOLERANCE); EXPECT_NEAR(node.getTotalFixedCost(), 7. + COST_UNIDIRECTIONAL_SWAP, - tolerance); - node.recalculateFixedCost(arch, empty1Mult, multiplicity); + FLOAT_TOLERANCE); + + recalculateFixedCost(0, node); + EXPECT_EQ(node.validMappedTwoQubitGates.size(), 0); EXPECT_NEAR(node.costFixed, COST_TELEPORTATION + COST_UNIDIRECTIONAL_SWAP * 2, - tolerance); + FLOAT_TOLERANCE); EXPECT_NEAR(node.costHeur, COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE, - tolerance); + FLOAT_TOLERANCE); EXPECT_NEAR(node.getTotalCost(), 2. + COST_TELEPORTATION + COST_UNIDIRECTIONAL_SWAP * 3 + COST_DIRECTION_REVERSE, - tolerance); + FLOAT_TOLERANCE); EXPECT_NEAR(node.getTotalFixedCost(), 2. + COST_TELEPORTATION + COST_UNIDIRECTIONAL_SWAP * 2, - tolerance); + FLOAT_TOLERANCE); +} + +TEST_F(InternalsTest, NodeLookaheadCalculation) { + results.config.heuristic = Heuristic::GateCountMaxDistance; + results.config.lookaheadHeuristic = LookaheadHeuristic::None; + results.config.layering = Layering::Disjoint2qBlocks; + + architecture->loadCouplingMap(5, {{0, 1}, {1, 2}, {3, 1}, {4, 3}}); + qc = qc::QuantumComputation{5}; + // layer 0 distances: + // 0-1: 2 swaps & 2 reversals + // 2-3: 0 swaps & 1 reversals + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(0, 1); + qc.cx(1, 0); + qc.cx(1, 0); + qc.cx(3, 2); + + // layer 1 distances: + // 0-4: 2 swaps & 0 reversals + // 1-2: 1 swaps & 1 reversals + qc.cx(0, 4); + qc.cx(0, 4); + qc.cx(1, 2); + + // layer 2 distances: + // 0-1: 2 swaps & 0 reversals + qc.cx(0, 1); + + // layer 3 distances: + // 0-4: 2 swaps & 0 reversals + qc.cx(4, 0); + + // Architecture::distance currently counts at most 1 reversal per qubit pair + + createLayers(); + + EXPECT_EQ(layers.size(), 4) + << "layering failed, not able to test node cost calculation"; + + const std::vector swaps{Exchange(0, 1, qc::OpType::Teleportation), + Exchange(1, 2, SWAP)}; + + HeuristicMapper::Node node(0, 0, {4, 3, 1, 2, 0}, {4, 2, 3, 1, 0}, swaps, + {{2, 3}}, 5., 0); + EXPECT_NEAR(node.lookaheadPenalty, 0., FLOAT_TOLERANCE); + + results.config.firstLookaheadFactor = 0.75; + results.config.lookaheadFactor = 0.5; + + results.config.lookaheadHeuristic = LookaheadHeuristic::GateCountMaxDistance; + results.config.nrLookaheads = 1; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, 0.75 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + results.config.nrLookaheads = 2; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + results.config.nrLookaheads = 3; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + results.config.nrLookaheads = 4; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + + results.config.lookaheadHeuristic = LookaheadHeuristic::GateCountSumDistance; + results.config.nrLookaheads = 1; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (3 * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE), + FLOAT_TOLERANCE); + results.config.nrLookaheads = 2; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (3 * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE) + + 0.75 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + results.config.nrLookaheads = 3; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (3 * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE) + + 0.75 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + results.config.nrLookaheads = 4; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (3 * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE) + + 0.75 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP) + + 0.75 * 0.5 * 0.5 * (2 * COST_UNIDIRECTIONAL_SWAP), + FLOAT_TOLERANCE); + + node.qubits = {4, 3, 1, -1, -1}; + node.locations = {-1, 2, -1, 1, 0}; + + results.config.lookaheadHeuristic = LookaheadHeuristic::GateCountMaxDistance; + results.config.nrLookaheads = 1; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE), + FLOAT_TOLERANCE); + + results.config.lookaheadHeuristic = LookaheadHeuristic::GateCountSumDistance; + results.config.nrLookaheads = 1; + updateLookaheadPenalty(0, node); + EXPECT_NEAR(node.lookaheadPenalty, + 0.75 * (2 * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE), + FLOAT_TOLERANCE); +} + +class TestHeuristics + : public testing::TestWithParam> { +protected: + std::string testExampleDir = "../examples/"; + std::string testArchitectureDir = "../extern/architectures/"; + std::string testCalibrationDir = "../extern/calibration/"; + + qc::QuantumComputation qc{}; + std::string circuitName{}; + Architecture ibmqYorktown{}; // 5 qubits + Architecture ibmqLondon{}; // 5 qubits (with calibration) + std::unique_ptr ibmqYorktownMapper; + std::unique_ptr ibmqLondonMapper; + Architecture ibmQX5{}; // 16 qubits + std::unique_ptr ibmQX5Mapper; + Configuration settings{}; + + static const std::unordered_map>> + OPTIMAL_SOLUTIONS; + + void SetUp() override { + std::string cn = std::get<1>(GetParam()); + std::replace(cn.begin(), cn.end(), '-', '_'); + std::stringstream ss{}; + ss << cn << "_" << toString(std::get<0>(GetParam())); + const std::string testName = ss.str(); + + circuitName = std::get<1>(GetParam()); + qc.import(testExampleDir + circuitName + ".qasm"); + ibmqYorktown.loadCouplingMap(AvailableArchitecture::IbmqYorktown); + ibmqLondon.loadCouplingMap(testArchitectureDir + "ibmq_london.arch"); + ibmqLondon.loadProperties(testCalibrationDir + "ibmq_london.csv"); + ibmqYorktownMapper = std::make_unique(qc, ibmqYorktown); + ibmqLondonMapper = std::make_unique(qc, ibmqLondon); + ibmQX5.loadCouplingMap(AvailableArchitecture::IbmQx5); + ibmQX5Mapper = std::make_unique(qc, ibmQX5); + settings.debug = true; + settings.automaticLayerSplits = false; + settings.initialLayout = InitialLayout::Identity; + settings.layering = Layering::Disjoint2qBlocks; + settings.lookaheadHeuristic = LookaheadHeuristic::None; + settings.heuristic = std::get<0>(GetParam()); + settings.dataLoggingPath = "test_log/heur_properties_" + testName + "/"; + } +}; + +const std::unordered_map>> + TestHeuristics::OPTIMAL_SOLUTIONS{ + {"3_17_13", + {{0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {1, 0, 2}, + {1, 2, 0}, + {1, 2, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 0, 1}, + {2, 1, 0}, + {2, 0, 1}, + {2, 0, 1}, + {2, 1, 0}, + {1, 0, 2}, + {1, 2, 0}, + {1, 2, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}, + {2, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}, + {0, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2}, + {0, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2}, + {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2}, + {2, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}}}, + {"ex-1_166", + {{0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {1, 0, 2}, + {1, 0, 2}, + {1, 2, 0}, + {1, 2, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {0, 1, 2}, + {0, 1, 2}, + {1, 0, 2}, + {1, 0, 2}, + {0, 1, 2}, + {1, 0, 2}, + {1, 0, 2}, + {1, 2, 0}, + {1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}}}, + {"ham3_102", + {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, + {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}, {1, 0, 2}, {1, 0, 2}, + {1, 2, 0}, {1, 2, 0}, {2, 1, 0}, {2, 1, 0}, {2, 0, 1}, {2, 1, 0}, + {0, 1, 2}, {1, 0, 2}, {1, 0, 2}, {0, 1, 2}, {1, 0, 2}, {1, 0, 2}, + {1, 2, 0}, {1, 2, 0}, {1, 2, 0}}}, + {"miller_11", + {{0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {0, 1, 2}, + {1, 0, 2}, + {1, 2, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {2, 1, 0}, + {0, 1, 2}, + {0, 1, 2}, + {0, 2, 1}, + {0, 2, 1}, + {0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1}, + {0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1}, + {2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1}, + {2, 0, 1}, + {2, 1, 0}, + {2, 1, 0}, + {1, 2, 0}, + {1, 2, 0}, + {1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}, + {1, 2, 0}, + {1, 2, 0}, + {1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}, + {1, 2, 0}, + {1, 2, 0}, + {2, 1, 0}, + {1, 2, 0}, + {1, 2, 0}, + {1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}, + {1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0}}}, + {"4gt11_84", {{0, 1, 2, -1, 4}, {0, 1, 2, -1, 4}, {0, 1, 2, -1, 4}, + {0, 1, 2, -1, 4}, {0, 1, 2, -1, 4}, {0, 1, 2, -1, 4}, + {2, 1, 0, 3, 4}, {0, 1, 2, -1, 4}, {0, 1, 2, -1, 4}, + {1, 0, 2, -1, 4}, {1, 2, 0, -1, 4}, {2, 1, 0, -1, 4}, + {2, 0, 1, -1, 4}, {4, 2, 1, 0, 3}, {0, 1, 2, -1, 4}, + {0, 1, 2, -1, 4}, {0, 2, 1, -1, 4}, {0, 2, 1, -1, 4}, + {2, 0, 1, -1, 4}, {2, 0, 1, -1, 4}, {2, 1, 0, 3, 4}}}, + {"4mod5-v0_20", + {{0, 2, 1, 3, 4}, {0, 2, 1, 3, 4}, {0, 2, 1, 3, 4}, {0, 2, 4, 3, 1}, + {0, 4, 2, 3, 1}, {0, 4, 1, 3, 2}, {0, 4, 2, 3, 1}, {0, 4, 2, 3, 1}, + {4, 0, 2, 1, 3}, {4, 2, 0, 1, 3}, {4, 1, 0, 2, 3}, {4, 2, 0, 1, 3}, + {4, 2, 0, 1, 3}, {4, 1, 0, 2, 3}, {4, 2, 0, 1, 3}, {4, 2, 0, 1, 3}, + {0, 2, 1, 3, 4}, {0, 2, 1, 3, 4}, {0, 2, 3, 1, 4}, {0, 3, 2, 4, 1}, + {0, 3, 4, 2, 1}, {0, 3, 4, 1, 2}, {0, 3, 4, 2, 1}, {0, 3, 4, 2, 1}}}, + {"mod5d1_63", + {{0, 2, 1, 3, 4}, + {0, 2, 1, 3, 4}, + {1, 2, 0, 3, 4}, + {1, 2, 0, 3, 4}, + {1, 2, 0, 3, 4}, + {1, 2, 4, 3, 0}, + {4, 2, 1, 3, 0}, + {4, 2, 0, 3, 1}, + {4, 2, 1, 3, 0}, + {4, 2, 1, 3, 0}, + {4, 2, 0, 3, 1}, + {4, 0, 2, 1, 3}, + {4, 1, 2, 0, 3}, + {4, 0, 2, 1, 3}, + {4, 0, 2, 1, 3}, + {4, 0, 2, 1, 3}, + {4, 1, 2, 0, 3}, + {4, 1, 2, 0, 3}, + {4, 0, 2, 1, 3}, + {4, 1, 2, 0, 3}, + {4, 1, 2, 0, 3}, + {4, 0, 2, 1, 3}, + {0, 2, 1, 3, 4}, + {0, 2, 3, 1, 4}, + {2, 3, 1, 0, 4}, + {2, 3, 1, 0, 4}, + {2, 3, 1, 0, 4}, + {2, 3, 1, 4, 0}, + {2, 3, 4, 1, 0}, + {2, 3, 4, 0, 1}, + {2, 3, 4, 1, 0}, + {2, 3, 4, 1, 0}, + {-1, 3, 1, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4}}}, + {"ising_model_10", + {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}}, + {"rd73_140", + {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 2, 3, 4, 5, 1, 7, 6, 8, 9}, + {2, 3, 4, 0, 7, 5, 1, 6, 8, 9}, + {2, 3, 4, 0, 1, 7, 5, 6, 8, 9}, + {2, 3, 4, 0, 1, 7, 5, 6, 8, 9}, + {2, 3, 4, 0, 7, 1, 5, 6, 8, 9}, + {2, 7, 3, 4, 0, 1, 5, 6, 8, 9}, + {2, 3, 4, 0, 7, 8, 1, 5, 6, 9}, + {3, 4, 2, 8, 0, 7, 1, 5, 6, 9}, + {3, 4, 8, 2, 7, 0, 1, 5, 6, 9}, + {3, 4, 8, 7, 2, 0, 1, 5, 6, 9}, + {3, 4, 8, 2, 7, 0, 1, 5, 6, 9}, + {3, 8, 4, 2, 7, 0, 1, 5, 6, 9}, + {3, 8, 9, 4, 7, 2, 1, 5, 6, -1, -1, -1, 0}, + {8, 3, 9, 4, 7, 2, 1, 5, 6, -1, -1, -1, 0}, + {8, 3, 9, 4, 7, 1, 2, 5, 6, -1, -1, -1, 0}, + {8, 9, 3, 4, 7, 1, 2, 5, 6, -1, -1, -1, 0}, + {8, 9, 3, 4, 7, 2, 1, 5, 6, -1, -1, -1, 0}, + {9, 8, 3, 4, 7, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 4, 3, 7, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 7, 4, 3, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 3, 7, 4, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 3, 7, 4, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 7, 3, 4, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 3, 7, 4, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 4, 3, 7, 1, 2, 5, 6, -1, -1, -1, 0}, + {9, 8, 4, 3, 2, 7, 1, 5, 6, -1, -1, -1, 0}, + {9, 8, -1, 2, 3, 7, 1, 5, 6, -1, -1, -1, 0, -1, -1, 4}, + {9, -1, 8, 2, 7, 3, 1, 5, 6, -1, -1, -1, 0, -1, -1, 4}, + {4, -1, 8, 2, 3, 7, 1, 5, 6, -1, -1, -1, 0, -1, -1, 9}, + {4, -1, 8, 2, 3, 7, 1, 5, 6, -1, -1, -1, 0, -1, -1, 9}, + {4, 8, -1, 2, 7, 3, 1, 5, 6, -1, -1, -1, 0, -1, -1, 9}, + {9, 8, -1, 7, 2, 3, 1, 5, 6, -1, -1, -1, 0, -1, 4}, + {9, 8, 7, -1, 2, 3, 1, 5, 6, -1, -1, -1, 0, -1, 4}, + {9, 7, 8, -1, 2, 3, 1, 5, 6, -1, -1, -1, 0, -1, -1, 4}, + {9, 8, 7, -1, 2, 3, 1, 5, 6, -1, -1, -1, 0, -1, -1, 4}, + {9, 8, 7, -1, 2, 3, 1, 5, 6, -1, -1, -1, 0, -1, -1, 4}, + {9, 7, 8, -1, 2, 3, 1, 5, 6, -1, -1, -1, 0, -1, -1, 4}, + {4, 7, 8, 5, -1, 2, 3, 1, 6, -1, -1, -1, 0, -1, -1, 9}, + {9, 7, 5, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 8, 7, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 4}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 4, 5, 7, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 8, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 7}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 7, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 8}, + {9, 5, 8, 4, 3, -1, 2, 1, 6, -1, -1, -1, 0, -1, -1, 7}, + {5, 9, 8, 6, 4, 3, -1, 2, 1, -1, -1, -1, 0, -1, -1, 7}, + {7, 9, 8, 6, 4, 3, -1, 2, 1, -1, -1, -1, 0, 5}, + {-1, 9, 6, 8, 4, 3, -1, 2, 1, -1, -1, -1, 0, -1, 5, 7}, + {-1, 9, 6, 8, -1, 3, -1, 2, 1, -1, -1, -1, 0, 4, 7, 5}, + {-1, 9, 8, 6, -1, 3, -1, 2, 1, -1, -1, -1, 0, 4, 5, 7}, + {-1, 9, 6, 8, -1, 3, -1, 2, 1, -1, -1, -1, 0, 4, 5, 7}, + {-1, 9, 6, 8, -1, 3, -1, 2, 1, -1, -1, -1, 0, 4, 7, 5}, + {-1, 9, 6, 8, -1, 3, -1, 2, 1, -1, -1, -1, 0, 7, 4, 5}, + {-1, 9, 6, 8, -1, 3, -1, 2, 1, -1, -1, -1, 0, 7, 4, 5}, + {-1, 9, 8, 4, -1, 3, -1, 2, 1, -1, -1, -1, 0, 7, 6, 5}, + {-1, 9, 8, 4, -1, 3, -1, 2, 1, -1, -1, -1, 0, 6, 7, 5}, + {-1, 9, 8, 4, -1, 3, -1, 2, 1, -1, -1, -1, 0, 7, 6, 5}, + {-1, 9, 8, 4, -1, 3, -1, 2, 1, -1, -1, -1, 0, 7, 6, 5}, + {-1, 9, 8, 4, -1, 3, -1, 2, 1, -1, -1, -1, 0, 6, 7, 5}, + {-1, 9, 8, 4, -1, 3, -1, 2, 1, -1, -1, -1, 0, 7, 6, 5}}}}; + +INSTANTIATE_TEST_SUITE_P( + Heuristic, TestHeuristics, + testing::Combine( + testing::Values( + Heuristic::GateCountMaxDistance, Heuristic::GateCountSumDistance, + Heuristic::GateCountSumDistanceMinusSharedSwaps, + Heuristic::GateCountMaxDistanceOrSumDistanceMinusSharedSwaps, + Heuristic::FidelityBestLocation), + testing::Values("3_17_13", // 5q + "ex-1_166", // 5q + "ham3_102", // 5q + "miller_11", // 5q + "4gt11_84", // 5q + "4mod5-v0_20", // 5q + "mod5d1_63", // 5q + "ising_model_10", // 16q + "rd73_140" // 16q + )), + [](const testing::TestParamInfo& inf) { + std::string name = std::get<1>(inf.param); + std::replace(name.begin(), name.end(), '-', '_'); + std::stringstream ss{}; + ss << name << "_" << toString(std::get<0>(inf.param)); + return ss.str(); + }); + +TEST_P(TestHeuristics, HeuristicProperties) { + EXPECT_TRUE(!isAdmissible(settings.heuristic) || + isPrincipallyAdmissible(settings.heuristic)) + << "Admissible heuristics are by definition also principally admissible: " + << toString(settings.heuristic); + + EXPECT_TRUE( + !(isTight(settings.heuristic) && isFidelityAware(settings.heuristic))) + << "Fidelity-aware heuristics cannot be tight because of the " + "non-convexity of the fidelity-aware cost function: " + << toString(settings.heuristic); + + // list of nodes for each search process (i.e. each layer) in all mappings + // each node is at the position corresponding to its id; positions of unused + // ids are filled with default values (i.e. node.id = 0) + std::vector> allNodes{}; + std::vector layerNames{}; + std::vector finalSolutionIds{}; + + // map to IBM Yorktown if possible + if (qc.getNqubits() <= ibmqYorktown.getNqubits()) { + if (isFidelityAware(settings.heuristic)) { + EXPECT_THROW(ibmqYorktownMapper->map(settings), QMAPException); + } else { + ibmqYorktownMapper->map(settings); + auto results = ibmqYorktownMapper->getResults(); + for (std::size_t i = 0; i < results.layerHeuristicBenchmark.size(); ++i) { + allNodes.emplace_back( + results.layerHeuristicBenchmark.at(i).generatedNodes); + layerNames.emplace_back("on ibmq_yorktown in layer " + + std::to_string(i)); + parseNodesFromDatalog(settings.dataLoggingPath, i, allNodes.back()); + finalSolutionIds.push_back( + getFinalNodeFromDatalog(settings.dataLoggingPath, i)); + } + } + } + + // map to IBM London if possible + if (qc.getNqubits() <= ibmqLondon.getNqubits()) { + ibmqLondonMapper->map(settings); + auto results = ibmqLondonMapper->getResults(); + for (std::size_t i = 0; i < results.layerHeuristicBenchmark.size(); ++i) { + allNodes.emplace_back( + results.layerHeuristicBenchmark.at(i).generatedNodes); + layerNames.emplace_back("on ibmq_london in layer " + std::to_string(i)); + parseNodesFromDatalog(settings.dataLoggingPath, i, allNodes.back()); + finalSolutionIds.push_back( + getFinalNodeFromDatalog(settings.dataLoggingPath, i)); + } + } + + // map to IBM QX5 if possible + if (qc.getNqubits() <= ibmQX5.getNqubits()) { + if (isFidelityAware(settings.heuristic)) { + EXPECT_THROW(ibmQX5Mapper->map(settings), QMAPException); + } else { + ibmQX5Mapper->map(settings); + auto results = ibmQX5Mapper->getResults(); + for (std::size_t i = 0; i < results.layerHeuristicBenchmark.size(); ++i) { + allNodes.emplace_back( + results.layerHeuristicBenchmark.at(i).generatedNodes); + layerNames.emplace_back("on ibmQX5 in layer " + std::to_string(i)); + parseNodesFromDatalog(settings.dataLoggingPath, i, allNodes.back()); + finalSolutionIds.push_back( + getFinalNodeFromDatalog(settings.dataLoggingPath, i)); + } + } + } + + for (std::size_t i = 0; i < allNodes.size(); ++i) { + auto& nodes = allNodes.at(i); + auto& finalSolutionId = finalSolutionIds.at(i); + + if (finalSolutionId >= nodes.size() || + nodes.at(finalSolutionId).id != finalSolutionId) { + FAIL() << "Final solution node " << finalSolutionId << " not found " + << layerNames.at(i); + } + auto& finalSolutionNode = nodes.at(finalSolutionId); + EXPECT_TRUE(finalSolutionNode.validMapping); + + if (isPrincipallyAdmissible(settings.heuristic)) { + // for principally admissible heuristics all nodes on the optimal + // solution path should have + // node.costFixed+node.costHeur <= finalSolutionNode.costFixed + auto solutionPath = getPathToRoot(nodes, finalSolutionId); + for (auto nodeId : solutionPath) { + if (nodes.at(nodeId).id != nodeId) { + throw std::runtime_error("Invalid node id " + std::to_string(nodeId) + + " " + layerNames.at(i)); + } + EXPECT_LE(nodes.at(nodeId).getTotalCost(), finalSolutionNode.costFixed) + << "Heuristic " << toString(settings.heuristic) + << " is not principally admissible " << layerNames.at(i) + << " in node " << nodeId; + } + if (isTight(settings.heuristic)) { + // Principally admissible heuristics are guaranteed to find a solution + // with optimal cost (given lookahead is disabled). If there are + // multiple optimal solutions, though, and the heuristic is not tight, + // the differences in heuristics can lead to a different node ordering + // and thereby different solutions (potentially even resulting in + // different costs in later layers) + // However, if a heuristic is both principally admissible and tight, + // it is guaranteed to always find the same solution as any other such + // heuristic. + if (OPTIMAL_SOLUTIONS.find(circuitName) == OPTIMAL_SOLUTIONS.end() || + OPTIMAL_SOLUTIONS.at(circuitName).size() <= i) { + throw std::runtime_error( + "Missing precalculated optimal solution for circuit " + + circuitName); + } + std::size_t finalLayoutLastIndex = 0; + for (std::size_t j = finalSolutionNode.qubits.size() - 1; j > 0; --j) { + if (finalSolutionNode.qubits.at(j) != -1) { + finalLayoutLastIndex = j; + break; + } + } + std::vector finalLayout{}; + std::copy(finalSolutionNode.qubits.begin(), + finalSolutionNode.qubits.begin() + finalLayoutLastIndex + 1, + std::back_inserter(finalLayout)); + EXPECT_EQ(finalLayout, OPTIMAL_SOLUTIONS.at(circuitName).at(i)) + << "Heuristic " << toString(settings.heuristic) + << " did not find the optimal solution " << layerNames.at(i); + } + } + + for (std::size_t j = 0; j < nodes.size(); ++j) { + const auto& node = nodes.at(j); + if (j != node.id) { + continue; + } + + if (isNonDecreasing(settings.heuristic)) { + if (node.parent != node.id) { + if (node.parent >= nodes.size() || + nodes.at(node.parent).id != node.parent) { + FAIL() << "Invalid parent id " << node.parent << " for node " + << node.id << " " << layerNames.at(i); + } + EXPECT_GE(node.getTotalCost(), nodes.at(node.parent).getTotalCost()) + << "Heuristic " << toString(settings.heuristic) + << " does not result in non-decreasing cost estimation " + << layerNames.at(i) << " in node " << node.id; + } + } + + EXPECT_NEAR(node.lookaheadPenalty, 0., FLOAT_TOLERANCE) + << "Lookahead penalty not 0 " << layerNames.at(i) + << " even though lookahead has been deactivated"; + + if (node.validMapping) { + if (isTight(settings.heuristic)) { + // tight heuristics are 0 in any goal node + EXPECT_NEAR(node.costHeur, 0., FLOAT_TOLERANCE) + << "Heuristic " << toString(settings.heuristic) + << " is not tight " << layerNames.at(i) << " in node " << node.id; + } + + if (isAdmissible(settings.heuristic)) { + // for admissible heuristics all nodes should have + // node.costFixed+node.costHeur <= solutionNode.costFixed, + // where solutionNode is the best goal node reachable from the node; + // since reachability in a directed tree is equivalent to the being + // on the same path to the root, one can also check that all nodes on + // the path to the root from any goal node fulfill this condition + auto path = getPathToRoot(nodes, node.id); + for (auto nodeId : path) { + auto& n = nodes.at(nodeId); + EXPECT_LE(n.getTotalCost(), node.costFixed) + << "Heuristic " << toString(settings.heuristic) + << " is not admissible " << layerNames.at(i) << " in node " + << nodeId; + } + } + } + } + } } TEST(Functionality, HeuristicBenchmark) { @@ -86,9 +957,9 @@ TEST(Functionality, HeuristicBenchmark) { architecture.loadCouplingMap(5, cm); qc::QuantumComputation qc{5, 5}; - qc.cx(qc::Control{4}, 2); - qc.cx(qc::Control{3}, 1); - qc.cx(qc::Control{4}, 1); + qc.cx(4, 2); + qc.cx(3, 1); + qc.cx(4, 1); qc.barrier({0, 1, 2, 3, 4}); for (size_t i = 0; i < 5; ++i) { @@ -97,12 +968,12 @@ TEST(Functionality, HeuristicBenchmark) { const auto mapper = std::make_unique(qc, architecture); Configuration settings{}; - settings.admissibleHeuristic = true; + settings.heuristic = Heuristic::GateCountMaxDistance; settings.layering = Layering::DisjointQubits; settings.initialLayout = InitialLayout::Identity; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; - settings.lookahead = false; + settings.lookaheadHeuristic = LookaheadHeuristic::None; settings.debug = true; mapper->map(settings); auto& result = mapper->getResults(); @@ -167,7 +1038,7 @@ TEST(Functionality, EmptyDump) { TEST(Functionality, BenchmarkGeneratedNodes) { qc::QuantumComputation qc{16, 16}; - qc.cx(qc::Control{0}, 6); + qc.cx(0, 6); for (std::size_t i = 0; i < 16; ++i) { qc.measure(static_cast(i), i); } @@ -176,14 +1047,13 @@ TEST(Functionality, BenchmarkGeneratedNodes) { auto ibmQX5Mapper = std::make_unique(qc, ibmQX5); Configuration settings{}; - settings.admissibleHeuristic = true; - settings.lookahead = false; + settings.heuristic = Heuristic::GateCountMaxDistance; + settings.lookaheadHeuristic = LookaheadHeuristic::None; settings.layering = Layering::IndividualGates; settings.automaticLayerSplits = false; settings.initialLayout = InitialLayout::Identity; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; - settings.considerFidelity = false; settings.useTeleportation = false; settings.debug = true; ibmQX5Mapper->map(settings); @@ -201,20 +1071,25 @@ TEST(Functionality, InvalidSettings) { props.setSingleQubitErrorRate(0, "x", 0.1); arch.loadProperties(props); HeuristicMapper mapper(qc, arch); - auto config = Configuration{}; - config.method = Method::Heuristic; - config.layering = Layering::OddGates; + auto config = Configuration{}; + config.method = Method::Heuristic; + config.heuristic = Heuristic::GateCountMaxDistance; + config.lookaheadHeuristic = LookaheadHeuristic::GateCountMaxDistance; + // invalid layering + config.layering = Layering::OddGates; EXPECT_THROW(mapper.map(config), QMAPException); config.layering = Layering::QubitTriangle; EXPECT_THROW(mapper.map(config), QMAPException); - config.layering = Layering::IndividualGates; - config.considerFidelity = true; - config.lookahead = true; + config.layering = Layering::IndividualGates; + // fidelity-aware heuristic with non-fidelity-aware lookahead heuristic + config.heuristic = Heuristic::FidelityBestLocation; EXPECT_THROW(mapper.map(config), QMAPException); - config.lookahead = false; + config.lookaheadHeuristic = LookaheadHeuristic::None; + // fidelity-aware heuristic with teleportation config.teleportationQubits = 2; EXPECT_THROW(mapper.map(config), QMAPException); config.teleportationQubits = 0; + // valid settings EXPECT_NO_THROW(mapper.map(config)); } @@ -272,61 +1147,8 @@ TEST(Functionality, InvalidCircuits) { EXPECT_THROW(mapper3.map(config), QMAPException); } -TEST(Functionality, HeuristicAdmissibility) { - Architecture architecture{}; - const CouplingMap cm = {{0, 1}, {1, 0}, {1, 2}, {2, 1}, {2, 3}, - {3, 2}, {3, 4}, {4, 3}, {4, 5}, {5, 4}}; - architecture.loadCouplingMap(6, cm); - const std::vector perms{{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}}; - - const SingleQubitMultiplicity empty1Mult = {}; - const std::unordered_set& consideredQubits = {0, 1, 2, - 3, 4, 5}; - const TwoQubitMultiplicity multiplicity = { - {{0, 4}, {1, 0}}, {{1, 3}, {1, 0}}, {{2, 5}, {1, 0}}}; - - // perform depth-limited depth first search - const std::size_t depthLimit = 6; - std::vector nodeStack{}; - nodeStack.reserve(depthLimit); - std::stack permStack{}; - - auto initNode = HeuristicMapper::Node( - 0, 0, {0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5}, {}, 0., 0, false, true); - initNode.recalculateFixedCost(architecture, empty1Mult, multiplicity); - initNode.updateHeuristicCost(architecture, empty1Mult, multiplicity, - consideredQubits); - nodeStack.emplace_back(initNode); - permStack.emplace(perms.size()); - - while (!nodeStack.empty()) { - const auto& node = nodeStack.back(); - if (node.done) { - // check if all nodes in stack have lower or equal cost - for (const auto& prevNode : nodeStack) { - EXPECT_LE(prevNode.getTotalCost(), node.getTotalCost()); - } - } - if (node.done || nodeStack.size() >= depthLimit || permStack.top() == 0) { - nodeStack.pop_back(); - permStack.pop(); - continue; - } - --permStack.top(); - const auto perm = perms[permStack.top()]; - auto newNode = - HeuristicMapper::Node(1, 0, node.qubits, node.locations, node.swaps, - node.costFixed, node.depth + 1, false, true); - newNode.applySWAP(perm, architecture, empty1Mult, multiplicity); - newNode.updateHeuristicCost(architecture, empty1Mult, multiplicity, - consideredQubits); - nodeStack.emplace_back(newNode); - permStack.emplace(perms.size()); - } -} - TEST(Functionality, DataLoggerAfterClose) { - const std::string dataLoggingPath = "test_log/"; + const std::string dataLoggingPath = "test_log/datalogger_after_close/"; qc::QuantumComputation qc{3}; qc.x(0); Architecture arch{3, {}}; @@ -389,20 +1211,19 @@ TEST(Functionality, DataLogger) { qc.setName("test_circ"); Configuration settings{}; - settings.admissibleHeuristic = true; + settings.heuristic = Heuristic::GateCountMaxDistance; settings.layering = Layering::IndividualGates; settings.initialLayout = InitialLayout::Identity; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; - settings.lookahead = true; + settings.lookaheadHeuristic = LookaheadHeuristic::GateCountMaxDistance; settings.nrLookaheads = 1; settings.firstLookaheadFactor = 0.5; settings.lookaheadFactor = 0.9; settings.debug = true; - settings.considerFidelity = false; settings.useTeleportation = false; // setting data logging path to enable data logging - settings.dataLoggingPath = "test_log"; + settings.dataLoggingPath = "test_log/datalogger"; // remove directory at data logging path if it already exists if (std::filesystem::exists(settings.dataLoggingPath)) { @@ -488,14 +1309,13 @@ TEST(Functionality, DataLogger) { EXPECT_EQ(configJson["pre_mapping_optimizations"], settings.preMappingOptimizations); const auto& heuristicSettingsJson = configJson["settings"]; - EXPECT_EQ(heuristicSettingsJson["admissible_heuristic"], - settings.admissibleHeuristic); - EXPECT_EQ(heuristicSettingsJson["consider_fidelity"], - settings.considerFidelity); + EXPECT_EQ(heuristicSettingsJson["heuristic"], toString(settings.heuristic)); EXPECT_EQ(heuristicSettingsJson["initial_layout"], toString(settings.initialLayout)); - if (settings.lookahead) { + if (settings.lookaheadHeuristic != LookaheadHeuristic::None) { const auto& lookaheadJson = heuristicSettingsJson["lookahead"]; + EXPECT_EQ(lookaheadJson["heuristic"], + toString(settings.lookaheadHeuristic)); EXPECT_EQ(lookaheadJson["factor"], settings.lookaheadFactor); EXPECT_EQ(lookaheadJson["first_factor"], settings.firstLookaheadFactor); EXPECT_EQ(lookaheadJson["lookaheads"], settings.nrLookaheads); @@ -602,122 +1422,57 @@ TEST(Functionality, DataLogger) { EXPECT_EQ(layerJson["single_qubit_multiplicity"].size(), architecture.getNqubits()); - auto layerNodeFile = - std::ifstream(settings.dataLoggingPath + "/nodes_layer_" + - std::to_string(i) + ".csv"); - if (!layerNodeFile.is_open()) { - FAIL() << "Could not open file " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; + std::vector nodes{ + results.layerHeuristicBenchmark.at(i).generatedNodes}; + parseNodesFromDatalog(settings.dataLoggingPath, i, nodes); + + if (finalNodeId >= nodes.size() || + nodes.at(finalNodeId).id != finalNodeId) { + FAIL() << "Final solution node " << finalNodeId + << " not found in nodes of layer " << i; } - std::string line; - bool foundFinalNode = false; - std::set nodeIds; - while (std::getline(layerNodeFile, line)) { - if (line.empty()) { + auto& finalSolutionNode = nodes.at(finalNodeId); + EXPECT_EQ(layerJson["final_cost_fixed"], finalSolutionNode.costFixed); + EXPECT_EQ(layerJson["final_cost_heur"], finalSolutionNode.costHeur); + EXPECT_EQ(layerJson["final_lookahead_penalty"], + finalSolutionNode.lookaheadPenalty); + EXPECT_EQ(layerJson["final_search_depth"], finalSolutionNode.depth); + std::vector layout{}; + for (std::size_t j = 0; j < architecture.getNqubits(); ++j) { + layout.emplace_back(finalSolutionNode.qubits.at(j)); + } + std::vector> swaps{}; + swaps.reserve(finalSolutionNode.swaps.size()); + for (auto& swap : finalSolutionNode.swaps) { + swaps.emplace_back(swap.first, swap.second); + } + EXPECT_EQ(layerJson["final_layout"], layout); + EXPECT_EQ(layerJson["final_swaps"], swaps); + EXPECT_EQ(finalSolutionNode.validMapping, true); + + for (std::size_t j = 0; j < nodes.size(); ++j) { + auto& node = nodes.at(j); + if (j != node.id) { continue; } - std::string col; - std::size_t nodeId = 0; - std::size_t parentId = 0; - std::size_t depth = 0; - std::size_t isValidMapping = 0; - double costFixed = 0.; - double costHeur = 0.; - double lookaheadPenalty = 0.; - std::vector layout{}; - std::vector> swaps{}; - std::stringstream lineStream(line); - if (std::getline(lineStream, col, ';')) { - nodeId = std::stoull(col); - nodeIds.insert(nodeId); - } else { - FAIL() << "Missing value for node id in " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - parentId = std::stoull(col); - if (nodeId != 0) { - EXPECT_TRUE(nodeIds.count(parentId) > 0); - } - } else { - FAIL() << "Missing value for parent node id in " - << settings.dataLoggingPath << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - costFixed = std::stod(col); - } else { - FAIL() << "Missing value for fixed cost in " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - costHeur = std::stod(col); - } else { - FAIL() << "Missing value for heuristic cost in " - << settings.dataLoggingPath << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - lookaheadPenalty = std::stod(col); - } else { - FAIL() << "Missing value for lookahead penalty in " - << settings.dataLoggingPath << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - isValidMapping = std::stoull(col); - if (isValidMapping > 1) { - FAIL() << "Non-boolean value " << isValidMapping - << " for isValidMapping in " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; + + for (std::size_t k = 0; k < architecture.getNqubits(); ++k) { + EXPECT_GE(node.qubits.at(k), -1); + EXPECT_LT(node.qubits.at(k), qc.getNqubits()); + EXPECT_GE(node.locations.at(k), -1); + EXPECT_LT(node.locations.at(k), architecture.getNqubits()); + + if (node.qubits.at(k) >= 0) { + EXPECT_EQ( + node.locations.at(static_cast(node.qubits.at(k))), + static_cast(k)); } - } else { - FAIL() << "Missing value for isValidMapping in " - << settings.dataLoggingPath << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - depth = std::stoull(col); - } else { - FAIL() << "Missing value for depth in " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; - } - if (std::getline(lineStream, col, ';')) { - std::stringstream qubitMapBuffer(col); - std::string entry; - while (std::getline(qubitMapBuffer, entry, ',')) { - const std::int32_t qubit = std::stoi(entry); - layout.push_back(qubit); - EXPECT_TRUE(-1 <= qubit && qubit < architecture.getNqubits()); + if (node.locations.at(k) >= 0) { + EXPECT_EQ( + node.qubits.at(static_cast(node.locations.at(k))), + static_cast(k)); } - EXPECT_EQ(layout.size(), architecture.getNqubits()); - } else { - FAIL() << "Missing value for layout in " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; } - if (std::getline(lineStream, col, ';')) { - std::stringstream swapBuffer(col); - std::string entry; - while (std::getline(swapBuffer, entry, ',')) { - std::int32_t q1 = 0; - std::int32_t q2 = 0; - std::stringstream(entry) >> q1 >> q2; - EXPECT_TRUE(0 <= q1 && q1 < architecture.getNqubits()); - EXPECT_TRUE(0 <= q2 && q2 < architecture.getNqubits()); - swaps.emplace_back(q1, q2); - } - } - - if (nodeId == finalNodeId) { - foundFinalNode = true; - EXPECT_EQ(layerJson["final_cost_fixed"], costFixed); - EXPECT_EQ(layerJson["final_cost_heur"], costHeur); - EXPECT_EQ(layerJson["final_layout"], layout); - EXPECT_EQ(layerJson["final_lookahead_penalty"], lookaheadPenalty); - EXPECT_EQ(layerJson["final_search_depth"], depth); - EXPECT_EQ(layerJson["final_swaps"], swaps); - EXPECT_EQ(isValidMapping, 1); - } - } - if (!foundFinalNode) { - FAIL() << "Could not find final node in " << settings.dataLoggingPath - << "/nodes_layer_" << i << ".csv"; } } @@ -760,9 +1515,12 @@ TEST(Functionality, terminationStrategyFromString) { } TEST(Functionality, earlyTermination) { - qc::QuantumComputation qc{7}; + qc::QuantumComputation qc{7, 7}; qc.x(0); - qc.cx(qc::Control{1}, 3); + qc.cx(1, 2); + for (std::size_t i = 0; i < 7; ++i) { + qc.measure(static_cast(i), i); + } const CouplingMap cm = {{0, 1}, {1, 0}, {1, 2}, {2, 1}, {2, 3}, {3, 2}, {3, 4}, {4, 3}, {4, 5}, {5, 4}, {5, 6}, {6, 5}}; @@ -786,9 +1544,8 @@ TEST(Functionality, earlyTermination) { config.automaticLayerSplits = false; config.iterativeBidirectionalRouting = false; config.debug = true; - config.admissibleHeuristic = true; - config.considerFidelity = true; - config.lookahead = false; + config.heuristic = Heuristic::FidelityBestLocation; + config.lookaheadHeuristic = LookaheadHeuristic::None; config.earlyTerminationLimit = 4; auto mapper = std::make_unique(qc, arch); @@ -939,9 +1696,9 @@ class LayeringTest : public testing::Test { qc = qc::QuantumComputation{4, 4}; qc.x(0); qc.x(1); - qc.cx(qc::Control{0}, 1); - qc.cx(qc::Control{2}, 3); - qc.cx(qc::Control{1}, 2); + qc.cx(0, 1); + qc.cx(2, 3); + qc.cx(1, 2); qc.x(3); qc.barrier({0, 1, 2}); for (size_t i = 0; i < 3; ++i) { @@ -1041,8 +1798,8 @@ class HeuristicTest5Q : public testing::TestWithParam { ibmqLondon.loadProperties(testCalibrationDir + "ibmq_london.csv"); ibmqYorktownMapper = std::make_unique(qc, ibmqYorktown); ibmqLondonMapper = std::make_unique(qc, ibmqLondon); - settings.debug = true; settings.verbose = true; + settings.debug = true; settings.iterativeBidirectionalRouting = true; settings.iterativeBidirectionalRoutingPasses = 3; @@ -1216,7 +1973,6 @@ TEST_P(HeuristicTest20QTeleport, Teleportation) { Configuration settings{}; settings.initialLayout = InitialLayout::Dynamic; settings.debug = true; - settings.verbose = true; settings.teleportationQubits = std::min( (arch.getNqubits() - qc.getNqubits()) & ~1U, static_cast(8)); settings.teleportationSeed = std::get<0>(GetParam()); @@ -1261,10 +2017,10 @@ INSTANTIATE_TEST_SUITE_P( TEST_P(HeuristicTestFidelity, Identity) { Configuration settings{}; - settings.layering = Layering::DisjointQubits; - settings.initialLayout = InitialLayout::Identity; - settings.considerFidelity = true; - settings.lookahead = false; + settings.layering = Layering::DisjointQubits; + settings.initialLayout = InitialLayout::Identity; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; mapper->map(settings); mapper->dumpResult(GetParam() + "_heuristic_london_fidelity_identity.qasm"); mapper->printResult(std::cout); @@ -1273,10 +2029,10 @@ TEST_P(HeuristicTestFidelity, Identity) { TEST_P(HeuristicTestFidelity, Static) { Configuration settings{}; - settings.layering = Layering::DisjointQubits; - settings.initialLayout = InitialLayout::Static; - settings.considerFidelity = true; - settings.lookahead = false; + settings.layering = Layering::DisjointQubits; + settings.initialLayout = InitialLayout::Static; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; mapper->map(settings); mapper->dumpResult(GetParam() + "_heuristic_london_fidelity_static.qasm"); mapper->printResult(std::cout); @@ -1285,10 +2041,10 @@ TEST_P(HeuristicTestFidelity, Static) { TEST_P(HeuristicTestFidelity, Dynamic) { Configuration settings{}; - settings.layering = Layering::DisjointQubits; - settings.initialLayout = InitialLayout::Dynamic; - settings.considerFidelity = true; - settings.lookahead = false; + settings.layering = Layering::DisjointQubits; + settings.initialLayout = InitialLayout::Dynamic; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; mapper->map(settings); mapper->dumpResult(GetParam() + "_heuristic_london_fidelity_static.qasm"); mapper->printResult(std::cout); @@ -1297,10 +2053,10 @@ TEST_P(HeuristicTestFidelity, Dynamic) { TEST_P(HeuristicTestFidelity, NoFidelity) { Configuration settings{}; - settings.layering = Layering::DisjointQubits; - settings.initialLayout = InitialLayout::Static; - settings.considerFidelity = true; - settings.lookahead = false; + settings.layering = Layering::DisjointQubits; + settings.initialLayout = InitialLayout::Static; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; EXPECT_THROW(nonFidelityMapper->map(settings), QMAPException); } @@ -1352,15 +2108,14 @@ TEST(HeuristicTestFidelity, RemapSingleQubit) { auto mapper = std::make_unique(qc, architecture); Configuration settings{}; - settings.admissibleHeuristic = true; settings.layering = Layering::Disjoint2qBlocks; settings.initialLayout = InitialLayout::Identity; - settings.considerFidelity = true; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; settings.verbose = true; settings.swapOnFirstLayer = true; - settings.lookahead = false; mapper->map(settings); mapper->dumpResult("remap_single_qubit_mapped.qasm"); mapper->printResult(std::cout); @@ -1438,15 +2193,14 @@ TEST(HeuristicTestFidelity, QubitRideAlong) { auto mapper = std::make_unique(qc, architecture); Configuration settings{}; - settings.admissibleHeuristic = true; settings.layering = Layering::Disjoint2qBlocks; settings.initialLayout = InitialLayout::Identity; - settings.considerFidelity = true; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; settings.verbose = true; settings.swapOnFirstLayer = true; - settings.lookahead = false; mapper->map(settings); mapper->dumpResult("qubit_ride_along_mapped.qasm"); mapper->printResult(std::cout); @@ -1501,15 +2255,14 @@ TEST(HeuristicTestFidelity, SingleQubitsCompete) { auto mapper = std::make_unique(qc, architecture); Configuration settings{}; - settings.admissibleHeuristic = true; settings.layering = Layering::Disjoint2qBlocks; settings.initialLayout = InitialLayout::Identity; - settings.considerFidelity = true; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; settings.verbose = true; settings.swapOnFirstLayer = true; - settings.lookahead = false; mapper->map(settings); mapper->dumpResult("single_qubits_compete.qasm"); mapper->printResult(std::cout); @@ -1608,8 +2361,8 @@ TEST(HeuristicTestFidelity, LayerSplitting) { qc.x(5); qc.x(7); for (std::size_t i = 0; i < 5; ++i) { - qc.cx(qc::Control{3}, 0); - qc.cx(qc::Control{9}, 2); + qc.cx(3, 0); + qc.cx(9, 2); } for (size_t i = 0; i < 12; ++i) { @@ -1620,19 +2373,18 @@ TEST(HeuristicTestFidelity, LayerSplitting) { Configuration settings{}; settings.verbose = true; - settings.admissibleHeuristic = true; settings.layering = Layering::Disjoint2qBlocks; settings.initialLayout = InitialLayout::Identity; - settings.considerFidelity = true; + settings.heuristic = Heuristic::FidelityBestLocation; + settings.lookaheadHeuristic = LookaheadHeuristic::None; settings.preMappingOptimizations = false; settings.postMappingOptimizations = false; settings.swapOnFirstLayer = true; - settings.lookahead = false; settings.automaticLayerSplits = true; settings.automaticLayerSplitsNodeLimit = 1; // force splittings after 1st expanded node until layers are // unsplittable - settings.dataLoggingPath = "test_log/"; + settings.dataLoggingPath = "test_log/layer_splitting/"; mapper->map(settings); mapper->dumpResult("simple_grid_mapped.qasm"); mapper->printResult(std::cout);