| Line | Branch | Decision | Exec | Source | 
|---|---|---|---|---|
| 1 | // Copyright 2019-2022 Cambridge Quantum Computing | |||
| 2 | // | |||
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| 4 | // you may not use this file except in compliance with the License. | |||
| 5 | // You may obtain a copy of the License at | |||
| 6 | // | |||
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 | |||
| 8 | // | |||
| 9 | // Unless required by applicable law or agreed to in writing, software | |||
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, | |||
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| 12 | // See the License for the specific language governing permissions and | |||
| 13 | // limitations under the License. | |||
| 14 | ||||
| 15 | #include "ZX/Flow.hpp" | |||
| 16 | ||||
| 17 | #include "Utils/GraphHeaders.hpp" | |||
| 18 | #include "Utils/MatrixAnalysis.hpp" | |||
| 19 | ||||
| 20 | namespace tket { | |||
| 21 | ||||
| 22 | namespace zx { | |||
| 23 | ||||
| 24 | 24 | Flow::Flow( | ||
| 25 | const std::map<ZXVert, ZXVertSeqSet>& c, | |||
| 26 | 24 | const std::map<ZXVert, unsigned>& d) | ||
| 27 | 
        1/2✓ Branch 2 taken 24 times. 
          ✗ Branch 3 not taken. 
         | 
      24 | : c_(c), d_(d) {} | |
| 28 | ||||
| 29 | 366 | ZXVertSeqSet Flow::c(const ZXVert& v) const { return c_.at(v); } | ||
| 30 | ||||
| 31 | 169 | ZXVertSeqSet Flow::odd(const ZXVert& v, const ZXDiagram& diag) const { | ||
| 32 | 
        1/2✓ Branch 1 taken 169 times. 
          ✗ Branch 2 not taken. 
         | 
      169 | sequenced_map_t<ZXVert, unsigned> parities; | |
| 33 | 
        1/2✓ Branch 1 taken 169 times. 
          ✗ Branch 2 not taken. 
         | 
      169 | ZXVertSeqSet cv = c(v); | |
| 34 | 
        5/8✓ Branch 4 taken 315 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 315 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 484 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 315 times. 
          ✓ Branch 13 taken 169 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      484 | for (const ZXVert& u : cv.get<TagSeq>()) { | 
| 35 | 
        3/4✓ Branch 1 taken 315 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 8 taken 830 times. 
          ✓ Branch 9 taken 315 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      1145 | for (const ZXVert& n : diag.neighbours(u)) { | 
| 36 | 
        3/4✓ Branch 1 taken 830 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 89 times. 
          ✓ Branch 4 taken 741 times. 
         | 
      
        2/2✓ Decision 'true' taken 741 times. 
            ✓ Decision 'false' taken 89 times. 
           | 
      830 | if (diag.get_zxtype(n) == ZXType::Output) continue; | 
| 37 | sequenced_map_t<ZXVert, unsigned>::iterator found = | |||
| 38 | 
        1/2✓ Branch 2 taken 741 times. 
          ✗ Branch 3 not taken. 
         | 
      741 | parities.get<TagKey>().find(n); | |
| 39 | 
        3/4✓ Branch 3 taken 741 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 5 taken 576 times. 
          ✓ Branch 6 taken 165 times. 
         | 
      
        2/2✓ Decision 'true' taken 576 times. 
            ✓ Decision 'false' taken 165 times. 
           | 
      741 | if (found == parities.get<TagKey>().end()) { | 
| 40 | 
        1/2✓ Branch 2 taken 576 times. 
          ✗ Branch 3 not taken. 
         | 
      576 | parities.insert({n, 1}); | |
| 41 | } else { | |||
| 42 | 
        2/4✓ Branch 1 taken 165 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 5 taken 165 times. 
          ✗ Branch 6 not taken. 
         | 
      165 | parities.replace(found, {n, found->second + 1}); | |
| 43 | } | |||
| 44 | 315 | } | ||
| 45 | } | |||
| 46 | 
        1/2✓ Branch 1 taken 169 times. 
          ✗ Branch 2 not taken. 
         | 
      169 | ZXVertSeqSet odds; | |
| 47 | 
        5/8✓ Branch 4 taken 576 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 576 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 745 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 576 times. 
          ✓ Branch 13 taken 169 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      745 | for (const std::pair<ZXVert, unsigned>& p : parities.get<TagSeq>()) { | 
| 48 | 
        2/2✓ Branch 0 taken 427 times. 
          ✓ Branch 1 taken 149 times. 
         | 
      
        2/2✓ Decision 'true' taken 427 times. 
            ✓ Decision 'false' taken 149 times. 
           | 
      576 | if (p.second % 2 == 1) { | 
| 49 | 
        1/2✓ Branch 1 taken 427 times. 
          ✗ Branch 2 not taken. 
         | 
      427 | odds.insert(p.first); | |
| 50 | } | |||
| 51 | } | |||
| 52 | 338 | return odds; | ||
| 53 | 169 | } | ||
| 54 | ||||
| 55 | 758 | unsigned Flow::d(const ZXVert& v) const { return d_.at(v); } | ||
| 56 | ||||
| 57 | 24 | void Flow::verify(const ZXDiagram& diag) const { | ||
| 58 | 
        2/4✓ Branch 1 taken 24 times. 
          ✗ Branch 2 not taken. 
          ✗ Branch 3 not taken. 
          ✓ Branch 4 taken 24 times. 
         | 
      
        1/2✗ Decision 'true' not taken. 
            ✓ Decision 'false' taken 24 times. 
           | 
      24 | if (!diag.is_MBQC()) | 
| 59 | 
        0/1? Decision couldn't be analyzed. 
           | 
      ✗ | throw ZXError("Verifying a flow for a diagram that is not in MBQC form"); | |
| 60 | 24 | std::set<ZXVert> output_set; | ||
| 61 | 
        3/4✓ Branch 3 taken 24 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 9 taken 70 times. 
          ✓ Branch 10 taken 24 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      94 | for (const ZXVert& o : diag.get_boundary(ZXType::Output)) { | 
| 62 | 
        3/6✓ Branch 1 taken 70 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 70 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 70 times. 
          ✗ Branch 8 not taken. 
         | 
      70 | output_set.insert(diag.neighbours(o).at(0)); | |
| 63 | 24 | } | ||
| 64 | 
        7/8✓ Branch 2 taken 24 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 6 taken 303 times. 
          ✓ Branch 7 taken 5 times. 
          ✓ Branch 9 taken 303 times. 
          ✓ Branch 10 taken 5 times. 
          ✓ Branch 12 taken 24 times. 
          ✓ Branch 13 taken 5 times. 
         | 
      313 | BGL_FORALL_VERTICES(u, *diag.graph, ZXGraph) { | |
| 65 | 
        1/2✓ Branch 1 taken 303 times. 
          ✗ Branch 2 not taken. 
         | 
      303 | ZXType type = diag.get_zxtype(u); | |
| 66 | 
        8/10✓ Branch 1 taken 303 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 207 times. 
          ✓ Branch 4 taken 96 times. 
          ✓ Branch 7 taken 207 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 55 times. 
          ✓ Branch 11 taken 152 times. 
          ✓ Branch 12 taken 151 times. 
          ✓ Branch 13 taken 152 times. 
         | 
      
        2/2✓ Decision 'true' taken 151 times. 
            ✓ Decision 'false' taken 152 times. 
           | 
      303 | if (is_boundary_type(type) || output_set.find(u) != output_set.end()) | 
| 67 | 151 | continue; | ||
| 68 | 
        1/2✓ Branch 1 taken 152 times. 
          ✗ Branch 2 not taken. 
         | 
      152 | ZXVertSeqSet uc = c(u); | |
| 69 | 
        1/2✓ Branch 1 taken 152 times. 
          ✗ Branch 2 not taken. 
         | 
      152 | ZXVertSeqSet uodd = odd(u, diag); | |
| 70 | 
        5/8✓ Branch 4 taken 291 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 289 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 441 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 291 times. 
          ✓ Branch 13 taken 150 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      441 | for (const ZXVert& v : uc.get<TagSeq>()) { | 
| 71 | 
        1/2✓ Branch 1 taken 291 times. 
          ✗ Branch 2 not taken. 
         | 
      291 | ZXType vt = diag.get_zxtype(v); | |
| 72 | 
        12/14✓ Branch 0 taken 239 times. 
          ✓ Branch 1 taken 52 times. 
          ✓ Branch 2 taken 138 times. 
          ✓ Branch 3 taken 101 times. 
          ✓ Branch 4 taken 107 times. 
          ✓ Branch 5 taken 31 times. 
          ✓ Branch 7 taken 107 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 107 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 1 times. 
          ✓ Branch 13 taken 106 times. 
          ✓ Branch 14 taken 1 times. 
          ✓ Branch 15 taken 290 times. 
         | 
      
        2/2✓ Decision 'true' taken 1 times. 
            ✓ Decision 'false' taken 290 times. 
           | 
      291 | if (u != v && vt != ZXType::PX && vt != ZXType::PY && d(u) <= d(v)) | 
| 73 | 
        2/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      1 | throw ZXError("A qubit has an X correction in its past"); | |
| 74 | 
        8/10✓ Branch 0 taken 238 times. 
          ✓ Branch 1 taken 52 times. 
          ✓ Branch 2 taken 31 times. 
          ✓ Branch 3 taken 207 times. 
          ✓ Branch 5 taken 31 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 8 taken 31 times. 
          ✗ Branch 9 not taken. 
          ✓ Branch 10 taken 5 times. 
          ✓ Branch 11 taken 26 times. 
         | 
      
        2/2✓ Decision 'true' taken 1 times. 
            ✓ Decision 'false' taken 294 times. 
           | 
      295 | if (u != v && vt == ZXType::PY && d(u) <= d(v) && | 
| 75 | 
        6/8✓ Branch 2 taken 5 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 5 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 7 taken 1 times. 
          ✓ Branch 8 taken 4 times. 
          ✓ Branch 9 taken 1 times. 
          ✓ Branch 10 taken 289 times. 
         | 
      295 | uodd.find(v) == uodd.end()) | |
| 76 | 
        2/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      1 | throw ZXError("A past Y vertex receives an X correction"); | |
| 77 | } | |||
| 78 | 
        5/8✓ Branch 4 taken 377 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 375 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 525 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 377 times. 
          ✓ Branch 13 taken 148 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      525 | for (const ZXVert& v : uodd.get<TagSeq>()) { | 
| 79 | 
        1/2✓ Branch 1 taken 377 times. 
          ✗ Branch 2 not taken. 
         | 
      377 | ZXType vt = diag.get_zxtype(v); | |
| 80 | 
        12/14✓ Branch 0 taken 263 times. 
          ✓ Branch 1 taken 114 times. 
          ✓ Branch 2 taken 235 times. 
          ✓ Branch 3 taken 28 times. 
          ✓ Branch 4 taken 213 times. 
          ✓ Branch 5 taken 22 times. 
          ✓ Branch 7 taken 213 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 213 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 1 times. 
          ✓ Branch 13 taken 212 times. 
          ✓ Branch 14 taken 1 times. 
          ✓ Branch 15 taken 376 times. 
         | 
      
        2/2✓ Decision 'true' taken 1 times. 
            ✓ Decision 'false' taken 376 times. 
           | 
      377 | if (u != v && vt != ZXType::PY && vt != ZXType::PZ && d(u) <= d(v)) | 
| 81 | 
        2/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      1 | throw ZXError("A qubit has a Z correction in its past"); | |
| 82 | 
        14/18✓ Branch 0 taken 262 times. 
          ✓ Branch 1 taken 114 times. 
          ✓ Branch 2 taken 28 times. 
          ✓ Branch 3 taken 234 times. 
          ✓ Branch 5 taken 28 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 8 taken 28 times. 
          ✗ Branch 9 not taken. 
          ✓ Branch 10 taken 5 times. 
          ✓ Branch 11 taken 23 times. 
          ✓ Branch 14 taken 5 times. 
          ✗ Branch 15 not taken. 
          ✓ Branch 17 taken 5 times. 
          ✗ Branch 18 not taken. 
          ✓ Branch 19 taken 1 times. 
          ✓ Branch 20 taken 4 times. 
          ✓ Branch 21 taken 1 times. 
          ✓ Branch 22 taken 375 times. 
         | 
      
        2/2✓ Decision 'true' taken 1 times. 
            ✓ Decision 'false' taken 375 times. 
           | 
      376 | if (u != v && vt == ZXType::PY && d(u) <= d(v) && uc.find(v) == uc.end()) | 
| 83 | 
        2/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      1 | throw ZXError("A past Y vertex receives a Z correction"); | |
| 84 | } | |||
| 85 | 
        2/4✓ Branch 2 taken 148 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 148 times. 
          ✗ Branch 6 not taken. 
         | 
      148 | bool self_x = (uc.find(u) != uc.end()); | |
| 86 | 
        2/4✓ Branch 2 taken 148 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 148 times. 
          ✗ Branch 6 not taken. 
         | 
      148 | bool self_z = (uodd.find(u) != uodd.end()); | |
| 87 | 
        6/7✓ Branch 0 taken 79 times. 
          ✓ Branch 1 taken 20 times. 
          ✓ Branch 2 taken 20 times. 
          ✓ Branch 3 taken 12 times. 
          ✓ Branch 4 taken 5 times. 
          ✓ Branch 5 taken 12 times. 
          ✗ Branch 6 not taken. 
         | 
      148 | switch (type) { | |
| 88 | 
        1/1✓ Decision 'true' taken 79 times. 
           | 
      79 | case ZXType::XY: { | |
| 89 | 
        4/4✓ Branch 0 taken 77 times. 
          ✓ Branch 1 taken 2 times. 
          ✓ Branch 2 taken 1 times. 
          ✓ Branch 3 taken 76 times. 
         | 
      
        2/2✓ Decision 'true' taken 3 times. 
            ✓ Decision 'false' taken 76 times. 
           | 
      79 | if (self_x || !self_z) | 
| 90 | 
        2/4✓ Branch 3 taken 3 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 3 times. 
          ✗ Branch 7 not taken. 
         | 
      3 | throw ZXError("XY vertex must be corrected with a Z"); | |
| 91 | 76 | break; | ||
| 92 | } | |||
| 93 | 
        1/1✓ Decision 'true' taken 20 times. 
           | 
      20 | case ZXType::XZ: { | |
| 94 | 
        4/4✓ Branch 0 taken 18 times. 
          ✓ Branch 1 taken 2 times. 
          ✓ Branch 2 taken 1 times. 
          ✓ Branch 3 taken 17 times. 
         | 
      
        2/2✓ Decision 'true' taken 3 times. 
            ✓ Decision 'false' taken 17 times. 
           | 
      20 | if (!self_x || !self_z) | 
| 95 | 
        2/4✓ Branch 3 taken 3 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 3 times. 
          ✗ Branch 7 not taken. 
         | 
      3 | throw ZXError("XZ vertex must be corrected with a Y"); | |
| 96 | 17 | break; | ||
| 97 | } | |||
| 98 | 
        1/1✓ Decision 'true' taken 20 times. 
           | 
      20 | case ZXType::YZ: { | |
| 99 | 
        4/4✓ Branch 0 taken 18 times. 
          ✓ Branch 1 taken 2 times. 
          ✓ Branch 2 taken 1 times. 
          ✓ Branch 3 taken 17 times. 
         | 
      
        2/2✓ Decision 'true' taken 3 times. 
            ✓ Decision 'false' taken 17 times. 
           | 
      20 | if (!self_x || self_z) | 
| 100 | 
        2/4✓ Branch 3 taken 3 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 3 times. 
          ✗ Branch 7 not taken. 
         | 
      3 | throw ZXError("YZ vertex must be corrected with an X"); | |
| 101 | 17 | break; | ||
| 102 | } | |||
| 103 | 
        1/1✓ Decision 'true' taken 12 times. 
           | 
      12 | case ZXType::PX: { | |
| 104 | 
        4/6✓ Branch 0 taken 2 times. 
          ✓ Branch 1 taken 10 times. 
          ✓ Branch 5 taken 2 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 8 taken 2 times. 
          ✗ Branch 9 not taken. 
         | 
      
        2/2✓ Decision 'true' taken 10 times. 
            ✓ Decision 'false' taken 2 times. 
           | 
      12 | if (!self_z) throw ZXError("PX vertex must be corrected with a Y or Z"); | 
| 105 | 10 | break; | ||
| 106 | } | |||
| 107 | 
        1/1✓ Decision 'true' taken 5 times. 
           | 
      5 | case ZXType::PY: { | |
| 108 | 
        2/2✓ Branch 0 taken 2 times. 
          ✓ Branch 1 taken 3 times. 
         | 
      
        2/2✓ Decision 'true' taken 2 times. 
            ✓ Decision 'false' taken 3 times. 
           | 
      5 | if (self_x == self_z) | 
| 109 | 
        2/4✓ Branch 3 taken 2 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 2 times. 
          ✗ Branch 7 not taken. 
         | 
      2 | throw ZXError("PY vertex must be corrected with an X or Z"); | |
| 110 | 3 | break; | ||
| 111 | } | |||
| 112 | 
        1/1✓ Decision 'true' taken 12 times. 
           | 
      12 | case ZXType::PZ: { | |
| 113 | 
        2/2✓ Branch 0 taken 2 times. 
          ✓ Branch 1 taken 10 times. 
         | 
      
        2/2✓ Decision 'true' taken 2 times. 
            ✓ Decision 'false' taken 10 times. 
           | 
      12 | if (!self_x) | 
| 114 | 
        2/4✓ Branch 3 taken 2 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 2 times. 
          ✗ Branch 7 not taken. 
         | 
      2 | throw ZXError("PZ vertex must be corrected with an X or Y"); | |
| 115 | 10 | break; | ||
| 116 | } | |||
| 117 | 
        0/1✗ Decision 'true' not taken. 
           | 
      ✗ | default: | |
| 118 | 
        0/1? Decision couldn't be analyzed. 
           | 
      ✗ | throw ZXError("Invalid ZXType for MBQC diagram"); | |
| 119 | } | |||
| 120 | 171 | } | ||
| 121 | 24 | } | ||
| 122 | ||||
| 123 | 2 | void Flow::focus(const ZXDiagram& diag) { | ||
| 124 | 2 | std::set<ZXVert> output_set; | ||
| 125 | 
        3/4✓ Branch 3 taken 2 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 9 taken 5 times. 
          ✓ Branch 10 taken 2 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      7 | for (const ZXVert& o : diag.get_boundary(ZXType::Output)) { | 
| 126 | 
        3/6✓ Branch 1 taken 5 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 5 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 5 times. 
          ✗ Branch 8 not taken. 
         | 
      5 | output_set.insert(diag.neighbours(o).at(0)); | |
| 127 | 2 | } | ||
| 128 | 2 | std::map<unsigned, ZXVertVec> order; | ||
| 129 | 
        2/2✓ Branch 5 taken 22 times. 
          ✓ Branch 6 taken 2 times. 
         | 
      
        2/2✓ Decision 'true' taken 22 times. 
            ✓ Decision 'false' taken 2 times. 
           | 
      24 | for (const std::pair<const ZXVert, unsigned>& p : d_) { | 
| 130 | 
        1/2✓ Branch 1 taken 22 times. 
          ✗ Branch 2 not taken. 
         | 
      22 | auto found = order.find(p.second); | |
| 131 | 
        2/2✓ Branch 2 taken 11 times. 
          ✓ Branch 3 taken 11 times. 
         | 
      
        2/2✓ Decision 'true' taken 11 times. 
            ✓ Decision 'false' taken 11 times. 
           | 
      22 | if (found == order.end()) | 
| 132 | 
        3/6✓ Branch 2 taken 11 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 11 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 8 taken 11 times. 
          ✗ Branch 9 not taken. 
         | 
      11 | order.insert({p.second, {p.first}}); | |
| 133 | else | |||
| 134 | 
        1/2✓ Branch 2 taken 11 times. 
          ✗ Branch 3 not taken. 
         | 
      11 | found->second.push_back(p.first); | |
| 135 | } | |||
| 136 | ||||
| 137 | 
        2/2✓ Branch 5 taken 11 times. 
          ✓ Branch 6 taken 2 times. 
         | 
      
        2/2✓ Decision 'true' taken 11 times. 
            ✓ Decision 'false' taken 2 times. 
           | 
      13 | for (const std::pair<const unsigned, ZXVertVec>& p : order) { | 
| 138 | 
        2/2✓ Branch 5 taken 22 times. 
          ✓ Branch 6 taken 11 times. 
         | 
      
        2/2✓ Decision 'true' taken 22 times. 
            ✓ Decision 'false' taken 11 times. 
           | 
      33 | for (const ZXVert& u : p.second) { | 
| 139 | 
        3/4✓ Branch 2 taken 22 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 5 times. 
          ✓ Branch 6 taken 17 times. 
         | 
      
        2/2✓ Decision 'true' taken 17 times. 
            ✓ Decision 'false' taken 5 times. 
           | 
      22 | if (output_set.find(u) != output_set.end()) continue; | 
| 140 | 
        1/2✓ Branch 1 taken 17 times. 
          ✗ Branch 2 not taken. 
         | 
      17 | ZXVertSeqSet uc = c(u); | |
| 141 | 
        1/2✓ Branch 1 taken 17 times. 
          ✗ Branch 2 not taken. 
         | 
      17 | ZXVertSeqSet uodd = odd(u, diag); | |
| 142 | 
        1/2✓ Branch 1 taken 17 times. 
          ✗ Branch 2 not taken. 
         | 
      17 | sequenced_map_t<ZXVert, unsigned> parities; | |
| 143 | 
        6/10✓ Branch 4 taken 24 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 8 taken 24 times. 
          ✗ Branch 9 not taken. 
          ✓ Branch 11 taken 24 times. 
          ✗ Branch 12 not taken. 
          ✓ Branch 14 taken 41 times. 
          ✗ Branch 15 not taken. 
          ✓ Branch 16 taken 24 times. 
          ✓ Branch 17 taken 17 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      41 | for (const ZXVert& v : uc.get<TagSeq>()) parities.insert({v, 1}); | 
| 144 | 
        5/8✓ Branch 4 taken 24 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 24 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 41 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 24 times. 
          ✓ Branch 13 taken 17 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      41 | for (const ZXVert& v : uc.get<TagSeq>()) { | 
| 145 | 
        2/2✓ Branch 0 taken 3 times. 
          ✓ Branch 1 taken 21 times. 
         | 
      
        2/2✓ Decision 'true' taken 21 times. 
            ✓ Decision 'false' taken 3 times. 
           | 
      24 | if (v == u) continue; | 
| 146 | 
        1/2✓ Branch 1 taken 21 times. 
          ✗ Branch 2 not taken. 
         | 
      21 | ZXType vtype = diag.get_zxtype(v); | |
| 147 | 
        3/4✓ Branch 0 taken 2 times. 
          ✓ Branch 1 taken 9 times. 
          ✓ Branch 2 taken 2 times. 
          ✗ Branch 3 not taken. 
         | 
      
        1/2✗ Decision 'true' not taken. 
            ✓ Decision 'false' taken 11 times. 
           | 
      11 | if ((vtype != ZXType::XY && vtype != ZXType::PX && | 
| 148 | 
        4/4✓ Branch 0 taken 11 times. 
          ✓ Branch 1 taken 10 times. 
          ✓ Branch 2 taken 2 times. 
          ✓ Branch 3 taken 19 times. 
         | 
      34 | vtype != ZXType::PY) || | |
| 149 | 
        4/8✓ Branch 2 taken 2 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 2 times. 
          ✗ Branch 6 not taken. 
          ✗ Branch 7 not taken. 
          ✓ Branch 8 taken 2 times. 
          ✗ Branch 9 not taken. 
          ✓ Branch 10 taken 21 times. 
         | 
      23 | (vtype == ZXType::PY && uodd.find(v) == uodd.end())) { | |
| 150 | ✗ | ZXVertSeqSet cv = c(v); | ||
| 151 | 
        0/1? Decision couldn't be analyzed. 
           | 
      ✗ | for (const ZXVert& w : cv.get<TagSeq>()) { | |
| 152 | ✗ | auto found = parities.get<TagKey>().find(w); | ||
| 153 | 
        0/2✗ Decision 'true' not taken. 
            ✗ Decision 'false' not taken. 
           | 
      ✗ | if (found == parities.get<TagKey>().end()) | |
| 154 | ✗ | parities.insert({w, 1}); | ||
| 155 | else | |||
| 156 | ✗ | parities.replace(found, {w, found->second + 1}); | ||
| 157 | } | |||
| 158 | } | |||
| 159 | } | |||
| 160 | 
        5/8✓ Branch 4 taken 39 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 39 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 56 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 39 times. 
          ✓ Branch 13 taken 17 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      56 | for (const ZXVert& v : uodd.get<TagSeq>()) { | 
| 161 | 
        2/2✓ Branch 0 taken 16 times. 
          ✓ Branch 1 taken 23 times. 
         | 
      
        2/2✓ Decision 'true' taken 23 times. 
            ✓ Decision 'false' taken 16 times. 
           | 
      39 | if (v == u) continue; | 
| 162 | 
        1/2✓ Branch 1 taken 23 times. 
          ✗ Branch 2 not taken. 
         | 
      23 | ZXType vtype = diag.get_zxtype(v); | |
| 163 | 
        4/6✓ Branch 2 taken 23 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 18 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 7 taken 16 times. 
          ✓ Branch 8 taken 2 times. 
         | 
      
        2/2✓ Decision 'true' taken 12 times. 
            ✓ Decision 'false' taken 29 times. 
           | 
      41 | if ((output_set.find(v) == output_set.end() && vtype != ZXType::XZ && | 
| 164 | 
        4/4✓ Branch 0 taken 14 times. 
          ✓ Branch 1 taken 2 times. 
          ✓ Branch 2 taken 2 times. 
          ✓ Branch 3 taken 12 times. 
         | 
      16 | vtype != ZXType::YZ && vtype != ZXType::PY && | |
| 165 | 
        4/4✓ Branch 0 taken 18 times. 
          ✓ Branch 1 taken 5 times. 
          ✓ Branch 2 taken 2 times. 
          ✓ Branch 3 taken 9 times. 
         | 
      43 | vtype != ZXType::PZ) || | |
| 166 | 
        5/8✓ Branch 2 taken 2 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 2 times. 
          ✗ Branch 6 not taken. 
          ✗ Branch 7 not taken. 
          ✓ Branch 8 taken 2 times. 
          ✓ Branch 9 taken 12 times. 
          ✓ Branch 10 taken 11 times. 
         | 
      25 | (vtype == ZXType::PY && uc.find(v) == uc.end())) { | |
| 167 | 
        1/2✓ Branch 1 taken 12 times. 
          ✗ Branch 2 not taken. 
         | 
      12 | ZXVertSeqSet cv = c(v); | |
| 168 | 
        5/8✓ Branch 4 taken 22 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 22 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 34 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 22 times. 
          ✓ Branch 13 taken 12 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      34 | for (const ZXVert& w : cv.get<TagSeq>()) { | 
| 169 | 
        1/2✓ Branch 2 taken 22 times. 
          ✗ Branch 3 not taken. 
         | 
      22 | auto found = parities.get<TagKey>().find(w); | |
| 170 | 
        3/4✓ Branch 3 taken 22 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 5 taken 21 times. 
          ✓ Branch 6 taken 1 times. 
         | 
      
        2/2✓ Decision 'true' taken 21 times. 
            ✓ Decision 'false' taken 1 times. 
           | 
      22 | if (found == parities.get<TagKey>().end()) | 
| 171 | 
        1/2✓ Branch 2 taken 21 times. 
          ✗ Branch 3 not taken. 
         | 
      21 | parities.insert({w, 1}); | |
| 172 | else | |||
| 173 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 5 taken 1 times. 
          ✗ Branch 6 not taken. 
         | 
      1 | parities.replace(found, {w, found->second + 1}); | |
| 174 | } | |||
| 175 | 12 | } | ||
| 176 | } | |||
| 177 | 
        1/2✓ Branch 1 taken 17 times. 
          ✗ Branch 2 not taken. 
         | 
      17 | ZXVertSeqSet new_c; | |
| 178 | 
        5/8✓ Branch 4 taken 45 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 8 taken 45 times. 
          ✗ Branch 9 not taken. 
          ✓ Branch 11 taken 62 times. 
          ✗ Branch 12 not taken. 
          ✓ Branch 13 taken 45 times. 
          ✓ Branch 14 taken 17 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      62 | for (const std::pair<const ZXVert, unsigned> p : parities.get<TagSeq>()) { | 
| 179 | 
        3/4✓ Branch 0 taken 44 times. 
          ✓ Branch 1 taken 1 times. 
          ✓ Branch 3 taken 44 times. 
          ✗ Branch 4 not taken. 
         | 
      
        2/2✓ Decision 'true' taken 17 times. 
            ✓ Decision 'false' taken 28 times. 
           | 
      45 | if (p.second % 2 == 1) new_c.insert(p.first); | 
| 180 | } | |||
| 181 | 
        2/4✓ Branch 1 taken 17 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 17 times. 
          ✗ Branch 5 not taken. 
         | 
      17 | c_.at(u) = new_c; | |
| 182 | 17 | } | ||
| 183 | } | |||
| 184 | 2 | } | ||
| 185 | ||||
| 186 | 1 | Flow Flow::identify_causal_flow(const ZXDiagram& diag) { | ||
| 187 | // Check diagram has the expected form for causal flow | |||
| 188 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✗ Branch 3 not taken. 
          ✓ Branch 4 taken 1 times. 
         | 
      
        1/2✗ Decision 'true' not taken. 
            ✓ Decision 'false' taken 1 times. 
           | 
      1 | if (!diag.is_MBQC()) | 
| 189 | ✗ | throw ZXError("ZXDiagram must be in MBQC form to identify causal flow"); | ||
| 190 | 1 | std::set<ZXVert> input_set; | ||
| 191 | 
        3/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 9 taken 2 times. 
          ✓ Branch 10 taken 1 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      3 | for (const ZXVert& i : diag.get_boundary(ZXType::Input)) { | 
| 192 | 
        3/6✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 2 times. 
          ✗ Branch 8 not taken. 
         | 
      2 | input_set.insert(diag.neighbours(i).at(0)); | |
| 193 | 1 | } | ||
| 194 | 1 | std::set<ZXVert> output_set; | ||
| 195 | 
        3/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 9 taken 2 times. 
          ✓ Branch 10 taken 1 times. 
         | 
      
        0/1? Decision couldn't be analyzed. 
           | 
      3 | for (const ZXVert& o : diag.get_boundary(ZXType::Output)) { | 
| 196 | 
        3/6✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 2 times. 
          ✗ Branch 8 not taken. 
         | 
      2 | output_set.insert(diag.neighbours(o).at(0)); | |
| 197 | 1 | } | ||
| 198 | 
        7/8✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 6 taken 14 times. 
          ✓ Branch 7 taken 1 times. 
          ✓ Branch 9 taken 14 times. 
          ✓ Branch 10 taken 1 times. 
          ✓ Branch 12 taken 1 times. 
          ✓ Branch 13 taken 1 times. 
         | 
      16 | BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) { | |
| 199 | 
        1/2✓ Branch 1 taken 14 times. 
          ✗ Branch 2 not taken. 
         | 
      14 | ZXType vtype = diag.get_zxtype(v); | |
| 200 | 
        8/12✓ Branch 1 taken 14 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 10 times. 
          ✓ Branch 4 taken 4 times. 
          ✓ Branch 7 taken 10 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 8 times. 
          ✓ Branch 11 taken 2 times. 
          ✗ Branch 12 not taken. 
          ✓ Branch 13 taken 8 times. 
          ✗ Branch 14 not taken. 
          ✓ Branch 15 taken 14 times. 
         | 
      14 | if (!is_boundary_type(vtype) && output_set.find(v) == output_set.end() && | |
| 201 | vtype != ZXType::XY) | |||
| 202 | ✗ | throw ZXError( | ||
| 203 | ✗ | "Causal flow is only defined when all measured vertices are XY"); | ||
| 204 | } | |||
| 205 | ||||
| 206 | // solved contains all vertices for which we have found corrections | |||
| 207 | 
        1/2✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
         | 
      1 | ZXVertSeqSet solved; | |
| 208 | // correctors are those vertices that have been solved but are not yet | |||
| 209 | // fl.c(u) for some u | |||
| 210 | 
        1/2✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
         | 
      1 | ZXVertSeqSet correctors; | |
| 211 | // past[v] is undefined if v is not yet solved | |||
| 212 | // past[v] is the number of neighbours of v that are still unsolved | |||
| 213 | // When past[v] drops to 1, we can correct the unsolved vertex using an X on | |||
| 214 | // v and Z on all of its other neighbours | |||
| 215 | 1 | std::map<ZXVert, unsigned> past; | ||
| 216 | 
        1/2✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
         | 
      2 | Flow fl{{}, {}}; | |
| 217 | ||||
| 218 | // Outputs are trivially solved | |||
| 219 | 
        3/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 10 taken 2 times. 
          ✓ Branch 11 taken 1 times. 
         | 
      3 | for (const ZXVert& o : diag.get_boundary(ZXType::Output)) { | |
| 220 | // ZX Diagrams requires each output to have a unique edge to another vertex | |||
| 221 | 
        2/4✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
         | 
      2 | ZXVert n = diag.neighbours(o).at(0); | |
| 222 | 
        2/4✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
         | 
      2 | past[n] = diag.degree(n) - 1; | |
| 223 | 
        1/2✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
         | 
      2 | solved.insert(o); | |
| 224 | 
        1/2✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
         | 
      2 | solved.insert(n); | |
| 225 | 
        3/6✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 2 times. 
          ✗ Branch 8 not taken. 
         | 
      2 | fl.c_.insert({n, {}}); | |
| 226 | 
        1/2✓ Branch 2 taken 2 times. 
          ✗ Branch 3 not taken. 
         | 
      2 | fl.d_.insert({n, 0}); | |
| 227 | // Add output to correctors if it is not an input | |||
| 228 | 
        3/6✓ Branch 2 taken 2 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 2 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 8 taken 2 times. 
          ✗ Branch 9 not taken. 
         | 
      2 | if (input_set.find(n) == input_set.end()) correctors.insert(n); | |
| 229 | 1 | } | ||
| 230 | ||||
| 231 | 1 | unsigned depth = 1; | ||
| 232 | ||||
| 233 | do { | |||
| 234 | 
        1/2✓ Branch 1 taken 6 times. 
          ✗ Branch 2 not taken. 
         | 
      6 | ZXVertSeqSet new_correctors; | |
| 235 | 
        5/8✓ Branch 4 taken 8 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 8 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 10 taken 14 times. 
          ✗ Branch 11 not taken. 
          ✓ Branch 12 taken 8 times. 
          ✓ Branch 13 taken 6 times. 
         | 
      14 | for (const ZXVert& v : correctors.get<TagSeq>()) { | |
| 236 | // Determine whether |N(v) cap unsolved| == 1 to find u | |||
| 237 | ZXVert u; | |||
| 238 | 8 | unsigned n_found = 0; | ||
| 239 | 
        3/4✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 8 taken 18 times. 
          ✓ Branch 9 taken 8 times. 
         | 
      26 | for (const ZXVert& vn : diag.neighbours(v)) { | |
| 240 | 
        4/6✓ Branch 2 taken 18 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 18 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 7 taken 8 times. 
          ✓ Branch 8 taken 10 times. 
         | 
      18 | if (solved.find(vn) == solved.end()) { | |
| 241 | 8 | u = vn; | ||
| 242 | 8 | ++n_found; | ||
| 243 | } | |||
| 244 | 8 | } | ||
| 245 | 
        1/2✗ Branch 0 not taken. 
          ✓ Branch 1 taken 8 times. 
         | 
      8 | if (n_found != 1) continue; | |
| 246 | ||||
| 247 | // Can correct u by firing stabilizer of v | |||
| 248 | 
        4/8✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 5 taken 8 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 8 taken 8 times. 
          ✗ Branch 9 not taken. 
          ✓ Branch 11 taken 8 times. 
          ✗ Branch 12 not taken. 
         | 
      8 | fl.c_.insert({u, {v}}); | |
| 249 | 
        1/2✓ Branch 2 taken 8 times. 
          ✗ Branch 3 not taken. 
         | 
      8 | fl.d_.insert({u, depth}); | |
| 250 | 
        1/2✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
         | 
      8 | solved.insert(u); | |
| 251 | ||||
| 252 | // Determine any new correctors | |||
| 253 | 8 | n_found = 0; | ||
| 254 | 8 | bool in = false; | ||
| 255 | 
        3/4✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 8 taken 20 times. 
          ✓ Branch 9 taken 8 times. 
         | 
      28 | for (const ZXVert& un : diag.neighbours(u)) { | |
| 256 | 
        3/4✓ Branch 1 taken 20 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 2 times. 
          ✓ Branch 4 taken 18 times. 
         | 
      20 | if (diag.get_zxtype(un) == ZXType::Input) { | |
| 257 | 2 | in = true; | ||
| 258 | 
        1/2✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
         | 
      2 | solved.insert(un); | |
| 259 | 2 | continue; | ||
| 260 | } | |||
| 261 | 
        4/6✓ Branch 2 taken 18 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 18 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 7 taken 8 times. 
          ✓ Branch 8 taken 10 times. 
         | 
      18 | if (solved.find(un) == solved.end()) { | |
| 262 | 8 | ++n_found; | ||
| 263 | } | |||
| 264 | // Another neighbour of un has been solved, so check if it can now | |||
| 265 | // correct something | |||
| 266 | 
        1/2✓ Branch 1 taken 18 times. 
          ✗ Branch 2 not taken. 
         | 
      18 | auto it = past.find(un); | |
| 267 | 
        5/6✓ Branch 2 taken 8 times. 
          ✓ Branch 3 taken 10 times. 
          ✓ Branch 5 taken 8 times. 
          ✗ Branch 6 not taken. 
          ✓ Branch 7 taken 8 times. 
          ✓ Branch 8 taken 10 times. 
         | 
      18 | if (it != past.end() && it->second > 0) { | |
| 268 | 8 | --it->second; | ||
| 269 | 
        1/4✗ Branch 1 not taken. 
          ✓ Branch 2 taken 8 times. 
          ✗ Branch 4 not taken. 
          ✗ Branch 5 not taken. 
         | 
      8 | if (it->second == 1) new_correctors.insert(un); | |
| 270 | } | |||
| 271 | 8 | } | ||
| 272 | // u is a new corrector if u notin I and |N(u) cap unsolved| == 1 | |||
| 273 | 
        2/2✓ Branch 0 taken 6 times. 
          ✓ Branch 1 taken 2 times. 
         | 
      8 | if (!in) { | |
| 274 | 
        1/2✓ Branch 2 taken 6 times. 
          ✗ Branch 3 not taken. 
         | 
      6 | past.insert({u, n_found}); | |
| 275 | 
        2/4✓ Branch 0 taken 6 times. 
          ✗ Branch 1 not taken. 
          ✓ Branch 3 taken 6 times. 
          ✗ Branch 4 not taken. 
         | 
      6 | if (n_found == 1) new_correctors.insert(u); | |
| 276 | } | |||
| 277 | } | |||
| 278 | 
        1/2✓ Branch 1 taken 6 times. 
          ✗ Branch 2 not taken. 
         | 
      6 | correctors = new_correctors; | |
| 279 | 6 | ++depth; | ||
| 280 | 
        2/2✓ Branch 2 taken 5 times. 
          ✓ Branch 3 taken 1 times. 
         | 
      6 | } while (!correctors.empty()); | |
| 281 | 
        2/4✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
          ✗ Branch 4 not taken. 
          ✓ Branch 5 taken 1 times. 
         | 
      1 | if (solved.size() != diag.n_vertices()) | |
| 282 | ✗ | throw ZXError("ZXDiagram does not have causal flow"); | ||
| 283 | 2 | return fl; | ||
| 284 | 1 | } | ||
| 285 | ||||
| 286 | 51 | std::map<ZXVert, ZXVertSeqSet> Flow::gauss_solve_correctors( | ||
| 287 | const ZXDiagram& diag, const boost::bimap<ZXVert, unsigned>& correctors, | |||
| 288 | const boost::bimap<ZXVert, unsigned>& preserve, const ZXVertVec& to_solve, | |||
| 289 | const boost::bimap<ZXVert, unsigned>& ys) { | |||
| 290 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      51 | unsigned n_correctors = correctors.size(); | |
| 291 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      51 | unsigned n_preserve = preserve.size(); | |
| 292 | 51 | unsigned n_to_solve = to_solve.size(); | ||
| 293 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      51 | unsigned n_ys = ys.size(); | |
| 294 | 
        2/4✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 51 times. 
          ✗ Branch 5 not taken. 
         | 
      51 | MatrixXb mat = MatrixXb::Zero(n_preserve + n_ys, n_correctors + n_to_solve); | |
| 295 | // Build adjacency matrix | |||
| 296 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      102 | for (boost::bimap<ZXVert, unsigned>::const_iterator it = correctors.begin(), | |
| 297 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      51 | end = correctors.end(); | |
| 298 | 
        4/6✓ Branch 1 taken 288 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 339 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 288 times. 
          ✓ Branch 7 taken 51 times. 
         | 
      339 | it != end; ++it) { | |
| 299 | 
        4/6✓ Branch 1 taken 288 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 288 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 11 taken 809 times. 
          ✓ Branch 12 taken 288 times. 
         | 
      1097 | for (const ZXVert& n : diag.neighbours(it->left)) { | |
| 300 | 
        1/2✓ Branch 1 taken 809 times. 
          ✗ Branch 2 not taken. 
         | 
      809 | auto in_past = preserve.left.find(n); | |
| 301 | 
        4/6✓ Branch 1 taken 809 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 809 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 302 times. 
          ✓ Branch 7 taken 507 times. 
         | 
      809 | if (in_past != preserve.left.end()) { | |
| 302 | 
        3/6✓ Branch 1 taken 302 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 302 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 302 times. 
          ✗ Branch 8 not taken. 
         | 
      302 | mat(in_past->second, it->right) = true; | |
| 303 | } else { | |||
| 304 | 
        1/2✓ Branch 1 taken 507 times. 
          ✗ Branch 2 not taken. 
         | 
      507 | auto in_ys = ys.left.find(n); | |
| 305 | 
        4/6✓ Branch 1 taken 507 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 507 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 2 times. 
          ✓ Branch 7 taken 505 times. 
         | 
      507 | if (in_ys != ys.left.end()) { | |
| 306 | 
        3/6✓ Branch 1 taken 2 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 2 times. 
          ✗ Branch 8 not taken. 
         | 
      2 | mat(n_preserve + in_ys->second, it->right) = true; | |
| 307 | } | |||
| 308 | } | |||
| 309 | 288 | } | ||
| 310 | } | |||
| 311 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      102 | for (boost::bimap<ZXVert, unsigned>::const_iterator it = ys.begin(), | |
| 312 | 
        1/2✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
         | 
      51 | end = ys.end(); | |
| 313 | 
        4/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 52 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✓ Branch 7 taken 51 times. 
         | 
      52 | it != end; ++it) { | |
| 314 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | auto found = correctors.left.find(it->left); | |
| 315 | 
        3/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      1 | if (found != correctors.left.end()) | |
| 316 | 
        3/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 1 times. 
          ✗ Branch 8 not taken. 
         | 
      1 | mat(n_preserve + it->right, found->second) = true; | |
| 317 | } | |||
| 318 | // Add rhs | |||
| 319 | 
        2/2✓ Branch 0 taken 255 times. 
          ✓ Branch 1 taken 51 times. 
         | 
      306 | for (unsigned i = 0; i < n_to_solve; ++i) { | |
| 320 | 
        1/2✓ Branch 1 taken 255 times. 
          ✗ Branch 2 not taken. 
         | 
      255 | ZXVert v = to_solve.at(i); | |
| 321 | 
        5/7✓ Branch 1 taken 255 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 219 times. 
          ✓ Branch 4 taken 3 times. 
          ✓ Branch 5 taken 32 times. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      255 | switch (diag.get_zxtype(v)) { | |
| 322 | 219 | case ZXType::XY: | ||
| 323 | case ZXType::PX: { | |||
| 324 | 
        2/4✓ Branch 1 taken 219 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 219 times. 
          ✗ Branch 5 not taken. 
         | 
      219 | mat(preserve.left.at(v), n_correctors + i) = true; | |
| 325 | 219 | break; | ||
| 326 | } | |||
| 327 | 3 | case ZXType::XZ: { | ||
| 328 | 
        2/4✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 3 times. 
          ✗ Branch 5 not taken. 
         | 
      3 | mat(preserve.left.at(v), n_correctors + i) = true; | |
| 329 | } | |||
| 330 | // fall through | |||
| 331 | 35 | case ZXType::YZ: | ||
| 332 | case ZXType::PZ: { | |||
| 333 | 
        3/4✓ Branch 1 taken 35 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 8 taken 83 times. 
          ✓ Branch 9 taken 35 times. 
         | 
      118 | for (const ZXVert& n : diag.neighbours(v)) { | |
| 334 | 
        1/2✓ Branch 1 taken 83 times. 
          ✗ Branch 2 not taken. 
         | 
      83 | auto found = preserve.left.find(n); | |
| 335 | 
        4/6✓ Branch 1 taken 83 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 83 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 58 times. 
          ✓ Branch 7 taken 25 times. 
         | 
      83 | if (found != preserve.left.end()) | |
| 336 | 
        2/4✓ Branch 1 taken 58 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 58 times. 
          ✗ Branch 5 not taken. 
         | 
      58 | mat(found->second, n_correctors + i) = true; | |
| 337 | else { | |||
| 338 | 
        1/2✓ Branch 1 taken 25 times. 
          ✗ Branch 2 not taken. 
         | 
      25 | found = ys.left.find(n); | |
| 339 | 
        4/6✓ Branch 1 taken 25 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 25 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✓ Branch 7 taken 24 times. 
         | 
      25 | if (found != ys.left.end()) | |
| 340 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | mat(n_preserve + found->second, n_correctors + i) = true; | |
| 341 | } | |||
| 342 | 35 | } | ||
| 343 | 35 | break; | ||
| 344 | } | |||
| 345 | 1 | case ZXType::PY: { | ||
| 346 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | mat(n_preserve + ys.left.at(v), n_correctors + i) = true; | |
| 347 | 1 | break; | ||
| 348 | } | |||
| 349 | ✗ | default: { | ||
| 350 | ✗ | throw ZXError( | ||
| 351 | ✗ | "Internal error in flow identification: non-MBQC vertex found"); | ||
| 352 | } | |||
| 353 | } | |||
| 354 | } | |||
| 355 | ||||
| 356 | // Gaussian elimination | |||
| 357 | std::vector<std::pair<unsigned, unsigned>> row_ops = | |||
| 358 | gaussian_elimination_row_ops( | |||
| 359 | 
        3/6✓ Branch 1 taken 51 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 51 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 51 times. 
          ✗ Branch 8 not taken. 
         | 
      102 | mat.block(0, 0, n_preserve + n_ys, n_correctors)); | |
| 360 | 
        2/2✓ Branch 5 taken 312 times. 
          ✓ Branch 6 taken 51 times. 
         | 
      363 | for (const std::pair<unsigned, unsigned>& op : row_ops) { | |
| 361 | 
        2/2✓ Branch 0 taken 3811 times. 
          ✓ Branch 1 taken 312 times. 
         | 
      4123 | for (unsigned j = 0; j < n_correctors + n_to_solve; ++j) { | |
| 362 | 
        2/4✓ Branch 1 taken 3811 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 3811 times. 
          ✗ Branch 5 not taken. 
         | 
      3811 | mat(op.second, j) ^= mat(op.first, j); | |
| 363 | } | |||
| 364 | } | |||
| 365 | ||||
| 366 | // Back substitution | |||
| 367 | // For each row i, pick a corrector j for which mat(i,j) == true, else | |||
| 368 | // determine that row i has zero lhs | |||
| 369 | 102 | std::map<unsigned, ZXVert> row_corrector; | ||
| 370 | 
        2/2✓ Branch 0 taken 254 times. 
          ✓ Branch 1 taken 51 times. 
         | 
      305 | for (unsigned i = 0; i < n_preserve + n_ys; ++i) { | |
| 371 | 
        2/2✓ Branch 0 taken 1132 times. 
          ✓ Branch 1 taken 77 times. 
         | 
      1209 | for (unsigned j = 0; j < n_correctors; ++j) { | |
| 372 | 
        3/4✓ Branch 1 taken 1132 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 177 times. 
          ✓ Branch 4 taken 955 times. 
         | 
      1132 | if (mat(i, j)) { | |
| 373 | 
        2/4✓ Branch 1 taken 177 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 5 taken 177 times. 
          ✗ Branch 6 not taken. 
         | 
      177 | row_corrector.insert({i, correctors.right.at(j)}); | |
| 374 | 177 | break; | ||
| 375 | } | |||
| 376 | } | |||
| 377 | } | |||
| 378 | // For each past i, scan down column of rhs and for each mat(j,CI+i) == true, | |||
| 379 | // add corrector from row j or try next i if row j has zero lhs | |||
| 380 | 51 | std::map<ZXVert, ZXVertSeqSet> solved_flow; | ||
| 381 | 
        2/2✓ Branch 0 taken 255 times. 
          ✓ Branch 1 taken 51 times. 
         | 
      306 | for (unsigned i = 0; i < n_to_solve; ++i) { | |
| 382 | 255 | bool fail = false; | ||
| 383 | 
        1/2✓ Branch 1 taken 255 times. 
          ✗ Branch 2 not taken. 
         | 
      255 | ZXVertSeqSet c_i; | |
| 384 | 
        2/2✓ Branch 0 taken 1618 times. 
          ✓ Branch 1 taken 146 times. 
         | 
      1764 | for (unsigned j = 0; j < n_preserve + n_ys; ++j) { | |
| 385 | 
        3/4✓ Branch 1 taken 1618 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 462 times. 
          ✓ Branch 4 taken 1156 times. 
         | 
      1618 | if (mat(j, n_correctors + i)) { | |
| 386 | 
        1/2✓ Branch 1 taken 462 times. 
          ✗ Branch 2 not taken. 
         | 
      462 | auto found = row_corrector.find(j); | |
| 387 | 
        2/2✓ Branch 2 taken 109 times. 
          ✓ Branch 3 taken 353 times. 
         | 
      462 | if (found == row_corrector.end()) { | |
| 388 | 109 | fail = true; | ||
| 389 | 109 | break; | ||
| 390 | } else { | |||
| 391 | 
        1/2✓ Branch 2 taken 353 times. 
          ✗ Branch 3 not taken. 
         | 
      353 | c_i.insert(found->second); | |
| 392 | } | |||
| 393 | } | |||
| 394 | } | |||
| 395 | 
        2/2✓ Branch 0 taken 146 times. 
          ✓ Branch 1 taken 109 times. 
         | 
      255 | if (!fail) { | |
| 396 | 
        1/2✓ Branch 1 taken 146 times. 
          ✗ Branch 2 not taken. 
         | 
      146 | ZXVert v = to_solve.at(i); | |
| 397 | 
        1/2✓ Branch 1 taken 146 times. 
          ✗ Branch 2 not taken. 
         | 
      146 | ZXType vt = diag.get_zxtype(v); | |
| 398 | 
        6/6✓ Branch 0 taken 145 times. 
          ✓ Branch 1 taken 1 times. 
          ✓ Branch 2 taken 134 times. 
          ✓ Branch 3 taken 11 times. 
          ✓ Branch 4 taken 1 times. 
          ✓ Branch 5 taken 133 times. 
         | 
      146 | if (vt == ZXType::XZ || vt == ZXType::YZ || vt == ZXType::PZ) | |
| 399 | 
        1/2✓ Branch 1 taken 13 times. 
          ✗ Branch 2 not taken. 
         | 
      13 | c_i.insert(v); | |
| 400 | 
        2/4✓ Branch 1 taken 146 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 146 times. 
          ✗ Branch 5 not taken. 
         | 
      146 | solved_flow.insert({v, c_i}); | |
| 401 | } | |||
| 402 | 255 | } | ||
| 403 | 102 | return solved_flow; | ||
| 404 | 51 | } | ||
| 405 | ||||
| 406 | 3 | Flow Flow::identify_pauli_flow(const ZXDiagram& diag) { | ||
| 407 | // Check diagram has the expected form for pauli flow | |||
| 408 | 
        2/4✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
          ✗ Branch 3 not taken. 
          ✓ Branch 4 taken 3 times. 
         | 
      3 | if (!diag.is_MBQC()) | |
| 409 | ✗ | throw ZXError("ZXDiagram must be in MBQC form to identify Pauli flow"); | ||
| 410 | ||||
| 411 | 
        1/2✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
         | 
      3 | ZXVertSeqSet solved; | |
| 412 | 3 | std::set<ZXVert> inputs; | ||
| 413 | 
        1/2✓ Branch 3 taken 3 times. 
          ✗ Branch 4 not taken. 
         | 
      6 | Flow fl{{}, {}}; | |
| 414 | ||||
| 415 | // Tag input measurements | |||
| 416 | 
        3/4✓ Branch 3 taken 3 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 10 taken 9 times. 
          ✓ Branch 11 taken 3 times. 
         | 
      12 | for (const ZXVert& i : diag.get_boundary(ZXType::Input)) { | |
| 417 | 
        2/4✓ Branch 1 taken 9 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 9 times. 
          ✗ Branch 5 not taken. 
         | 
      9 | ZXVert ni = diag.neighbours(i).at(0); | |
| 418 | 
        1/2✓ Branch 1 taken 9 times. 
          ✗ Branch 2 not taken. 
         | 
      9 | inputs.insert(ni); | |
| 419 | 
        1/2✓ Branch 1 taken 9 times. 
          ✗ Branch 2 not taken. 
         | 
      9 | ZXType nt = diag.get_zxtype(ni); | |
| 420 | 
        3/6✓ Branch 0 taken 9 times. 
          ✗ Branch 1 not taken. 
          ✓ Branch 2 taken 9 times. 
          ✗ Branch 3 not taken. 
          ✗ Branch 4 not taken. 
          ✓ Branch 5 taken 9 times. 
         | 
      9 | if (nt == ZXType::XZ || nt == ZXType::YZ || nt == ZXType::PY) | |
| 421 | ✗ | throw ZXError( | ||
| 422 | "Inputs measured in XZ, YZ, or Y cannot be corrected with Pauli " | |||
| 423 | ✗ | "flow"); | ||
| 424 | 3 | } | ||
| 425 | ||||
| 426 | // Indexing of correctors in binary matrix can be preserved between rounds as | |||
| 427 | // we will only ever add new correctors | |||
| 428 | 
        1/2✓ Branch 2 taken 3 times. 
          ✗ Branch 3 not taken. 
         | 
      3 | boost::bimap<ZXVert, unsigned> correctors; | |
| 429 | 3 | unsigned corrector_i = 0; | ||
| 430 | ||||
| 431 | 
        7/8✓ Branch 2 taken 3 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 6 taken 68 times. 
          ✓ Branch 7 taken 3 times. 
          ✓ Branch 9 taken 68 times. 
          ✓ Branch 10 taken 3 times. 
          ✓ Branch 12 taken 3 times. 
          ✓ Branch 13 taken 3 times. 
         | 
      74 | BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) { | |
| 432 | 
        4/5✓ Branch 1 taken 68 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 11 times. 
          ✓ Branch 4 taken 13 times. 
          ✓ Branch 5 taken 44 times. 
         | 
      68 | switch (diag.get_zxtype(v)) { | |
| 433 | 11 | case ZXType::Output: { | ||
| 434 | 
        2/4✓ Branch 1 taken 11 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 11 times. 
          ✗ Branch 5 not taken. 
         | 
      11 | ZXVert n = diag.neighbours(v).at(0); | |
| 435 | // Outputs are trivially solved | |||
| 436 | 
        1/2✓ Branch 1 taken 11 times. 
          ✗ Branch 2 not taken. 
         | 
      11 | solved.insert(v); | |
| 437 | 
        2/4✓ Branch 1 taken 11 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 11 times. 
          ✗ Branch 4 not taken. 
         | 
      11 | if (diag.get_zxtype(n) != ZXType::Input) { | |
| 438 | 
        1/2✓ Branch 1 taken 11 times. 
          ✗ Branch 2 not taken. 
         | 
      11 | solved.insert(n); | |
| 439 | 
        3/6✓ Branch 1 taken 11 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 11 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 11 times. 
          ✗ Branch 8 not taken. 
         | 
      11 | fl.c_.insert({n, {}}); | |
| 440 | 
        1/2✓ Branch 2 taken 11 times. 
          ✗ Branch 3 not taken. 
         | 
      11 | fl.d_.insert({n, 0}); | |
| 441 | } | |||
| 442 | // n is either an Input or PX, in which case it will be added to the | |||
| 443 | // correctors in the PX case | |||
| 444 | 11 | break; | ||
| 445 | } | |||
| 446 | 13 | case ZXType::PX: | ||
| 447 | case ZXType::PY: { | |||
| 448 | // Can use non-input Xs and Ys to correct | |||
| 449 | 
        2/4✓ Branch 2 taken 13 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 13 times. 
          ✗ Branch 6 not taken. 
         | 
      13 | if (inputs.find(v) == inputs.end()) { | |
| 450 | 
        2/4✓ Branch 1 taken 13 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 13 times. 
          ✗ Branch 5 not taken. 
         | 
      13 | correctors.insert({v, corrector_i}); | |
| 451 | 13 | ++corrector_i; | ||
| 452 | } | |||
| 453 | 13 | break; | ||
| 454 | } | |||
| 455 | 44 | default: | ||
| 456 | 44 | break; | ||
| 457 | } | |||
| 458 | } | |||
| 459 | ||||
| 460 | 3 | unsigned depth = 1; | ||
| 461 | ||||
| 462 | 3 | unsigned n_solved = 0; | ||
| 463 | do { | |||
| 464 | // Construct Gaussian elimination problem | |||
| 465 | 
        1/2✓ Branch 2 taken 13 times. 
          ✗ Branch 3 not taken. 
         | 
      13 | boost::bimap<ZXVert, unsigned> preserve; | |
| 466 | 
        1/2✓ Branch 2 taken 13 times. 
          ✗ Branch 3 not taken. 
         | 
      13 | boost::bimap<ZXVert, unsigned> unsolved_ys; | |
| 467 | 13 | ZXVertVec to_solve; | ||
| 468 | 
        7/8✓ Branch 2 taken 13 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 6 taken 331 times. 
          ✓ Branch 7 taken 13 times. 
          ✓ Branch 9 taken 331 times. 
          ✓ Branch 10 taken 13 times. 
          ✓ Branch 12 taken 13 times. 
          ✓ Branch 13 taken 13 times. 
         | 
      357 | BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) { | |
| 469 | 
        1/2✓ Branch 1 taken 331 times. 
          ✗ Branch 2 not taken. 
         | 
      331 | ZXType type = diag.get_zxtype(v); | |
| 470 | 
        8/10✓ Branch 4 taken 331 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 331 times. 
          ✗ Branch 8 not taken. 
          ✓ Branch 9 taken 122 times. 
          ✓ Branch 10 taken 209 times. 
          ✓ Branch 11 taken 79 times. 
          ✓ Branch 12 taken 43 times. 
          ✓ Branch 13 taken 79 times. 
          ✓ Branch 14 taken 252 times. 
         | 
      331 | if (solved.get<TagKey>().find(v) == solved.get<TagKey>().end() && | |
| 471 | type != ZXType::Input) { | |||
| 472 | 
        1/2✓ Branch 1 taken 79 times. 
          ✗ Branch 2 not taken. 
         | 
      79 | to_solve.push_back(v); | |
| 473 | 
        2/2✓ Branch 0 taken 1 times. 
          ✓ Branch 1 taken 78 times. 
         | 
      79 | if (type == ZXType::PY) | |
| 474 | 
        3/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 1 times. 
          ✗ Branch 8 not taken. 
         | 
      1 | unsolved_ys.insert({v, (unsigned)unsolved_ys.size()}); | |
| 475 | 
        2/2✓ Branch 0 taken 77 times. 
          ✓ Branch 1 taken 1 times. 
         | 
      78 | else if (type != ZXType::PZ) | |
| 476 | 
        3/6✓ Branch 1 taken 77 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 77 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 77 times. 
          ✗ Branch 8 not taken. 
         | 
      77 | preserve.insert({v, (unsigned)preserve.size()}); | |
| 477 | } | |||
| 478 | } | |||
| 479 | ||||
| 480 | std::map<ZXVert, ZXVertSeqSet> new_corrections = gauss_solve_correctors( | |||
| 481 | 
        1/2✓ Branch 1 taken 13 times. 
          ✗ Branch 2 not taken. 
         | 
      13 | diag, correctors, preserve, to_solve, unsolved_ys); | |
| 482 | ||||
| 483 | 13 | n_solved = new_corrections.size(); | ||
| 484 | ||||
| 485 | 
        2/2✓ Branch 5 taken 37 times. 
          ✓ Branch 6 taken 13 times. 
         | 
      50 | for (const std::pair<const ZXVert, ZXVertSeqSet>& nc : new_corrections) { | |
| 486 | 
        1/2✓ Branch 1 taken 37 times. 
          ✗ Branch 2 not taken. 
         | 
      37 | fl.c_.insert(nc); | |
| 487 | 
        1/2✓ Branch 2 taken 37 times. 
          ✗ Branch 3 not taken. 
         | 
      37 | fl.d_.insert({nc.first, depth}); | |
| 488 | 
        1/2✓ Branch 1 taken 37 times. 
          ✗ Branch 2 not taken. 
         | 
      37 | solved.insert(nc.first); | |
| 489 | 
        3/4✓ Branch 2 taken 37 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 28 times. 
          ✓ Branch 6 taken 9 times. 
         | 
      37 | if (inputs.find(nc.first) == inputs.end()) | |
| 490 | 
        3/6✓ Branch 1 taken 28 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 28 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 28 times. 
          ✗ Branch 8 not taken. 
         | 
      28 | correctors.insert({nc.first, (unsigned)correctors.size()}); | |
| 491 | } | |||
| 492 | ||||
| 493 | 13 | ++depth; | ||
| 494 | 
        2/2✓ Branch 4 taken 10 times. 
          ✓ Branch 5 taken 3 times. 
         | 
      13 | } while (n_solved != 0); | |
| 495 | ||||
| 496 | 
        2/4✓ Branch 3 taken 3 times. 
          ✗ Branch 4 not taken. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 3 times. 
         | 
      3 | if (solved.size() + inputs.size() != diag.n_vertices()) | |
| 497 | ✗ | throw ZXError("ZXDiagram does not have pauli flow"); | ||
| 498 | ||||
| 499 | 6 | return fl; | ||
| 500 | 3 | } | ||
| 501 | ||||
| 502 | 1 | std::set<ZXVertSeqSet> Flow::identify_focussed_sets(const ZXDiagram& diag) { | ||
| 503 | // Check diagram has the expected form for pauli flow | |||
| 504 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✗ Branch 3 not taken. 
          ✓ Branch 4 taken 1 times. 
         | 
      1 | if (!diag.is_MBQC()) | |
| 505 | ✗ | throw ZXError("ZXDiagram must be in MBQC form to identify gflow"); | ||
| 506 | ||||
| 507 | 1 | std::set<ZXVert> inputs; | ||
| 508 | 1 | std::set<ZXVert> outputs; | ||
| 509 | ||||
| 510 | // Tag input measurements | |||
| 511 | 
        3/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 10 taken 1 times. 
          ✓ Branch 11 taken 1 times. 
         | 
      2 | for (const ZXVert& i : diag.get_boundary(ZXType::Input)) { | |
| 512 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | ZXVert ni = diag.neighbours(i).at(0); | |
| 513 | 
        1/2✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
         | 
      1 | inputs.insert(ni); | |
| 514 | 
        1/2✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
         | 
      1 | ZXType nt = diag.get_zxtype(ni); | |
| 515 | 
        3/6✓ Branch 0 taken 1 times. 
          ✗ Branch 1 not taken. 
          ✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
          ✗ Branch 4 not taken. 
          ✓ Branch 5 taken 1 times. 
         | 
      1 | if (nt == ZXType::XZ || nt == ZXType::YZ || nt == ZXType::PY) | |
| 516 | ✗ | throw ZXError( | ||
| 517 | "Inputs measured in XZ, YZ, or Y cannot be corrected with Pauli " | |||
| 518 | ✗ | "flow"); | ||
| 519 | 1 | } | ||
| 520 | 
        3/4✓ Branch 3 taken 1 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 10 taken 3 times. 
          ✓ Branch 11 taken 1 times. 
         | 
      4 | for (const ZXVert& o : diag.get_boundary(ZXType::Output)) { | |
| 521 | 
        2/4✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 3 times. 
          ✗ Branch 5 not taken. 
         | 
      3 | ZXVert no = diag.neighbours(o).at(0); | |
| 522 | 
        1/2✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
         | 
      3 | outputs.insert(no); | |
| 523 | 1 | } | ||
| 524 | ||||
| 525 | // Build Gaussian elimination problem | |||
| 526 | 
        1/2✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
         | 
      1 | boost::bimap<ZXVert, unsigned> correctors; | |
| 527 | 
        1/2✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
         | 
      1 | boost::bimap<ZXVert, unsigned> preserve; | |
| 528 | 
        1/2✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
         | 
      1 | boost::bimap<ZXVert, unsigned> ys; | |
| 529 | 1 | unsigned n_correctors = 0; | ||
| 530 | 1 | unsigned n_preserve = 0; | ||
| 531 | 1 | unsigned n_ys = 0; | ||
| 532 | ||||
| 533 | 
        7/8✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 6 taken 16 times. 
          ✓ Branch 7 taken 1 times. 
          ✓ Branch 9 taken 16 times. 
          ✓ Branch 10 taken 1 times. 
          ✓ Branch 12 taken 1 times. 
          ✓ Branch 13 taken 1 times. 
         | 
      18 | BGL_FORALL_VERTICES(v, *diag.graph, ZXGraph) { | |
| 534 | 
        5/6✓ Branch 1 taken 16 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 3 taken 4 times. 
          ✓ Branch 4 taken 4 times. 
          ✓ Branch 5 taken 1 times. 
          ✓ Branch 6 taken 7 times. 
         | 
      16 | switch (diag.get_zxtype(v)) { | |
| 535 | 4 | case ZXType::XY: { | ||
| 536 | 
        2/4✓ Branch 1 taken 4 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 4 times. 
          ✗ Branch 5 not taken. 
         | 
      4 | preserve.insert({v, n_preserve}); | |
| 537 | 4 | ++n_preserve; | ||
| 538 | // Can use non-input Xs and Ys to correct | |||
| 539 | 
        3/4✓ Branch 2 taken 4 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 3 times. 
          ✓ Branch 6 taken 1 times. 
         | 
      4 | if (inputs.find(v) == inputs.end()) { | |
| 540 | 
        2/4✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 3 times. 
          ✗ Branch 5 not taken. 
         | 
      3 | correctors.insert({v, n_correctors}); | |
| 541 | 3 | ++n_correctors; | ||
| 542 | } | |||
| 543 | 4 | break; | ||
| 544 | } | |||
| 545 | 4 | case ZXType::PX: { | ||
| 546 | // Nonmeasured vertices also covered by PX | |||
| 547 | // Only need to preserve measured vertices | |||
| 548 | 
        3/4✓ Branch 2 taken 4 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 1 times. 
          ✓ Branch 6 taken 3 times. 
         | 
      4 | if (outputs.find(v) == outputs.end()) { | |
| 549 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | preserve.insert({v, n_preserve}); | |
| 550 | 1 | ++n_preserve; | ||
| 551 | } | |||
| 552 | // Can use non-input Xs and Ys to correct | |||
| 553 | 
        2/4✓ Branch 2 taken 4 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 4 times. 
          ✗ Branch 6 not taken. 
         | 
      4 | if (inputs.find(v) == inputs.end()) { | |
| 554 | 
        2/4✓ Branch 1 taken 4 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 4 times. 
          ✗ Branch 5 not taken. 
         | 
      4 | correctors.insert({v, n_correctors}); | |
| 555 | 4 | ++n_correctors; | ||
| 556 | } | |||
| 557 | 4 | break; | ||
| 558 | } | |||
| 559 | 1 | case ZXType::PY: { | ||
| 560 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | ys.insert({v, n_ys}); | |
| 561 | 1 | ++n_ys; | ||
| 562 | // Can use non-input Xs and Ys to correct | |||
| 563 | 
        2/4✓ Branch 2 taken 1 times. 
          ✗ Branch 3 not taken. 
          ✓ Branch 5 taken 1 times. 
          ✗ Branch 6 not taken. 
         | 
      1 | if (inputs.find(v) == inputs.end()) { | |
| 564 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | correctors.insert({v, n_correctors}); | |
| 565 | 1 | ++n_correctors; | ||
| 566 | } | |||
| 567 | 1 | break; | ||
| 568 | } | |||
| 569 | 7 | default: | ||
| 570 | 7 | break; | ||
| 571 | } | |||
| 572 | } | |||
| 573 | ||||
| 574 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      2 | MatrixXb mat = MatrixXb::Zero(n_preserve + n_ys, n_correctors); | |
| 575 | ||||
| 576 | // Build adjacency matrix | |||
| 577 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | for (boost::bimap<ZXVert, unsigned>::const_iterator it = correctors.begin(), | |
| 578 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | end = correctors.end(); | |
| 579 | 
        4/6✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 9 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 8 times. 
          ✓ Branch 7 taken 1 times. 
         | 
      9 | it != end; ++it) { | |
| 580 | 
        4/6✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 8 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 11 taken 22 times. 
          ✓ Branch 12 taken 8 times. 
         | 
      30 | for (const ZXVert& n : diag.neighbours(it->left)) { | |
| 581 | 
        1/2✓ Branch 1 taken 22 times. 
          ✗ Branch 2 not taken. 
         | 
      22 | auto in_preserve = preserve.left.find(n); | |
| 582 | 
        4/6✓ Branch 1 taken 22 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 22 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 7 times. 
          ✓ Branch 7 taken 15 times. 
         | 
      22 | if (in_preserve != preserve.left.end()) { | |
| 583 | 
        3/6✓ Branch 1 taken 7 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 7 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 7 times. 
          ✗ Branch 8 not taken. 
         | 
      7 | mat(in_preserve->second, it->right) = true; | |
| 584 | } else { | |||
| 585 | 
        1/2✓ Branch 1 taken 15 times. 
          ✗ Branch 2 not taken. 
         | 
      15 | auto in_ys = ys.left.find(n); | |
| 586 | 
        4/6✓ Branch 1 taken 15 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 15 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 3 times. 
          ✓ Branch 7 taken 12 times. 
         | 
      15 | if (in_ys != ys.left.end()) { | |
| 587 | 
        3/6✓ Branch 1 taken 3 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 3 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 3 times. 
          ✗ Branch 8 not taken. 
         | 
      3 | mat(n_preserve + in_ys->second, it->right) = true; | |
| 588 | } | |||
| 589 | } | |||
| 590 | 8 | } | ||
| 591 | } | |||
| 592 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | for (boost::bimap<ZXVert, unsigned>::const_iterator it = ys.begin(), | |
| 593 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | end = ys.end(); | |
| 594 | 
        4/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 2 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✓ Branch 7 taken 1 times. 
         | 
      2 | it != end; ++it) { | |
| 595 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | auto found = correctors.left.find(it->left); | |
| 596 | 
        3/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 1 times. 
          ✗ Branch 7 not taken. 
         | 
      1 | if (found != correctors.left.end()) | |
| 597 | 
        3/6✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 7 taken 1 times. 
          ✗ Branch 8 not taken. 
         | 
      1 | mat(n_preserve + it->right, found->second) = true; | |
| 598 | } | |||
| 599 | ||||
| 600 | // Gaussian elimination | |||
| 601 | std::vector<std::pair<unsigned, unsigned>> row_ops = | |||
| 602 | 
        1/2✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
         | 
      2 | gaussian_elimination_row_ops(mat); | |
| 603 | 
        2/2✓ Branch 5 taken 7 times. 
          ✓ Branch 6 taken 1 times. 
         | 
      8 | for (const std::pair<unsigned, unsigned>& op : row_ops) { | |
| 604 | 
        2/2✓ Branch 0 taken 56 times. 
          ✓ Branch 1 taken 7 times. 
         | 
      63 | for (unsigned j = 0; j < n_correctors; ++j) { | |
| 605 | 
        2/4✓ Branch 1 taken 56 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 56 times. 
          ✗ Branch 5 not taken. 
         | 
      56 | mat(op.second, j) ^= mat(op.first, j); | |
| 606 | } | |||
| 607 | } | |||
| 608 | ||||
| 609 | // Back substitution | |||
| 610 | // For each column j, it either a leading column (the first column for which | |||
| 611 | // mat(i,j) == true for a given i, so set row_corrector[i] = j; by Gaussian | |||
| 612 | // Elimination this is the only entry in the column) or it describes the | |||
| 613 | // focussed set generator {j} + {row_corrector[i] | mat(i,j) == true} | |||
| 614 | 1 | std::set<ZXVertSeqSet> focussed; | ||
| 615 | 2 | std::map<unsigned, ZXVert> row_corrector; | ||
| 616 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | for (boost::bimap<ZXVert, unsigned>::const_iterator it = correctors.begin(), | |
| 617 | 
        2/4✓ Branch 1 taken 1 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 1 times. 
          ✗ Branch 5 not taken. 
         | 
      1 | end = correctors.end(); | |
| 618 | 
        4/6✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 9 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 8 times. 
          ✓ Branch 7 taken 1 times. 
         | 
      9 | it != end; ++it) { | |
| 619 | 
        3/6✓ Branch 1 taken 8 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 8 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 8 taken 8 times. 
          ✗ Branch 9 not taken. 
         | 
      8 | ZXVertSeqSet fset{it->left}; | |
| 620 | 8 | bool new_row_corrector = false; | ||
| 621 | 
        2/2✓ Branch 0 taken 33 times. 
          ✓ Branch 1 taken 2 times. 
         | 
      35 | for (unsigned i = 0; i < n_preserve + n_ys; ++i) { | |
| 622 | 
        4/6✓ Branch 1 taken 33 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 4 taken 33 times. 
          ✗ Branch 5 not taken. 
          ✓ Branch 6 taken 10 times. 
          ✓ Branch 7 taken 23 times. 
         | 
      33 | if (mat(i, it->right)) { | |
| 623 | 
        2/4✓ Branch 1 taken 10 times. 
          ✗ Branch 2 not taken. 
          ✓ Branch 5 taken 10 times. 
          ✗ Branch 6 not taken. 
         | 
      10 | auto inserted = row_corrector.insert({i, it->left}); | |
| 624 | 
        2/2✓ Branch 0 taken 6 times. 
          ✓ Branch 1 taken 4 times. 
         | 
      10 | if (inserted.second) { | |
| 625 | // New row_corrector, so move to next column | |||
| 626 | 6 | new_row_corrector = true; | ||
| 627 | 6 | break; | ||
| 628 | } else { | |||
| 629 | // Non-correcting column | |||
| 630 | 
        1/2✓ Branch 2 taken 4 times. 
          ✗ Branch 3 not taken. 
         | 
      4 | fset.insert(inserted.first->second); | |
| 631 | } | |||
| 632 | } | |||
| 633 | } | |||
| 634 | 
        6/10✓ Branch 0 taken 2 times. 
          ✓ Branch 1 taken 6 times. 
          ✓ Branch 3 taken 2 times. 
          ✗ Branch 4 not taken. 
          ✓ Branch 6 taken 2 times. 
          ✗ Branch 7 not taken. 
          ✓ Branch 8 taken 2 times. 
          ✓ Branch 9 taken 2 times. 
          ✗ Branch 11 not taken. 
          ✗ Branch 12 not taken. 
         | 
      10 | if (!new_row_corrector) focussed.insert({fset}); | |
| 635 | 8 | } | ||
| 636 | ||||
| 637 | 2 | return focussed; | ||
| 638 | 1 | } | ||
| 639 | ||||
| 640 | } // namespace zx | |||
| 641 | ||||
| 642 | } // namespace tket | |||
| 643 |