Repository: dwavesystems/minorminer (language: python)

Issue:
find_clique_embedding(..., use_cache=False) is nondeterministic in deterministic regime
**Description**
This occurred in randomized testing: rarely, in testing problems with supposedly deterministic results, we see a mismatch in chainlength.

**To Reproduce**
```python
import dwave_networkx as dnx, networkx as nx
from minorminer.busclique import find_clique_embedding
c4 = dnx.chimera_graph(4)
c4.remove_nodes_from([1, 119, 34, 6, 46, 117])
c4.remove_edges_from([
    (119, 127), (56, 62), (43, 46), (41, 46), (113, 117), (46, 54), (50, 82),
    (121, 127), (34, 37), (115, 117), (1, 6), (111, 119), (6, 14), (98, 100),
    (47, 55), (1, 33), (3, 6), (84, 92), (112, 117), (38, 46), (114, 117), 
    (113, 119), (42, 74), (1, 5), (34, 36), (40, 46), (34, 39), (115, 119),
    (19, 20), (80, 112), (117, 125), (34, 66), (18, 21), (2, 34), (42, 46),
    (112, 119), (1, 4), (114, 119), (0, 6), (34, 38), (1, 7), (2, 6), 
    (109, 117), (16, 23)
])
emb0 = find_clique_embedding(11, c4, use_cache = False)
emb1 = find_clique_embedding(11, c4, use_cache = True)
assert max(map(len, emb0.values())) == max(map(len, emb1.values())) #boom
assert emb0 == emb1 #boom
```

**Expected behavior**
The embeddings should be identical.

**Environment:**
minorminer 0.2.5

find_clique_embedding(..., use_cache=False) is nondeterministic in deterministic regime
**Description**
This occurred in randomized testing: rarely, in testing problems with supposedly deterministic results, we see a mismatch in chainlength.

**To Reproduce**
```python
import dwave_networkx as dnx, networkx as nx
from minorminer.busclique import find_clique_embedding
c4 = dnx.chimera_graph(4)
c4.remove_nodes_from([1, 119, 34, 6, 46, 117])
c4.remove_edges_from([
    (119, 127), (56, 62), (43, 46), (41, 46), (113, 117), (46, 54), (50, 82),
    (121, 127), (34, 37), (115, 117), (1, 6), (111, 119), (6, 14), (98, 100),
    (47, 55), (1, 33), (3, 6), (84, 92), (112, 117), (38, 46), (114, 117), 
    (113, 119), (42, 74), (1, 5), (34, 36), (40, 46), (34, 39), (115, 119),
    (19, 20), (80, 112), (117, 125), (34, 66), (18, 21), (2, 34), (42, 46),
    (112, 119), (1, 4), (114, 119), (0, 6), (34, 38), (1, 7), (2, 6), 
    (109, 117), (16, 23)
])
emb0 = find_clique_embedding(11, c4, use_cache = False)
emb1 = find_clique_embedding(11, c4, use_cache = True)
assert max(map(len, emb0.values())) == max(map(len, emb1.values())) #boom
assert emb0 == emb1 #boom
```

**Expected behavior**
The embeddings should be identical.

**Environment:**
minorminer 0.2.5



Discussion / hints:

Fix patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index c3127cad..c6edd87a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -109,7 +109,7 @@ jobs:
 
       - restore_cache: &restore-cache-template
           keys:
-          - v2-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "tests/requirements.txt" }}-{{ .Environment.CIRCLE_JOB }}
+          - v2-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "test_requirements.txt" }}-{{ .Environment.CIRCLE_JOB }}
 
       - run:
           name: create virtualenv
@@ -121,12 +121,12 @@ jobs:
           command: |
             . env/bin/activate
             python --version
-            pip install -r requirements.txt -r tests/requirements.txt
+            pip install -r requirements.txt -r test_requirements.txt
 
       - save_cache: &save-cache-template
           paths:
             - ./env
-          key: v2-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "tests/requirements.txt" }}-{{ .Environment.CIRCLE_JOB }}
+          key: v2-dependencies-{{ checksum "requirements.txt" }}-{{ checksum "test_requirements.txt" }}-{{ .Environment.CIRCLE_JOB }}
 
       - run: &build-ext-template
           name: install package
@@ -139,7 +139,7 @@ jobs:
           command: |
             . env/bin/activate
             python --version
-            python -m nose . --exe
+            python -m pytest . --verbose
           environment:
             CPPDEBUG: 1
 
@@ -241,7 +241,7 @@ jobs:
           command: |
             eval "$(pyenv init -)"
             python --version
-            pip install -r requirements.txt -r tests/requirements.txt
+            pip install -r requirements.txt -r test_requirements.txt
 
       - save_cache: *save-cache-template
 
@@ -256,7 +256,7 @@ jobs:
           command: |
             eval "$(pyenv init -)"
             python --version
-            python -m nose . --exe
+            python -m pytest . --verbose
           environment:
             CPPDEBUG: 1
 
@@ -312,7 +312,7 @@ jobs:
           command: |
             for PYBIN in /opt/python/*/bin; do
               if "${PYBIN}/python" -c "import sys; sys.exit(sys.version_info>=(3, 6) and sys.version_info<(3, 10))"; then continue; fi;
-              "${PYBIN}/pip" install -r requirements.txt -r tests/requirements.txt
+              "${PYBIN}/pip" install -r requirements.txt -r test_requirements.txt
               "${PYBIN}/pip" wheel . -w ./wheelhouse
               "${PYBIN}/python" setup.py sdist -d ./dist
             done
diff --git a/.gitignore b/.gitignore
index 7adc3fd0..8e3ea399 100644
--- a/.gitignore
+++ b/.gitignore
@@ -180,3 +180,7 @@ venv.bak/
 .vscode/
 
 # End of https://www.gitignore.io/api/c,c++,python
+
+#because I keep doing tests in the repo root directory...
+/*.png
+/callgrind.out.*
diff --git a/README.rst b/README.rst
index 2c850b13..859d540a 100644
--- a/README.rst
+++ b/README.rst
@@ -60,8 +60,8 @@ To install from this repository, run the `setuptools` script.
     pip install -r requirements.txt
     python setup.py install
     # optionally, run the tests to check your build
-    pip install -r tests/requirements.txt
-    python -m nose . --exe
+    pip install -r test_requirements.txt
+    python -m pytest .
 
 
 .. install-python-end
diff --git a/appveyor.yml b/appveyor.yml
index 5e40f1fd..a679137d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -57,7 +57,7 @@ install:
   # Install dependencies
   - if "%CPPDEBUG%" == "1" ( SET MM_BUILD_DIR=%DEBUG_MM_BUILD_DIR% ) else ( SET MM_BUILD_DIR=%TRUE_MM_BUILD_DIR%)
   - "%PYTHON%\\python.exe -m pip install -U wheel setuptools pip twine"
-  - "%PYTHON%\\python.exe -m pip install -r requirements.txt -r tests\\requirements.txt"
+  - "%PYTHON%\\python.exe -m pip install -r requirements.txt -r test_requirements.txt"
  
 build_script:
   # Build the compiled extension
@@ -65,7 +65,7 @@ build_script:
 
 test_script:
   # Run the project tests
-  - "%PYTHON%\\python.exe -m nose . --exe"
+  - "%PYTHON%\\python.exe -m pytest . --verbose"
 
 after_test:
   # If tests are successful, create binary packages for the project.
diff --git a/include/busclique/biclique_cache.hpp b/include/busclique/biclique_cache.hpp
index 0bb054c9..314684ed 100644
--- a/include/busclique/biclique_cache.hpp
+++ b/include/busclique/biclique_cache.hpp
@@ -19,23 +19,20 @@ namespace busclique {
 
 class yieldcache {
   public:
-    const size_t rows;
-    const size_t cols;
+    const size_y rows;
+    const size_x cols;
   private:
     size_t *mem;
+    size_t index(size_y y, size_x x, bool u) const {
+        return coordinate_converter::cell_index(y, x, u, rows, cols);
+    }
   public:
-    yieldcache(size_t r, size_t c, size_t *m) : rows(r), cols(c), mem(m) {};
-    size_t get(size_t y, size_t x, size_t u) const {
-        minorminer_assert(y < rows);
-        minorminer_assert(x < cols);
-        minorminer_assert(u < 2);
-        return mem[(y*cols + x)*2 + u];
+    yieldcache(size_y r, size_x c, size_t *m) : rows(r), cols(c), mem(m) {};
+    size_t get(size_y y, size_x x, bool u) const {
+        return mem[index(y, x, u)];
     }
-    void set(size_t y, size_t x, size_t u, size_t score) {
-        minorminer_assert(y < rows);
-        minorminer_assert(x < cols);
-        minorminer_assert(u < 2);
-        mem[(y*cols + x)*2 + u] = score;
+    void set(size_y y, size_x x, bool u, size_t score) {
+        mem[index(y, x, u)] = score;
     }
     
 };
@@ -49,91 +46,91 @@ class biclique_cache {
   private:
     size_t *mem;
 
-    size_t memrows(size_t h) const {
-        return cells.topo.dim[0] - h + 1;
+    size_y memrows(size_y h) const {
+        return cells.topo.dim_y - h + 1u;
     }
-    size_t memcols(size_t w) const {
-        return cells.topo.dim[1] - w + 1;
+    size_x memcols(size_x w) const {
+        return cells.topo.dim_x - w + 1u;
     }
 
-    size_t memsize(size_t h, size_t w) const {
-        return 2 * memrows(h) * memcols(w);
+    size_t memsize(size_y h, size_x w) const {
+        return coordinate_converter::product(2, memrows(h), memcols(w));
     }
 
     size_t memsize() const {
         size_t size = 0;
-        for(size_t h = 0; h <= cells.topo.dim[0]; h++)
-            for(size_t w = 0; w <= cells.topo.dim[1]; w++)
+        for(size_y h = 0; h <= cells.topo.dim_y; h++)
+            for(size_x w = 0; w <= cells.topo.dim_x; w++)
                 size += memsize(h, w) + 1;
         return size;
     }
 
-    size_t mem_addr(size_t h, size_t w) const {
-        return (h-1)*cells.topo.dim[1] + (w-1);
+    size_t mem_addr(size_y h, size_x w) const {
+        return coordinate_converter::grid_index(h-1u, w-1u, cells.topo.dim_y, cells.topo.dim_x);
     }
         
 
     void make_access_table() {
-        size_t offset = cells.topo.dim[0]*cells.topo.dim[1];
-        for(size_t h = 1; h <= cells.topo.dim[0]; h++)
-            for(size_t w = 1; w <= cells.topo.dim[1]; w++) {        
+        size_t offset = coordinate_converter::product(cells.topo.dim_y, cells.topo.dim_x);
+        for(size_y h = 1; h <= cells.topo.dim_y; h++)
+            for(size_x w = 1; w <= cells.topo.dim_x; w++) {        
                 mem[mem_addr(h,w)] = offset;
                 offset += memsize(h, w);
             }
     }
 
   public:
-    yieldcache get(size_t h, size_t w) const {
-        minorminer_assert(1 <= h);
-        minorminer_assert(h <= cells.topo.dim[0]);
-        minorminer_assert(1 <= w);
-        minorminer_assert(w <= cells.topo.dim[1]);
+    yieldcache get(size_y h, size_x w) const {
+        minorminer_assert(size_y(0) < h);
+        minorminer_assert(h <= cells.topo.dim_y);
+        minorminer_assert(size_x(0) < w);
+        minorminer_assert(w <= cells.topo.dim_x);
         return yieldcache(memrows(h), memcols(w), mem + mem[mem_addr(h,w)]);
     }
 
   private:
     void compute_cache(const bundle_cache<topo_spec> &bundles) {
-        for(size_t h = 1; h <= cells.topo.dim[0]; h++) {
+        for(size_y h = 1; h <= cells.topo.dim_y; h++) {
             {
-                size_t w = 1;
+                size_x w = 1;
                 yieldcache next = get(h, w);
-                for(size_t y0 = 0; y0 <= cells.topo.dim[0]-h; y0++)
-                    for(size_t x0 = 0; x0 <= cells.topo.dim[1]-w; x0++)
-                        next.set(y0, x0, 0, bundles.get_line_score(0, x0, y0, y0+h-1));
+                for(size_y y0 = 0; y0 <= cells.topo.dim_y-h; y0++)
+                    for(size_x x0 = 0; x0 <= cells.topo.dim_x-w; x0++)
+                        next.set(y0, x0, 0, bundles.get_line_score(0, vert(x0), vert(y0), vert(y0+h-1u)));
             }
-            for(size_t w = 2; w <= cells.topo.dim[1]; w++) {
-                yieldcache prev = get(h, w-1);
+            for(size_x w = 2; w <= cells.topo.dim_x; w++) {
+                yieldcache prev = get(h, w-1u);
                 yieldcache next = get(h, w);
-                for(size_t y0 = 0; y0 <= cells.topo.dim[0]-h; y0++) {
+                for(size_y y0 = 0; y0 <= cells.topo.dim_y-h; y0++) {
                     size_t score = prev.get(y0, 0, 0);
-                    score += bundles.get_line_score(0, w-1, y0, y0+h-1);
+                    score += bundles.get_line_score(0, vert(w-1u), vert(y0), vert(y0+h-1u));
                     next.set(y0, 0, 0, score);
-                    for(size_t x0 = 1; x0 <= cells.topo.dim[1]-w; x0++) {
-                        score -= bundles.get_line_score(0, x0-1, y0, y0+h-1);
-                        score += bundles.get_line_score(0, x0+w-1, y0, y0+h-1);
+                    for(size_x x0 = 1; x0 <= cells.topo.dim_x-w; x0++) {
+                        score -= bundles.get_line_score(0, vert(x0-1u), vert(y0), vert(y0+h-1u));
+                        score += bundles.get_line_score(0, vert(x0+w-1u), vert(y0), vert(y0+h-1u));
                         next.set(y0, x0, 0, score);
                     }
                 }
             }
         }
-        for(size_t w = 1; w <= cells.topo.dim[1]; w++) {
+        for(size_x w = 1; w <= cells.topo.dim_x; w++) {
             {
-                size_t h = 1;
+                size_y h = 1;
                 yieldcache next = get(h, w);
-                for(size_t y0 = 0; y0 <= cells.topo.dim[0]-h; y0++)
-                    for(size_t x0 = 0; x0 <= cells.topo.dim[1]-w; x0++)
-                        next.set(y0, x0, 1, bundles.get_line_score(1, y0, x0, x0+w-1));
+                for(size_y y0 = 0; y0 <= cells.topo.dim_y-h; y0++)
+                    for(size_x x0 = 0; x0 <= cells.topo.dim_x-w; x0++)
+                        next.set(y0, x0, 1, bundles.get_line_score(1, horz(y0), horz(x0), horz(x0+w-1u)));
             }
-            for(size_t h = 2; h <= cells.topo.dim[0]; h++) {
-                yieldcache prev = get(h-1, w);
+            for(size_y h = 2; h <= cells.topo.dim_y; h++) {
+                yieldcache prev = get(h-1u, w);
                 yieldcache next = get(h, w);
-                for(size_t x0 = 0; x0 <= cells.topo.dim[1]-w; x0++) {
+                for(size_x x0 = 0; x0 <= cells.topo.dim_x-w; x0++) {
                     size_t score = prev.get(0, x0, 1);
-                    score += bundles.get_line_score(1, h-1, x0, x0+w-1);
+                    score += bundles.get_line_score(1, horz(h-1u), horz(x0), horz(x0+w-1u));
                     next.set(0, x0, 1, score);
-                    for(size_t y0 = 1; y0 <= cells.topo.dim[0]-h; y0++) {
-                        score -= bundles.get_line_score(1, y0-1, x0, x0+w-1);
-                        score += bundles.get_line_score(1, y0+h-1, x0, x0+w-1);
+                    for(size_y y0 = 1; y0 <= cells.topo.dim_y-h; y0++) {
+                        score -= bundles.get_line_score(1, horz(y0-1u), horz(x0), horz(x0+w-1u));
+                        score += bundles.get_line_score(1, horz(y0+h-1u), horz(x0), horz(x0+w-1u));
                         next.set(y0, x0, 1, score);
                     }
                 }
@@ -151,13 +148,13 @@ class biclique_cache {
         if(mem != nullptr) { delete[] mem; mem = nullptr; }
     }
 
-    std::pair<size_t, size_t> score(size_t y0, size_t y1, size_t x0, size_t x1) const {
+    std::pair<size_t, size_t> score(size_y y0, size_y y1, size_x x0, size_x x1) const {
         minorminer_assert(y0 <= y1);
         minorminer_assert(x0 <= x1);
-        minorminer_assert(y1 < cells.topo.dim[0]);
-        minorminer_assert(x1 < cells.topo.dim[1]);
-        size_t h = y1 - y0 + 1;
-        size_t w = x1 - x0 + 1;
+        minorminer_assert(y1 < cells.topo.dim_y);
+        minorminer_assert(x1 < cells.topo.dim_x);
+        size_y h = y1 - y0 + 1u;
+        size_x w = x1 - x0 + 1u;
         yieldcache cache = get(h, w);
         return std::make_pair<size_t, size_t>(cache.get(y0, x0, 0), cache.get(y0, x0, 1));
     }
@@ -165,14 +162,17 @@ class biclique_cache {
 
 template<typename topo_spec>
 class biclique_yield_cache {
-    using bound_t = std::tuple<size_t, size_t, size_t, size_t>;
+    using bound_t = std::tuple<size_y, size_y, size_x, size_x>;
 
   public:
     const cell_cache<topo_spec> &cells;
     const bundle_cache<topo_spec> &bundles;
 
   private:
-    const size_t rows, cols;
+    //note: the role of rows and columns is reversed, because the indices
+    //are chainlengths in a given direction (not the number of chains)
+    const size_x rows;
+    const size_y cols;
     vector<vector<size_t>> chainlength;
     vector<vector<bound_t>> biclique_bounds;
 
@@ -186,32 +186,30 @@ class biclique_yield_cache {
         cells(c),
         bundles(b),
 
-        //note: the role of rows and columns is reversed, because the indices
-        //are chainlengths in a given direction (not the number of chains)
-        rows(cells.topo.dim[1]*cells.topo.shore),
-        cols(cells.topo.dim[0]*cells.topo.shore),
+        rows(cells.topo.dim_x*cells.topo.shore),
+        cols(cells.topo.dim_y*cells.topo.shore),
 
-        chainlength(rows, vector<size_t>(cols, 0)),
-        biclique_bounds(rows, vector<bound_t>(cols, bound_t(0,0,0,0))) {
+        chainlength(coordinate_index(rows), vector<size_t>(coordinate_index(cols), 0)),
+        biclique_bounds(coordinate_index(rows), vector<bound_t>(coordinate_index(cols), bound_t(0,0,0,0))) {
         compute_cache(bicliques);
     }
   private:
     void compute_cache(const biclique_cache<topo_spec> &bicliques) {
-        for(size_t h = 1; h <= cells.topo.dim[0]; h++) {
-            for(size_t w = 1; w <= cells.topo.dim[1]; w++) {
+        for(size_y h = 1; h <= cells.topo.dim_y; h++) {
+            for(size_x w = 1; w <= cells.topo.dim_x; w++) {
                 auto cache = bicliques.get(h, w);
-                for(size_t y = 0; y < cache.rows; y++) {
-                    for(size_t x = 0; x < cache.cols; x++) {
+                for(size_y y = 0; y < cache.rows; y++) {
+                    for(size_x x = 0; x < cache.cols; x++) {
                         size_t s0 = cache.get(y, x, 0);
                         size_t s1 = cache.get(y, x, 1);
                         if (s0 == 0 || s1 == 0) continue;
-                        minorminer_assert(s0-1 < rows);
-                        minorminer_assert(s1-1 < cols);
-                        size_t maxlen = cells.topo.biclique_length(y, y+h-1, x, x+w-1);
+                        minorminer_assert(size_x(s0-1) < rows);
+                        minorminer_assert(size_y(s1-1) < cols);
+                        size_t maxlen = cells.topo.biclique_length(y, y+h-1u, x, x+w-1u);
                         size_t prevlen = chainlength[s0-1][s1-1];
                         if(prevlen == 0 || prevlen > maxlen) {
                             chainlength[s0-1][s1-1] = maxlen;
-                            biclique_bounds[s0-1][s1-1] = bound_t(y, y+h-1, x, x+w-1);
+                            biclique_bounds[s0-1][s1-1] = bound_t(y, y+h-1u, x, x+w-1u);
                         }
                     }
                 }
@@ -222,21 +220,22 @@ class biclique_yield_cache {
   public:
     class iterator {
         size_t s0, s1;
-        const size_t &rows, &cols;
+        const size_x &rows;
+        const size_y &cols;
         const vector<vector<size_t>> &chainlength;
         const vector<vector<bound_t>> &bounds;
         const bundle_cache<topo_spec> &bundles;
         void adv() {
-            while(s0 < rows && chainlength[s0][s1] == 0) inc();
+            while(size_x(s0) < rows && chainlength[s0][s1] == 0) inc();
         }
         bool inc() {
-            if(s0 >= rows) return false;
+            if(size_x(s0) >= rows) return false;
             s1++;
-            if(s1 == cols) { s1 = 0; s0++; }
+            if(size_y(s1) == cols) { s1 = 0; s0++; }
             return true;
         }
       public:
-        iterator(size_t _s0, size_t _s1, const size_t &r, const size_t &c,
+        iterator(size_t _s0, size_t _s1, const size_x &r, const size_y &c,
                  const vector<vector<size_t>> &cl,
                  const vector<vector<bound_t>> &_bounds,
                  const bundle_cache<topo_spec> &_bundles) :
@@ -262,7 +261,7 @@ class biclique_yield_cache {
         return iterator(0, 0, rows, cols, chainlength, biclique_bounds, bundles);
     }
     iterator end() const {
-        return iterator(rows, 0, rows, cols, chainlength, biclique_bounds, bundles);
+        return iterator(coordinate_index(rows), 0, rows, cols, chainlength, biclique_bounds, bundles);
     }
 };
 
diff --git a/include/busclique/bundle_cache.hpp b/include/busclique/bundle_cache.hpp
index ba2b075e..0fc7bd95 100644
--- a/include/busclique/bundle_cache.hpp
+++ b/include/busclique/bundle_cache.hpp
@@ -39,45 +39,51 @@ class bundle_cache {
     }
     bundle_cache(const cell_cache<topo_spec> &c) :
                  cells(c),
-                 linestride{binom(c.topo.dim[0]), binom(c.topo.dim[1])},
-                 orthstride(c.topo.dim[1]*linestride[0]),
-                 line_mask(new uint8_t[orthstride + c.topo.dim[0]*linestride[1]]{}) {
+                 linestride{binom(c.topo.dim_y), binom(c.topo.dim_x)},
+                 orthstride(coordinate_index(c.topo.dim_x)*linestride[0]),
+                 line_mask(new uint8_t[orthstride + coordinate_index(c.topo.dim_y)*linestride[1]]{}) {
         compute_line_masks();
     }
 
-    size_t score(size_t yc, size_t xc, size_t y0, size_t y1, size_t x0, size_t x1) const {
-        return min(get_line_score(0, xc, y0, y1), get_line_score(1, yc, x0, x1));
+    size_t score(size_y yc, size_x xc, size_y y0, size_y y1, size_x x0, size_x x1) const {
+        return min(
+            get_line_score(0, vert(xc), vert(y0), vert(y1)),
+            get_line_score(1, horz(yc), horz(x0), horz(x1))
+        );
     }
 
-    void inflate(size_t yc, size_t xc, size_t y0, size_t y1, size_t x0, size_t x1,
+    void inflate(size_y yc, size_x xc, size_y y0, size_y y1, size_x x0, size_x x1,
                  vector<vector<size_t>> &emb) const {
-        uint8_t k0 = get_line_mask(0, xc, y0, y1);
-        uint8_t k1 = get_line_mask(1, yc, x0, x1);
+        uint8_t k0 = get_line_mask(0, vert(xc), vert(y0), vert(y1));
+        uint8_t k1 = get_line_mask(1, horz(yc), horz(x0), horz(x1));
         while (k0 && k1) {
             emb.emplace_back(0);
             vector<size_t> &chain = emb.back();
-            cells.topo.construct_line(0, xc, y0, y1, first_bit[k0], chain);
-            cells.topo.construct_line(1, yc, x0, x1, first_bit[k1], chain);
+            cells.topo.construct_line(0, vert(xc), vert(y0), vert(y1), first_bit[k0], chain);
+            cells.topo.construct_line(1, horz(yc), horz(x0), horz(x1), first_bit[k1], chain);
             k0 ^= mask_bit[first_bit[k0]];
             k1 ^= mask_bit[first_bit[k1]];
         }
     }
 
-    void inflate(size_t y0, size_t y1, size_t x0, size_t x1,
+    void inflate(size_y y0, size_y y1, size_x x0, size_x x1,
                  vector<vector<size_t>> &emb) const {
         inflate(0, y0, y1, x0, x1, emb);
         inflate(1, y0, y1, x0, x1, emb);
     }
 
-    void inflate(size_t u, size_t y0, size_t y1, size_t x0, size_t x1,
+    void inflate(bool u, size_y y0, size_y y1, size_x x0, size_x x1,
                  vector<vector<size_t>> &emb) const {
-        size_t w0, w1, z0, z1;
+        size_w w0, w1;
+        size_z z0, z1;
         if(u) {
-            w0 = y0; w1 = y1; z0 = x0; z1 = x1;
+            w0 = horz(y0); w1 = horz(y1);
+            z0 = horz(x0); z1 = horz(x1);
         } else {
-            w0 = x0; w1 = x1; z0 = y0; z1 = y1;
+            w0 = vert(x0); w1 = vert(x1);
+            z0 = vert(y0); z1 = vert(y1);
         }
-        for(size_t w = w0; w <= w1; w++) {
+        for(size_w w = w0; w <= w1; w++) {
             uint8_t k = get_line_mask(u, w, z0, z1);
             while(k) {
                 emb.emplace_back(0);
@@ -88,34 +94,46 @@ class bundle_cache {
         }
     }
 
-    size_t length(size_t yc, size_t xc, size_t y0, size_t y1, size_t x0, size_t x1) const {
-        return cells.topo.line_length(0, xc, y0, y1) + cells.topo.line_length(1, yc, x0, x1);
+    size_t length(size_y yc, size_x xc, size_y y0, size_y y1, size_x x0, size_x x1) const {
+        return cells.topo.line_length(0, vert(xc), vert(y0), vert(y1)) + cells.topo.line_length(1, horz(yc), horz(x0), horz(x1));
     }
 
-    inline uint8_t get_line_score(size_t u, size_t w, size_t z0, size_t z1) const {
+    inline uint8_t get_line_score(bool u, size_w w, size_z z0, size_z z1) const {
         return popcount[get_line_mask(u, w, z0, z1)];
     }
 
   private:
-    inline uint8_t get_line_mask(size_t u, size_t w, size_t z0, size_t z1) const {
-        minorminer_assert(u < 2);
-        minorminer_assert(w < cells.topo.dim[1-u]);
+  
+    inline uint8_t &get_line_mask(bool u, size_w w, size_z z0, size_z z1) const {
+        minorminer_assert(u?(horz(w)<cells.topo.dim_y):(vert(w)<cells.topo.dim_x));
         minorminer_assert(z0 <= z1);
-        minorminer_assert(z1 < cells.topo.dim[u]);
-        return line_mask[u*orthstride + w*linestride[u] + binom(z1) + z0];
+        minorminer_assert(u?(horz(z1)<cells.topo.dim_x):(vert(z1)<cells.topo.dim_y));
+        size_t index = coordinate_converter::bundle_cache_index(u, w, z0, z1, orthstride, linestride[u]);
+        minorminer_assert(index < orthstride + coordinate_index(cells.topo.dim_y)*linestride[1]);
+        return line_mask[index];
     }
 
     void compute_line_masks() {
-        for(size_t u = 0; u < 2; u++) {
-            for (size_t w = 0; w < cells.topo.dim[1-u]; w++) {
-                for (size_t z = 0; z < cells.topo.dim[u]; z++) {
-                    uint8_t *t = line_mask + u*orthstride + w*linestride[u] + binom(z);
-                    uint8_t m = t[z] = cells.qmask(u, w, z);
-                    for(size_t z0 = z; z0--;)
-                        m = t[z0] = m & cells.emask(u, w, z0+1);
+        uint8_t *mask = line_mask;
+        struct {bool u; size_w dim_w; size_z dim_z; } sides[2] = {
+            {0, vert(cells.topo.dim_x), vert(cells.topo.dim_y)},
+            {1, horz(cells.topo.dim_y), horz(cells.topo.dim_x)},
+        };
+        for (auto side : sides) {
+            for (size_w w = 0; w < side.dim_w; w++) {
+                for (size_z z = 0; z < side.dim_z; z++) {
+                    mask+= coordinate_index(z);
+                    minorminer_assert(mask+coordinate_index(z) == &get_line_mask(side.u, w, z, z));
+                    uint8_t m = mask[coordinate_index(z)] = cells.qmask(side.u, w, z);
+                    for(size_z z0 = z; z0-->size_z(0);) {
+                        minorminer_assert(mask+coordinate_index(z0) == &get_line_mask(side.u, w, z0, z));
+                        m = mask[coordinate_index(z0)] = m & cells.emask(side.u, w, z0+1u);
+                    }
                 }
+                mask += coordinate_index(side.dim_z);
             }
         }
+        minorminer_assert(mask == line_mask + orthstride + coordinate_index(cells.topo.dim_y)*linestride[1]);
     }
 };
 
diff --git a/include/busclique/cell_cache.hpp b/include/busclique/cell_cache.hpp
index 9d035e36..c6299077 100644
--- a/include/busclique/cell_cache.hpp
+++ b/include/busclique/cell_cache.hpp
@@ -49,11 +49,29 @@ class cell_cache {
                borrow(true), topo(p), nodemask(nm), edgemask(em) {}
 
   public:
-    uint8_t qmask(size_t u, size_t w, size_t z) const {
-        return nodemask[topo.cell_addr(u, w, z)];
+    uint8_t qmask(bool u, size_w w, size_z z) const {
+        return nodemask[topo.cell_index(u, w, z)];
     }
-    uint8_t emask(size_t u, size_t w, size_t z) const {
-        return edgemask[topo.cell_addr(u, w, z)];
+    uint8_t emask(bool u, size_w w, size_z z) const {
+        return edgemask[topo.cell_index(u, w, z)];
+    }
+
+    uint8_t score(size_y y, size_x x) const {
+        return min(popcount[qmask(0, vert(x), vert(y))],
+                   popcount[qmask(1, horz(y), horz(x))]);
+    }
+
+    void inflate(size_y y, size_x x, vector<vector<size_t>> &emb) const {
+        uint8_t k0 = qmask(0, vert(x), vert(y));
+        uint8_t k1 = qmask(1, horz(y), horz(x));
+        while (k0 && k1) {
+            emb.emplace_back(0);
+            vector<size_t> &chain = emb.back();
+            topo.construct_line(0, vert(x), vert(y), vert(y), first_bit[k0], chain);
+            topo.construct_line(1, horz(y), horz(x), horz(x), first_bit[k1], chain);
+        }
+        k0 ^= mask_bit[first_bit[k0]];
+        k1 ^= mask_bit[first_bit[k1]];
     }
 
 };
diff --git a/include/busclique/clique_cache.hpp b/include/busclique/clique_cache.hpp
index 9534c3c3..09f0a69b 100644
--- a/include/busclique/clique_cache.hpp
+++ b/include/busclique/clique_cache.hpp
@@ -19,37 +19,34 @@ namespace busclique {
 
 class maxcache {
   public:
-    const size_t rows;
-    const size_t cols;
+    const size_y rows;
+    const size_x cols;
   private:
     size_t *mem;
+    size_t index(size_y y, size_x x) const {
+        return coordinate_converter::grid_index(y, x, rows, cols);
+    }
   public:
     maxcache(size_t r, size_t c, size_t *m) : rows(r), cols(c), mem(m) {};
-    void setmax(size_t y, size_t x, size_t s, corner c) { 
-        minorminer_assert(y < rows);
-        minorminer_assert(x < cols);
+    void setmax(size_y y, size_x x, size_t s, corner c) { 
         size_t old_s = score(y, x);
         if(s == old_s) {
-            mem[y*cols+x] |= c;
+            mem[index(y, x)] |= c;
         } else if(s > old_s) {
-            mem[y*cols+x] = (s << corner::shift) | c;
+            mem[index(y, x)] = (s << corner::shift) | c;
         }
     }
-    size_t score(size_t y, size_t x) const {
-        minorminer_assert(y < rows);
-        minorminer_assert(x < cols);
-        return mem[y*cols + x] >> corner::shift;
+    size_t score(size_y y, size_x x) const {
+        return mem[index(y, x)] >> corner::shift;
     }
-    corner corners(size_t y, size_t x) const {
-        minorminer_assert(y < rows);
-        minorminer_assert(x < cols);
-        return static_cast<corner>(mem[y*cols + x] & corner::mask);
+    corner corners(size_y y, size_x x) const {
+        return static_cast<corner>(mem[index(y, x)] & corner::mask);
     }
 };
 
 class zerocache {
   public:
-    inline constexpr size_t score(size_t, size_t) const { return 0; }
+    inline constexpr size_t score(size_y, size_x) const { return 0; }
 };
 
 
@@ -70,13 +67,13 @@ class clique_cache {
     size_t *mem;
 
     size_t memrows(size_t i) const {
-        if (i < width) return cells.topo.dim[0]-i;
+        if (i < width) return coordinate_index(cells.topo.dim_y)-i;
         else if (i == width) return 1;
         else throw "memrows";
     }
     size_t memcols(size_t i) const {
-        if (i + 1 < width) return cells.topo.dim[1]-width+i+2;
-        else if (i + 1 == width) return cells.topo.dim[1];
+        if (i + 1 < width) return coordinate_index(cells.topo.dim_x)-width+i+2;
+        else if (i + 1 == width) return coordinate_index(cells.topo.dim_x);
         else throw "memcols";
     }
 
@@ -90,7 +87,7 @@ class clique_cache {
             size += memsize(i) + 1;
         return size;
     }
-    static constexpr bool nocheck(size_t,size_t,size_t,size_t,size_t,size_t) {return true;}
+    static constexpr bool nocheck(size_y,size_x,size_y,size_y,size_x,size_x) {return true;}
 
   public:
     clique_cache(const cell_cache<topo_spec> &c, const bundle_cache<topo_spec> &b, size_t w) :
@@ -102,8 +99,8 @@ class clique_cache {
             bundles(b),
             width(w),
             mem(new size_t[memsize()]{}) {
-        minorminer_assert(width <= cells.topo.dim[0]);
-        minorminer_assert(width <= cells.topo.dim[1]);
+        minorminer_assert(size_y(width) <= cells.topo.dim_y);
+        minorminer_assert(size_x(width) <= cells.topo.dim_x);
         mem[0] = width;
         for(size_t i = 1; i < width; i++)
             mem[i] = mem[i-1] + memsize(i-1);
@@ -126,8 +123,8 @@ class clique_cache {
         for(size_t i = 0; i < width-1; i++) {
             maxcache m = get(i);
             std::cout << mem[i] << ':' << memsize(i) << "?"<< std::endl;
-            for(size_t y = 0; y < m.rows; y++) {
-                for(size_t x = 0; x < m.cols; x++) {
+            for(size_y y = 0; y < m.rows; y++) {
+                for(size_x x = 0; x < m.cols; x++) {
                     std::cout << m.score(y, x) << '~' << (m.corners(y, x)) << " ";
                 }
                 std::cout << std::endl;
@@ -140,36 +137,36 @@ class clique_cache {
     template<typename C>
     void compute_cache(C &check) {
         {
-            size_t h = 1;
-            size_t w = width;
+            size_y h = 1;
+            size_x w = width;
             auto zero = zerocache();
             extend_cache(zero, h, w, check, corner::SW, corner::SE);
         }
         for(size_t i = 1; i < width-1; i++) {
-            size_t h = i+1;
-            size_t w = width-i;
-            maxcache prev = get(h-2);
+            size_y h = i+1;
+            size_x w = width-i;
+            maxcache prev = get(coordinate_index(h-2u));
             extend_cache(prev, h, w, check, corner::NE, corner::NW, corner::SW, corner::SE);
         }
         {
-            size_t h = width;
-            size_t w = 1;
-            maxcache prev = get(h-2);
+            size_y h = width;
+            size_x w = 1;
+            maxcache prev = get(coordinate_index(h-2u));
             extend_cache(prev, h, w, check, corner::NE, corner::SE);
         }
     }
 
     template<typename T, typename C, typename ... Corners>
-    inline void extend_cache(const T &prev, size_t h, size_t w, C &check, Corners ... corners) {
-        maxcache next = get(h-1);
-        for(size_t y = 0; y <= cells.topo.dim[0]-h; y++)
-            for(size_t x = 0; x <= cells.topo.dim[1]-w; x++)
-                extend_cache(prev, next, y, y+h-1, x, x+w-1, check, corners...);
+    inline void extend_cache(const T &prev, size_y h, size_x w, C &check, Corners ... corners) {
+        maxcache next = get(coordinate_index(h-1u));
+        for(size_y y = 0; y <= cells.topo.dim_y-h; y++)
+            for(size_x x = 0; x <= cells.topo.dim_x-w; x++)
+                extend_cache(prev, next, y, y+h-1u, x, x+w-1u, check, corners...);
     }
 
     template<typename T, typename C, typename ... Corners>
     inline void extend_cache(const T &prev, maxcache &next,
-                       size_t y0, size_t y1, size_t x0, size_t x1,
+                       size_y y0, size_y y1, size_x x0, size_x x1,
                        C &check, corner c, Corners ... corners) {
         extend_cache(prev, next, y0, y1, x0, x1, check, c);
         extend_cache(prev, next, y0, y1, x0, x1, check, corners...);
@@ -177,16 +174,16 @@ class clique_cache {
 
     template<typename T, typename C>
     inline void extend_cache(const T &prev, maxcache &next,
-                       size_t y0, size_t y1, size_t x0, size_t x1,
+                       size_y y0, size_y y1, size_x x0, size_x x1,
                        C &check, corner c) {
-        size_t next_y, prev_y, yc; next_y = prev_y = yc = y0;
-        size_t next_x, prev_x, xc; next_x = prev_x = xc = x0;
+        size_y next_y, prev_y, yc; next_y = prev_y = yc = y0;
+        size_x next_x, prev_x, xc; next_x = prev_x = xc = x0;
         corner skip_c;
         switch(c) {
-            case corner::NW: next_x = x0+1; prev_y = y0+1; skip_c = corner::NWskip; break;
-            case corner::SW: next_x = x0+1; yc = y1;       skip_c = corner::SWskip; break;
-            case corner::NE: xc = x1;       prev_y = y0+1; skip_c = corner::NEskip; break;
-            case corner::SE: xc = x1;       yc = y1;       skip_c = corner::SEskip; break;
+            case corner::NW: next_x = x0+1u; prev_y = y0+1u; skip_c = corner::NWskip; break;
+            case corner::SW: next_x = x0+1u; yc = y1;        skip_c = corner::SWskip; break;
+            case corner::NE: xc = x1;        prev_y = y0+1u; skip_c = corner::NEskip; break;
+            case corner::SE: xc = x1;        yc = y1;        skip_c = corner::SEskip; break;
             default: throw std::exception();
         }
         size_t score = prev.score(prev_y, prev_x);
@@ -198,7 +195,7 @@ class clique_cache {
     }
 
     corner inflate_first_ell(vector<vector<size_t>> &emb,
-                             size_t &y, size_t &x, size_t h, size_t w, corner c) const {
+                             size_y &y, size_x &x, size_y h, size_x w, corner c) const {
         corner c0 = static_cast<corner>(1<< first_bit[c]);
         switch(c0) {
             case corner::NW: x--; bundles.inflate(y,  x,  y,y+h,x,x+w, emb); y++; break;
@@ -216,10 +213,13 @@ class clique_cache {
 
   public:
     bool extract_solution(vector<vector<size_t>> &emb) const {
-        size_t bx, by, bscore=0;
+        minorminer_assert(emb.size() == 0);
+        size_y by;
+        size_x bx;
+        size_t bscore=0;
         maxcache scores = get(width-1);
-        for(size_t y = 0; y < scores.rows; y++) {
-            for(size_t x = 0; x < scores.cols; x++) {
+        for(size_y y = 0; y < scores.rows; y++) {
+            for(size_x x = 0; x < scores.cols; x++) {
                 size_t s = scores.score(y, x);
                 if (bscore < s) { 
                     bx = x; by = y; bscore = s;
@@ -242,8 +242,8 @@ class clique_iterator {
     const cell_cache<topo_spec> &cells;
     const clique_cache<topo_spec> &cliq;
     size_t width;
-    vector<std::tuple<size_t, size_t, corner>> basepoints;
-    vector<std::tuple<size_t, size_t, size_t, corner>> stack;
+    vector<std::tuple<size_y, size_x, corner>> basepoints;
+    vector<std::tuple<size_t, size_y, size_x, corner>> stack;
     vector<vector<size_t>> emb;
     
   public:
@@ -257,8 +257,8 @@ class clique_iterator {
         //location, and a corner c to denote the orientation of the ell.  
         size_t score = 0;
         maxcache scores = cliq.get(width-1);
-        for(size_t y = 0; y < scores.rows; y++)
-            for(size_t x = 0; x < scores.cols; x++) {
+        for(size_y y = 0; y < scores.rows; y++)
+            for(size_x x = 0; x < scores.cols; x++) {
                 size_t s = scores.score(y, x);
                 if(s < score) continue;
                 else if (s > score) basepoints.clear();
@@ -271,7 +271,9 @@ class clique_iterator {
     bool advance() {
         //first, peel back the zeros (exhausted solutions) until we hit a
         //nonzero corner
-        size_t n, by, bx;
+        size_t n;
+        size_y by;
+        size_x bx;
         corner bc;
         while(stack.size()) {
             std::tie(n, by, bx, bc) = stack.back();                
@@ -296,7 +298,9 @@ class clique_iterator {
         //function -- we do one step at a time and return true if there's still
         //more work to do; false otherwise.
 
-        size_t n, by, bx, i;
+        size_t n, i;
+        size_y by;
+        size_x bx;
         corner lc, bc;
         if(stack.size() == 0) {
             //in this case, we've used up the last basepoint that was examined
@@ -349,20 +353,22 @@ class clique_yield_cache {
     vector<size_t> clique_yield;
     vector<vector<vector<size_t>>> best_embeddings;
 
-  public:
-    clique_yield_cache(const cell_cache<zephyr_spec> &cells) :
-                       length_bound(4+cells.topo.zdim),
-                       clique_yield(length_bound, 0), 
-                       best_embeddings(length_bound, empty_emb) { compute_cache(cells); }
+    size_t compute_length_bound(const zephyr_spec &topo) {
+        return 4+topo.zdim;
+    }
 
-    clique_yield_cache(const cell_cache<pegasus_spec> &cells) :
-                       length_bound(5 + cells.topo.pdim),
-                       clique_yield(length_bound, 0), 
-                       best_embeddings(length_bound, empty_emb) { compute_cache(cells); }
+    size_t compute_length_bound(const pegasus_spec &topo) {
+        return 5 + topo.pdim;
+    }
 
-    clique_yield_cache(const cell_cache<chimera_spec> &cells) :
-                       length_bound(2+std::min(cells.topo.dim[0], cells.topo.dim[1])),
-                       clique_yield(length_bound, 0), 
+    size_t compute_length_bound(const chimera_spec &topo) {
+        return 2+coordinate_converter::min(topo.dim_y, topo.dim_x);
+    }
+
+  public:
+    clique_yield_cache(const cell_cache<topo_spec> &cells) :
+                       length_bound(compute_length_bound(cells.topo)),
+                       clique_yield(length_bound, 0),
                        best_embeddings(length_bound, empty_emb) { compute_cache(cells); }
 
   private:
@@ -386,8 +392,8 @@ class clique_yield_cache {
 
     void compute_cache_width_1(const cell_cache<topo_spec> &cells,
                                const bundle_cache<topo_spec> &bundles) {
-        for(size_t y = 0; y < cells.topo.dim[0]; y++)
-            for(size_t x = 0; x < cells.topo.dim[1]; x++) {
+        for(size_y y = 0; y < cells.topo.dim_y; y++)
+            for(size_x x = 0; x < cells.topo.dim_x; x++) {
                 size_t score = bundles.score(y,x,y,y,x,x);
                 if(score > clique_yield[2]) {
                     vector<vector<size_t>> emb;
@@ -403,30 +409,30 @@ class clique_yield_cache {
 
     void compute_cache_width_gt_1(const cell_cache<pegasus_spec> &cells,
                                   const bundle_cache<pegasus_spec> &bundles) {
-        size_t maxw = min(cells.topo.dim[0], cells.topo.dim[1]);
+        size_t maxw = coordinate_converter::min(cells.topo.dim_y, cells.topo.dim_x);
 
         for(size_t w = 2; w <= maxw; w++) {
             size_t min_length, max_length;
-            get_length_range(bundles, w, min_length, max_length);
+            get_length_range(cells.topo, w, min_length, max_length);
+            {
+                clique_cache<pegasus_spec> cliques(cells, bundles, w);
+                process_cliques(cliques);
+            }
             for(size_t len = min_length; len < max_length; len++) {
-                auto check_length = [&bundles, len](size_t yc, size_t xc,
-                                                    size_t y0, size_t y1,
-                                                    size_t x0, size_t x1){
+                auto check_length = [&bundles, len](size_y yc, size_x xc,
+                                                    size_y y0, size_y y1,
+                                                    size_x x0, size_x x1){
                     return bundles.length(yc,xc,y0,y1,x0,x1) <= len; 
                 };
                 clique_cache<pegasus_spec> cliques(cells, bundles, w, check_length);
                 process_cliques(cliques);
             }
-            {
-                clique_cache<pegasus_spec> cliques(cells, bundles, w);
-                process_cliques(cliques);
-            }
         }
     }
     
     void compute_cache_width_gt_1(const cell_cache<chimera_spec> &cells,
                                   const bundle_cache<chimera_spec> &bundles) {
-        size_t maxw = min(cells.topo.dim[0], cells.topo.dim[1]);
+        size_t maxw = coordinate_converter::min(cells.topo.dim_y, cells.topo.dim_x);
 
         for(size_t w = 2; w <= maxw; w++) {
             clique_cache<topo_spec> cliques(cells, bundles, w);
@@ -435,10 +441,9 @@ class clique_yield_cache {
 
     }
 
-    
     void compute_cache_width_gt_1(const cell_cache<zephyr_spec> &cells,
                                   const bundle_cache<zephyr_spec> &bundles) {
-        size_t maxw = min(cells.topo.dim[0], cells.topo.dim[1]);
+        size_t maxw = coordinate_converter::min(cells.topo.dim_y, cells.topo.dim_x);
 
         for(size_t w = 2; w <= maxw; w++) {
             clique_cache<topo_spec> cliques(cells, bundles, w);
@@ -447,11 +452,10 @@ class clique_yield_cache {
 
     }
 
-
     void compute_cache(const cell_cache<zephyr_spec> &cells) {
         bundle_cache<zephyr_spec> bundles(cells);
         compute_cache_width_1(cells, bundles);
-        compute_cache_width_gt_1(cells, bundles); 
+        compute_cache_width_gt_1(cells, bundles);
     }
 
     void compute_cache(const cell_cache<chimera_spec> &cells) {
@@ -469,39 +473,32 @@ class clique_yield_cache {
     const vector<vector<vector<size_t>>> &embeddings() {
         return best_embeddings;
     }
-
-  private:
-    void get_length_range(const bundle_cache<pegasus_spec> &bundles, size_t width, size_t &min_length, size_t &max_length) {
+  
+    static void get_length_range(const pegasus_spec &topo, size_t width, size_t &min_length, size_t &max_length) {
         max_length = 0;
         min_length = ~max_length;
-        for(size_t w = 1, h = width; w <= width; w++, h--) {
-            for(size_t y = 6; y < 12; y++) {
-                for(size_t x = 6; x < 12; x++) {
+        for(size_t i = 0; i < width; i++) {
+            size_y h = width-i;
+            size_x w = i+1;
+            for(size_y y = 6; y < size_y(12); y++) {
+                for(size_x x = 6; x < size_x(12); x++) {
                     size_t length;
-                    length = bundles.length(y, x, y, y+h-1, x, x+w-1);
+                    length = topo.line_length(0, vert(x), vert(y), vert(y+h-1u)) + topo.line_length(1, horz(y), horz(x), horz(x+w-1u));
                     max_length = max(max_length, length);
                     min_length = min(min_length, length);
-                    length = bundles.length(y+h-1, x, y, y+h-1, x, x+w-1);
+                    length = topo.line_length(0, vert(x), vert(y), vert(y+h-1u)) + topo.line_length(1, horz(y+h-1u), horz(x), horz(x+w-1u));
                     max_length = max(max_length, length);
                     min_length = min(min_length, length);
-                    length = bundles.length(y+h-1, x+w-1, y, y+h-1, x, x+w-1);
+                    length = topo.line_length(0, vert(x+w-1u), vert(y), vert(y+h-1u)) + topo.line_length(1, horz(y), horz(x), horz(x+w-1u));
                     max_length = max(max_length, length);
                     min_length = min(min_length, length);
-                    length = bundles.length(y, x+w-1, y, y+h-1, x, x+w-1);
+                    length = topo.line_length(0, vert(x+w-1u), vert(y), vert(y+h-1u)) + topo.line_length(1, horz(y+h-1u), horz(x), horz(x+w-1u));
                     max_length = max(max_length, length);
                     min_length = min(min_length, length);
                 }
             }
         }
     }
-
-    void get_length_range(const bundle_cache<chimera_spec> &, size_t width, size_t &min_length, size_t &max_length) {
-        max_length = min_length = width+1;
-    }
-    void get_length_range(const bundle_cache<zephyr_spec> &, size_t width, size_t &min_length, size_t &max_length) {
-        max_length = width+3;
-        min_length = width+1;
-    }
 };
 
 
diff --git a/include/busclique/coordinate_types.hpp b/include/busclique/coordinate_types.hpp
new file mode 100644
index 00000000..ebb21951
--- /dev/null
+++ b/include/busclique/coordinate_types.hpp
@@ -0,0 +1,461 @@
+// Copyright 2021 D-Wave Systems Inc.
+//
+//    Licensed under the Apache License, Version 2.0 (the "License");
+//    you may not use this file except in compliance with the License.
+//    You may obtain a copy of the License at
+//
+//        http://www.apache.org/licenses/LICENSE-2.0
+//
+//    Unless required by applicable law or agreed to in writing, software
+//    distributed under the License is distributed on an "AS IS" BASIS,
+//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//    See the License for the specific language governing permissions and
+//    limitations under the License.
+
+#pragma once
+
+namespace busclique {
+
+size_t binom(size_t);
+
+/*
+Coordinate arithmetic is absolutely fraught with challenges.  In order to 
+reduce the possibility of bugs, we've introduced an extremely pedantic system
+of coordinate types.  The busclique algorithms use two kinds of coordinate 
+systems:
+
+  * matrix coordinates (y, x) which are featured in the chimera coordinate
+    system (y, x, u, k) -- where u indicates the 'orientation' of a qubit,
+    either vertical(u=0) or horizontal(u=1) and k indicates the 'shore index'
+    of a qubit (irrelevant here)
+
+  * relative coordinates (w, z) which are features in the pegasus and zephyr
+    coordinate systems (u, w, k[, j], z) -- where u is the orientation as
+    above and k and j are similarly irrelevant.
+
+The relationship between matrix and relative coordinates is that
+
+  * (y, x, u=0) is equivalent to (z=y, w=x, u=0), and
+
+  * (y, x, u=1) is equivalent to (w=y, z=x, u=1)
+
+We have encoded this equivalence in four types, size_y, size_x, size_w, and
+size_z.  Each type supports arithmetic with itself, but forbids confusion.  For
+example,
+
+    size_y y0, y1;
+    size_x x0, x0;
+    (y0 + y1*y0 + y1)/(y1-y0) // cool
+    y0 + x0;                  // compile error!
+
+But, we need to convert between matrix and relative coordinates frequently!
+We do that with the `vert` and `horz` functions:
+
+    bool u = 0;
+    size_y y;
+    size_x x;
+    size_w w0, w1;
+    size_z z0, z1;
+    w1 = w0 + vert(y);
+    z1 = z0 + vert(x);
+    u = 1;
+    w1 = w0 + horz(x);
+    z1 = z0 + horz(y);
+
+Sometimes, we *really* need to mix things up.  There are two ways to convert
+a coordinate to a size_t: implicitly through `coordinate_base::operator size_t`,
+or explicitly through `coordinate_index()`.  The implicit conversion is
+private, to prevent accidental implicit conversions.  The `coordinate_converter`
+class is a friend to the coordinate types, and that's the only place we need to
+be very careful.  We use the `coordinate_index()` method in very few one-off
+instances.  Ideally, it should only be used for indexing into arrays:
+
+    size_y y;
+    uint8_t mem[100];
+    mem[coordinate_index(y)];
+
+This entire file, and the associated refactor, is a very heavy-handed approach
+to fixing a bug (segfaulting non-square Chimera graphs).  However, it reduces a
+significant amount of anxiety in developing and extending this library.  In 
+small tests, code involving coordinate classes is optimized down to identical 
+assembly to the same code using size_t in their place.  We'll see if that's true
+in a complex codebase...
+
+Update: Curious.  The performance was rather miserable on my system (gcc 9.3.0);
+at around 3x slower than before the change that I hoped would be "zero cost."  I
+decided to roll back very carefully; not changing any more code than necessary
+to ensure a clean test.  I commented out the coordinate types, and replaced them
+with `typedef size_t ...`.  In the initial version, the coordinate types had 
+methods, specifically, `coordinate_base.index`, `coordinate_base.horz` and 
+`coordinate_base.vert`.  I replaced these with global functions because we can't
+attach methods to a size_t.  Sure enough, the performance returned to normal.
+For sake of a fuller understanding, I refactored `coordinate_base` to support 
+the same syntax, and lo and behold, the performance stayed the same.
+
+Since I'm using a very up-to-date gcc, I am reluctant to put the coordinate
+types into the release build.  If I was a docker shepherd, I'd probably spool up
+a few dozen instances and benchmark on all the gcc's and clang's and msvc's that
+I could get my hands on.  Alas, it is much easier to rely on the fact that CI
+tests with debugging enabled, and retain the flag-dependent build since I've
+already written it.  A note, I'd be oh-so-slightly more comfortable if `vert`,
+`horz`, and `coordinate_index` were macros.  But I take a deep breath and remind
+myself what century I'm programming in.
+
+*/
+
+
+class coordinate_converter;
+
+#if defined CPPDEBUG || defined SAFE_COORDS
+
+//! a tag-struct used to indicate y-coordinates
+struct coord_y {};
+
+//! a tag-struct used to indicate x-coordinates
+struct coord_x {};
+
+//! a tag-struct used to indicate w-coordinates -- given a qubit, the w
+//! coordinate corresponds to an offset perpendicular to the qubit
+struct coord_w {};
+
+//! a tag-struct used to indicate z-coordinates -- given a qubit, the z
+//! coordinate corresponds to an offset parallel to the qubit
+struct coord_z {};
+
+//! This struct defines the relationships between y, x, w, and z in vertical
+//! context.  That is, the y coordinate is a parallel offset (z coordinate)
+//! from the perspective of a vertical qubit.  Likewise, the x coordinate is a
+//! perpendicular offset (w coordinate)
+template<typename T> struct vert_lut;
+template<> struct vert_lut<coord_y> { typedef coord_z type; };
+template<> struct vert_lut<coord_x> { typedef coord_w type; };
+template<> struct vert_lut<coord_w> { typedef coord_x type; };
+template<> struct vert_lut<coord_z> { typedef coord_y type; };
+
+//! This sruct defines the relationships between y, x, w, and z in horizontal
+//! context.  That is, the y coordinate is a perpendicular offset (w coordinate)
+//! from the perspective of a vertical qubit.  Likewise, the x coordinate is a
+//! parallel offset (z coordinate)
+template<typename T> struct horz_lut;
+template<> struct horz_lut<coord_y> { typedef coord_w type; };
+template<> struct horz_lut<coord_x> { typedef coord_z type; };
+template<> struct horz_lut<coord_w> { typedef coord_y type; };
+template<> struct horz_lut<coord_z> { typedef coord_x type; };
+
+
+// Check that vert_lut is idempotent
+static_assert(std::is_same<vert_lut<vert_lut<coord_y>::type>::type, coord_y>::value, "idempotence check failed");
+static_assert(std::is_same<vert_lut<vert_lut<coord_x>::type>::type, coord_x>::value, "idempotence check failed");
+static_assert(std::is_same<vert_lut<vert_lut<coord_w>::type>::type, coord_w>::value, "idempotence check failed");
+static_assert(std::is_same<vert_lut<vert_lut<coord_z>::type>::type, coord_z>::value, "idempotence check failed");
+
+// Check that horz_lut is idempotent
+static_assert(std::is_same<horz_lut<horz_lut<coord_y>::type>::type, coord_y>::value, "idempotence check failed");
+static_assert(std::is_same<horz_lut<horz_lut<coord_x>::type>::type, coord_x>::value, "idempotence check failed");
+static_assert(std::is_same<horz_lut<horz_lut<coord_w>::type>::type, coord_w>::value, "idempotence check failed");
+static_assert(std::is_same<horz_lut<horz_lut<coord_z>::type>::type, coord_z>::value, "idempotence check failed");
+
+// Check that vert_lut and horz_lut are orthogonal
+static_assert(std::is_same<vert_lut<horz_lut<coord_y>::type>::type, coord_x>::value, "orthogonality check failed");
+static_assert(std::is_same<vert_lut<horz_lut<coord_x>::type>::type, coord_y>::value, "orthogonality check failed");
+static_assert(std::is_same<vert_lut<horz_lut<coord_w>::type>::type, coord_z>::value, "orthogonality check failed");
+static_assert(std::is_same<vert_lut<horz_lut<coord_z>::type>::type, coord_w>::value, "orthogonality check failed");
+
+static_assert(std::is_same<horz_lut<vert_lut<coord_y>::type>::type, coord_x>::value, "orthogonality check failed");
+static_assert(std::is_same<horz_lut<vert_lut<coord_x>::type>::type, coord_y>::value, "orthogonality check failed");
+static_assert(std::is_same<horz_lut<vert_lut<coord_w>::type>::type, coord_z>::value, "orthogonality check failed");
+static_assert(std::is_same<horz_lut<vert_lut<coord_z>::type>::type, coord_w>::value, "orthogonality check failed");
+
+//! This template class represents a generic coordinate type.  The intention is
+//! that the template parameter T is one of coord_w, coord_z, coord_y, coord_x.
+//! Class instances are meant to behave similar to a size_t; but similar to a
+//! system of units, we disallow implicit conversions between coordinates of
+//! different kinds.
+template<typename T>
+class coordinate_base;
+
+//! This function is coodinate_base's only friend.  It performs an explicit 
+//! conversion from a coordinate_base<T> to a size_t.  Ideally, this will be 
+//! used very rarely outside of the coordinate_converter class.
+template<typename T>
+size_t coordinate_index(coordinate_base<T>);
+
+template<typename T>
+class coordinate_base {
+    typedef coordinate_base<T> cb;
+    friend size_t coordinate_index<T>(cb);
+    size_t v;
+    operator size_t() { return v; }
+  public:
+  
+    constexpr coordinate_base() {}
+    coordinate_base(size_t v) : v(v) {}
+    coordinate_base(const coordinate_base<T> &c) : v(c.v) {}
+    cb operator++(int) { cb t = *this; v++; return t; }
+    cb operator--(int) { cb t = *this; v--; return t; }
+    cb &operator++() { v++; return *this; }
+    cb &operator--() { v--; return *this; }
+    cb operator+(cb a) const { return v+a; }
+    cb operator-(cb a) const { return v-a; }
+    cb operator/(cb a) const { return v/a; }
+    cb operator*(cb a) const { return v*a; }
+    cb operator+(unsigned int a) const { return v+a; }
+    cb operator-(unsigned int a) const { return v-a; }
+    cb operator/(unsigned int a) const { return v/a; }
+    cb operator*(unsigned int a) const { return v*a; }
+    bool operator<(cb a) const { return v<a.v; }
+    bool operator>(cb a) const { return v>a.v; }
+    bool operator<=(cb a) const { return v<=a.v; }
+    bool operator>=(cb a) const { return v>=a.v; }
+    bool operator==(cb a) const { return v==a.v; }
+};
+
+//! 
+//! a class to protect y-coordinates as a unit type
+typedef coordinate_base<coord_y> size_y;
+
+//! a class to protect x-coordinates as a unit type
+typedef coordinate_base<coord_x> size_x;
+
+//! a class to protect w-coordinates as a unit type -- given a qubit, the w 
+//! coordinate corresponds to an offset perpendicular to the qubit
+typedef coordinate_base<coord_w> size_w;
+
+//! a class to protect z-coordinates as a unit type -- given a qubit, the z
+//! coordinate corresponds to an offset parallel to the qubit
+typedef coordinate_base<coord_z> size_z;
+
+template<typename T>
+size_t coordinate_index(coordinate_base<T> c) {
+    return c;
+}
+
+//! This purely for convenience, to make implementations of coordinate_converter
+//! methods cleaner.
+size_t coordinate_index(size_t c) {
+    return c;
+}
+
+
+//! This function translates between relative coordinates (w, z) and matrix
+//! coordinates (y, x) in the context of a vertical qubit address.  
+template<typename T>
+coordinate_base<typename vert_lut<T>::type> vert(const coordinate_base<T> c) {
+    return coordinate_index(c);
+}
+
+template<typename T>
+coordinate_base<typename horz_lut<T>::type> horz(const coordinate_base<T> c) {
+    return coordinate_index(c);
+}
+
+template<typename T>
+size_t binom(coordinate_base<T> c) {
+    return binom(coordinate_index(c));
+}
+
+#else
+
+// if neither CPPDEBUG nor COORDINATE_TYPES is set, we take the safeties off.
+// All coordinates are just size_t's.
+
+typedef size_t size_y;
+typedef size_t size_x;
+typedef size_t size_w;
+typedef size_t size_z;
+
+inline const size_t &coordinate_index(const size_t &c) { return c; }
+inline const size_t &vert(const size_t &c) { return c; }
+inline const size_t &horz(const size_t &c) { return c; }
+
+#endif
+
+//! An arena to perform arithmetic that does not respect the unit system defined
+//! in coordinate_base<T>.  This class is not a friend of coordinate_base<T> so
+//! so that we can do all of our bounds-checking here, with full type-safety.
+//! The generic pattern is that bounds-checking is done inside a public method,
+//! respecting the unit systems, and then we explicitly convert coordinates and
+//! dimensions to size_t, to be combined in a private method with the _impl
+//! suffix.
+class coordinate_converter {
+
+  private:
+    //! a convenience shortener to reduce line lengths
+    template<typename T>
+    static size_t coord(T c) { return coordinate_index(c); }
+    
+  private:
+    //! private implementation of cell_index
+    static size_t cell_index_impl(size_t y, size_t x, size_t u, size_t dim_y, size_t dim_x) {
+        return u + 2*(x + dim_x*y);
+    }
+  public:
+  
+    //! This computes compute the index of A[u][y][x] in an array A of dimension
+    //! [2][dim_y][dim_x]    
+    static size_t cell_index(size_y y, size_x x, bool u, size_y dim_y, size_x dim_x) {
+        minorminer_assert(y < dim_y);
+        minorminer_assert(x < dim_x);
+        return cell_index_impl(coord(y), coord(x), u, coord(dim_y), coord(dim_x));
+    }
+    
+    //! This computes compute the index of A[u][y][x] in an array A of dimension
+    //! [2][dim_y][dim_x]; where y and x are determined from w and z, depending
+    //! on the orientation u
+    static size_t cell_index(bool u, size_w w, size_z z, size_y dim_y, size_x dim_x) {
+        if (u) {
+            return cell_index(horz(w), horz(z), u, dim_y, dim_x);
+        } else {
+            return cell_index(vert(z), vert(w), u, dim_y, dim_x);
+        }
+    }
+
+  private:
+    //! private implementation of chimera_linear
+    static size_t chimera_linear_impl(size_t y, size_t x, size_t u, size_t k, size_t dim_y, size_t dim_x, size_t shore) {
+        return k + shore*(u + 2*(x + dim_x*y));
+    }
+  public:
+    //! given a chimera coordinate (y, x, u, k) with chimera dimensions 
+    //! (m, n, t) = (dim_y, dim_x, shore), compute the linear index.
+    template <typename shore_t>
+    static size_t chimera_linear(size_y y, size_x x, bool u, shore_t k, size_y dim_y, size_x dim_x, shore_t shore) {
+        minorminer_assert(y < dim_y);
+        minorminer_assert(x < dim_x);
+        minorminer_assert(k < shore);
+        return chimera_linear_impl(coord(y), coord(x), u, k, coord(dim_y), coord(dim_x), shore);
+    }
+
+    //! given a linear index q0 in a chimera graph with dimensions 
+    //! (m, n, t) = (dim_y, dim_x, shore), compute the coordinate (y, x, u, k)
+    template <typename shore_t>
+    static void linear_chimera(size_t q0, size_y &y, size_x &x, bool &u, shore_t &k, size_y dim_y, size_x dim_x, shore_t shore) {
+        size_t q = q0;
+        k = q%coord(shore);
+        q = q/coord(shore);
+        u = q%2;
+        q = q/2;
+        x = q%coord(dim_x);
+        q = q/coord(dim_x);
+        minorminer_assert(q < coord(dim_y));
+        y = q;
+        minorminer_assert(q0 == chimera_linear(y, x, u, k, dim_y, dim_x, shore));
+    }
+    
+  private:
+    //! private implementation of linemajor_linear
+    static size_t linemajor_linear_impl(size_t u, size_t w, size_t k, size_t z, size_t dim_w, size_t shore, size_t dim_z) {
+        return z + dim_z*(k + shore*(w + dim_w*u));
+    }
+  public:
+    //! Pegasus and Zephyr coordinates exist in relative coordinate systems. We
+    //! note that our implementation of Zephyr coordinates differs from the one
+    //! used other places -- we've merged the k and j indices into one. The name
+    //! 'linemajor' indicates that the (u, w, k) parameters specify a line of
+    //! colinear qubits.  This function is used to pack a Zephyr or Pegasus 
+    //! coordinate address (u, w, k, z) into a linear index.  The interpretation
+    //! of dim_w, shore, and dim_z is determined by the relevant topology, and
+    //! we expect callers to know what they're doing.
+    //! Specifically, for pegasus_graph(m), dim_w = 6*m, shore = 2, dim_z = m-1
+    //! and for zephyr_graph(m), dim_w = 2*m+1, shore=2*t, dim_z = m
+    template <typename shore_t>
+    static size_t linemajor_linear(bool u, size_w w, shore_t k, size_z z, size_w dim_w, shore_t shore, size_z dim_z) {
+        minorminer_assert(w < dim_w);
+        minorminer_assert(k < shore);
+        minorminer_assert(z < dim_z);
+        return linemajor_linear_impl(u, coord(w), k, coord(z), coord(dim_w), shore, coord(dim_z));
+    }
+    
+    //! Pegasus and Zephyr coordinates exist in relative coordinate systems. We
+    //! note that our implementation of Zephyr coordinates differs from the one
+    //! used other places -- we've merged the k and j indices into one. The name
+    //! 'linemajor' indicates that the (u, w, k) parameters specify a line of
+    //! colinear qubits.  This function is used to unpack a Zephyr or Pegasus 
+    //! linear index to a coordinate address (u, w, k, z).  The interpretation
+    //! of dim_w, shore, and dim_z is determined by the relevant topology, and
+    //! we expect callers to know what they're doing.
+    //! Specifically, for pegasus_graph(m), dim_w = 6*m, shore = 2, dim_z = m-1
+    //! and for zephyr_graph(m), dim_w = 2*m+1, shore=2*t, dim_z = m
+    template <typename shore_t>
+    static void linear_linemajor(size_t q0, bool &u, size_w &w, shore_t &k, size_z &z, size_w dim_w, shore_t shore, size_z dim_z) {
+        size_t q = q0;
+        z = q%coord(dim_z);
+        q = q/coord(dim_z);
+        k = q%shore;
+        q = q/shore;
+        w = q%coord(dim_w);
+        q = q/coord(dim_w);
+        minorminer_assert(q < 2);
+        u = q;
+        minorminer_assert(q0 == linemajor_linear(u, w, k, z, dim_w, shore, dim_z));
+    }
+
+  public:
+    template<typename T, typename ... Args>
+    static size_t product(T t) { return coord(t); }
+
+    //! explicitly convert all arguments into a size_t and return the product of 
+    //! them all.
+    template<typename T, typename ... Args>
+    static size_t product(T t, Args ...args) {
+        return coord(t) * product(args...);
+    }
+
+    template<typename T, typename ... Args>
+    static size_t sum(T t) { return coord(t); }
+
+    //! explicitly convert all arguments into a size_t and return the product of 
+    //! them all.
+    template<typename T, typename ... Args>
+    static size_t sum(T t, Args ...args) {
+        return coord(t) + sum(args...);
+    }
+
+    //! explicitly convert both arguments into a size_t and return the minimum
+    template<typename S, typename T>
+    static size_t min(S s, T t) { return std::min(coord(s), coord(t)); }
+
+    //! explicitly convert both arguments into a size_t and return the maximum
+    template<typename S, typename T>
+    static size_t max(S s, T t) { return std::max(coord(s), coord(t)); }
+
+  private:
+    //! private implementation of grid_index
+    static size_t grid_index_impl(size_t y, size_t x, size_t dim_y, size_t dim_x) {
+        return x + dim_x*y;
+    }
+  public:
+    //! Implements addressing into a 2-dimensional array T[dim_y][dim_x]
+    static size_t grid_index(size_y y, size_x x, size_y dim_y, size_x dim_x) {
+        minorminer_assert(y < dim_y);
+        minorminer_assert(x < dim_x);
+        return grid_index_impl(coord(y), coord(x), coord(dim_y), coord(dim_x));
+    }
+
+
+  private:
+    //! private implementation of bundle_cache_index
+    static size_t bundle_cache_index_impl(size_t u, size_t w, size_t z0, size_t z1, size_t stride_u, size_t stride_w) {
+        minorminer_assert(z0 <= z1);
+        minorminer_assert(binom(z1) + z0 < stride_w);
+        return u*stride_u + w*stride_w + binom(z1) + z0;
+    }
+
+  public:
+    //! This addressing scheme is wikkid complicated.  The majority of the logic
+    //! is contained in bundle_cache.hpp.  We store line masks (that is, sets of
+    //! k-indices) corresponding to the line segment (u, w, z0, z1).  That is,
+    //! if the bit i is set in line_mask[bundle_cache_index(u, w, z0, z1)], then
+    //! line of qubits [(u, w, i, z) for z in range(z0, z1+1)] and the necessary
+    //! external couplers are present in the linemajor representation of the
+    //! topology.  We assume that the caller knows what it's doing with stride_u
+    //! and stride_w.  We do belts-and-suspenders assertion testing in 
+    //! bundle_cache::compute_line_masks for added confidence that the caller
+    //! actually knows what it's doing.
+    static size_t bundle_cache_index(bool u, size_w w, size_z z0, size_z z1, size_t stride_u, size_t stride_w) {
+        return bundle_cache_index_impl(u, coord(w), coord(z0), coord(z1), stride_u, stride_w);
+    }
+
+};
+
+}
diff --git a/include/busclique/find_clique.hpp b/include/busclique/find_clique.hpp
index 9a33b840..7ba044d9 100644
--- a/include/busclique/find_clique.hpp
+++ b/include/busclique/find_clique.hpp
@@ -30,6 +30,30 @@ size_t get_maxlen(vector<T> &emb, size_t size) {
     return emb[size-1].size();
 }
 
+template<typename clique_cache_t>
+size_t get_sol(const clique_cache_t &rects, vector<vector<size_t>> &emb, size_t size) {
+    if(rects.extract_solution(emb)) {
+        if (emb.size() >= size) {
+            return get_maxlen(emb, size);
+        }
+    }
+    return numeric_limits<size_t>::max();
+}
+
+
+template<typename cells_t>
+bool find_clique_short(const cells_t &cells, const size_t size, vector<vector<size_t>> &emb, size_t &max_length) {
+    for(size_y y = 0; y < cells.topo.dim_y; y++)
+        for(size_x x = 0; x < cells.topo.dim_x; x++)
+            if (cells.score(y,x) >= size) {
+                emb.clear();
+                max_length = 1;
+                cells.inflate(y, x, emb);
+                return true;
+            }
+    return false;
+}
+
 template<typename topo_spec>
 bool find_clique_nice(const cell_cache<topo_spec> &,
                       size_t size,
@@ -45,43 +69,28 @@ bool find_clique_nice(const cell_cache<chimera_spec> &cells,
                       size_t &,
                       size_t &,
                       size_t &max_length) {
-    bundle_cache<chimera_spec> bundles(cells);
     size_t shore = cells.topo.shore;
-    if(size <= shore)
-        for(size_t y = 0; y < cells.topo.dim[0]; y++)
-            for(size_t x = 0; x < cells.topo.dim[1]; x++)
-                if (bundles.score(y,x,y,y,x,x) >= size) {
-                    bundles.inflate(y,x,y,y,x,x,emb);
-                    return true;
-                }
+    if(size <= shore && find_clique_short(cells, size, emb, max_length))
+        return true;
+    else if (max_length == 1)
+        return false;
+    bundle_cache<chimera_spec> bundles(cells);
     size_t minw = (size + shore - 1)/shore;
-    size_t maxw = min(cells.topo.dim[0], cells.topo.dim[1]);
-    if (max_length > 0) maxw = min(max_length - 1, maxw);
+    size_t maxw = coordinate_converter::min(cells.topo.dim_y, cells.topo.dim_x);
+    maxw = min(max_length - 1, maxw);
     for(size_t width = minw; width <= maxw; width++) {
         clique_cache<chimera_spec> rects(cells, bundles, width);
-        if(rects.extract_solution(emb)) {
-            if(emb.size() < size)
-                emb.clear();
-            else {
-                max_length = width + 1;
-                return true;
-            }
+        vector<vector<size_t>> temp_emb;
+        size_t l = get_sol(rects, temp_emb, size);
+        if (l < max_length) {
+            emb = temp_emb;
+            max_length = l;
+            return true;
         }
     }
     return false;
 }
 
-template<typename clique_cache_t>
-size_t check_sol(const clique_cache_t &rects, vector<vector<size_t>> &emb, size_t size) {
-    if(rects.extract_solution(emb)) {
-        if(emb.size() < size)
-            emb.clear();
-        else
-            return get_maxlen(emb, size);
-    }
-    return 0;
-}
-
 template<>
 bool find_clique_nice(const cell_cache<zephyr_spec> &cells,
                       size_t size,
@@ -89,23 +98,22 @@ bool find_clique_nice(const cell_cache<zephyr_spec> &cells,
                       size_t &,
                       size_t &,
                       size_t &max_length) {
-    bundle_cache<zephyr_spec> bundles(cells);
     size_t shore = cells.topo.shore;
-    if(size <= shore)
-        for(size_t y = 0; y < cells.topo.dim[0]; y++)
-            for(size_t x = 0; x < cells.topo.dim[1]; x++)
-                if (bundles.score(y,x,y,y,x,x) >= size) {
-                    bundles.inflate(y,x,y,y,x,x,emb);
-                    return true;
-                }
+    if(size <= shore && find_clique_short(cells, size, emb, max_length))
+        return true;
+    else if (max_length == 1)
+        return false;
+    bundle_cache<zephyr_spec> bundles(cells);
     size_t minw = (size + shore - 1)/shore;
-    size_t maxw = min(cells.topo.dim[0], cells.topo.dim[1]);
-    if (max_length > 0) maxw = min(max_length - 1, maxw);
+    size_t maxw = coordinate_converter::min(cells.topo.dim_y, cells.topo.dim_x);
+    if (max_length < numeric_limits<size_t>::max()) maxw = min(2*max_length+1, maxw);
     for(size_t width = minw; width <= maxw; width++) {
         clique_cache<zephyr_spec> rects(cells, bundles, width);
-        size_t l = check_sol(rects, emb, size);
-        if (l) {
+        vector<vector<size_t>> temp_emb;
+        size_t l = get_sol(rects, temp_emb, size);
+        if (l < max_length) {
             max_length = l;
+            emb = temp_emb;
             return true;
         }
     }
@@ -121,44 +129,44 @@ bool find_clique_nice(const cell_cache<pegasus_spec> &cells,
                       size_t &max_length) {
     bundle_cache<pegasus_spec> bundles(cells);
     size_t minw = (size + 1)/2;
-    size_t maxw = cells.topo.dim[0];
-    if(max_length == 0) {
-        //naive first-pass: search for the first embedding with any max chainlength
-        for(; minw <= maxw; minw++) {
+    size_t maxw = coordinate_converter::min(cells.topo.dim_y, cells.topo.dim_x);
+
+    for(size_t w = minw; w <= maxw; w++) {
+        size_t w_lengthmin, w_lengthmax;
+        clique_yield_cache<pegasus_spec>::get_length_range(cells.topo, w, w_lengthmin, w_lengthmax);
+        if (w_lengthmin < max_length) {
+            //check for any embedding at this length
             vector<vector<size_t>> tmp;
-            clique_cache<pegasus_spec> rects(cells, bundles, minw);
-            if(rects.extract_solution(tmp))
-                //if we find an embedding, check that it's big enough
-                if(tmp.size() >= size) {
+            clique_cache<pegasus_spec> rects(cells, bundles, w);
+            if(rects.extract_solution(tmp) && tmp.size() >= size) {
+                size_t length = get_maxlen(tmp, size);
+                if (length < max_length)
                     emb = tmp;
-                    max_length = get_maxlen(tmp, size);
-                    maxw = min(maxw, minw + 6);
-                    break;
-                }
+                max_length = min(length, max_length);
+                maxw = min(maxw, w+6);
+            } else {
+                //no embeddings here; skip to the next minw;
+                continue;
+            }
+        } else {
+            break;
         }
-        if(minw > maxw) return false;
-    } else {
-        maxw = min(cells.topo.dim[0], (max_length)*6);
-    }
-    //we've already found an embedding; now try to find one with shorter chains
-    for(size_t w = minw; w <= maxw; w++) {
-        auto check_length = [&bundles, max_length](size_t yc, size_t xc,
-                                                   size_t y0, size_t y1,
-                                                   size_t x0, size_t x1){
-            return bundles.length(yc,xc,y0,y1,x0,x1) < max_length; 
-        };
-        clique_cache<pegasus_spec> rects(cells, bundles, w, check_length);
-        vector<vector<size_t>> tmp;
-        if(rects.extract_solution(tmp)) {
-            //if we find an embedding, check that it's big enough
-            if(tmp.size() >= size) {
-                //if it's big enough, find the max (necessary) chainlength
-                size_t tlen = get_maxlen(tmp, size);
-                if(tlen < max_length) {
-                    emb = tmp;
-                    max_length = tlen;
-                    maxw = min(maxw, w+6);
-                    w--; //re-do this width until we stop improving
+        w_lengthmax = max(max_length, w_lengthmax);
+        for (size_t length = w_lengthmin; length < w_lengthmax; length++) {
+            auto check_length = [&bundles, length](size_y yc, size_x xc,
+                                                   size_y y0, size_y y1,
+                                                   size_x x0, size_x x1){
+                return bundles.length(yc,xc,y0,y1,x0,x1) <= length;
+            };
+            clique_cache<pegasus_spec> rects(cells, bundles, w, check_length);
+            vector<vector<size_t>> tmp;
+            if(rects.extract_solution(tmp)) {
+                if(tmp.size() >= size) {
+                    //if it's big enough, find the max (necessary) chainlength
+                    size_t tlen = get_maxlen(tmp, size);
+                    if(tlen < max_length)
+                        emb = tmp;
+                    max_length = min(length, max_length);
                 }
             }
         } //if no embeddings were found, keep searching
@@ -191,30 +199,19 @@ bool find_clique(topo_cache<topo_spec> &topology,
                  size_t size,
                  vector<vector<size_t>> &emb) {
     topology.reset();
-    size_t max_length = 0;
+    size_t max_length = numeric_limits<size_t>::max();
     size_t min_width = 0;
     size_t max_width = 0;
     vector<vector<size_t>> _emb;
-    if (find_clique_nice(topology.cells, size, _emb, max_length, min_width, max_width))
+    if (find_clique_nice(topology.cells, size, _emb, min_width, max_width, max_length))
         emb = _emb;
     while(topology.next()) {
-        if (find_clique_nice(topology.cells, size, _emb, max_length, min_width, max_width))
+        if (find_clique_nice(topology.cells, size, _emb, min_width, max_width, max_length))
             emb = _emb;
     }
     return emb.size() >= size;
 }
 
-template<typename topo_spec>
-bool find_clique_nice(const topo_spec &topo,
-                      const vector<size_t> &nodes,
-                      const vector<pair<size_t, size_t>> &edges,
-                      size_t size,
-                      vector<vector<size_t>> &emb) {
-    const cell_cache<topo_spec> cells(topo, nodes, edges);
-    size_t _0, _1, _2;
-    return find_clique_nice(cells, size, emb, _0, _1, _2);
-}
-
 template<typename topo_spec>
 void short_clique(const topo_spec &,
                   const vector<size_t> &nodes,
diff --git a/include/busclique/topo_cache.hpp b/include/busclique/topo_cache.hpp
index ef3be0d2..74487e6d 100644
--- a/include/busclique/topo_cache.hpp
+++ b/include/busclique/topo_cache.hpp
@@ -78,42 +78,53 @@ class topo_cache {
         return topo.serialize(serialize_tag{}, output, nodemask, edgemask, badmask);
     }
 
-    vector<size_t> fragment_nodes() const {
+    vector<size_t> fragment_nodes(const uint8_t *nmask = nullptr) const {
+        if (nmask == nullptr) nmask = nodemask;
         vector<size_t> nodes;
         size_t q = 0;
-        for (size_t y = 0; y < topo.dim[0]; y++) {
-            for (size_t x = 0; x < topo.dim[1]; x++) {
-                for (size_t k = 0; k < topo.shore; k++, q++) {
-                    if (nodemask[topo.cell_addr(0, x, y)]&mask_bit[k])
+        for (size_y y = 0; y < topo.dim_y; y++) {
+            for (size_x x = 0; x < topo.dim_x; x++) {
+                for (uint8_t k = 0; k < topo.shore; k++) {
+                    if (nmask[topo.cell_index(0, vert(x), vert(y))]&mask_bit[k])
                         nodes.push_back(q);
+                    minorminer_assert(q == topo.chimera_linear(y, x, 0, k));
+                    q++;
                 }
-                for (size_t k = 0; k < topo.shore; k++, q++) {
-                    if (nodemask[topo.cell_addr(1, y, x)]&mask_bit[k])
+                for (uint8_t k = 0; k < topo.shore; k++) {
+                    if (nmask[topo.cell_index(1, horz(y), horz(x))]&mask_bit[k])
                         nodes.push_back(q);
+                    minorminer_assert(q == topo.chimera_linear(y, x, 1, k));
+                    q++;
                 }
             }
         }
         return nodes;
     }
     
-    vector<pair<size_t, size_t>> fragment_edges() const {
+    vector<pair<size_t, size_t>> fragment_edges(const uint8_t *nmask = nullptr, const uint8_t *emask = nullptr) const {
+        if (nmask == nullptr) nmask = nodemask;
+        if (emask == nullptr) emask = edgemask;
         vector<pair<size_t, size_t>> edges;
         size_t q = 0;
-        for (size_t y = 0; y < topo.dim[0]; y++) {
-            for (size_t x = 0; x < topo.dim[1]; x++) {
-                for (size_t k = 0; k < topo.shore; k++, q++) {
-                    if (edgemask[topo.cell_addr(0, x, y)]&mask_bit[k])
-                        edges.emplace_back(q, topo.chimera_linear(y-1, x, 0, k));
-                    if (nodemask[topo.cell_addr(0, x, y)]&mask_bit[k]) {
-                        for (size_t k1 = 0; k1 < topo.shore; k1++) {
-                            if (nodemask[topo.cell_addr(1, y, x)]&mask_bit[k1]&~badmask[q])
+        for (size_y y = 0; y < topo.dim_y; y++) {
+            for (size_x x = 0; x < topo.dim_x; x++) {
+                for (uint8_t k = 0; k < topo.shore; k++) {
+                    if (emask[topo.cell_index(0, vert(x), vert(y))]&mask_bit[k])
+                        edges.emplace_back(q, topo.chimera_linear(y-1u, x, 0, k));
+                    if (nmask[topo.cell_index(0, vert(x), vert(y))]&mask_bit[k]) {
+                        for (uint8_t k1 = 0; k1 < topo.shore; k1++) {
+                            if (nmask[topo.cell_index(1, horz(y), horz(x))]&mask_bit[k1]&~badmask[q])
                                 edges.emplace_back(q, topo.chimera_linear(y, x, 1, k1));
                         }
                     }
+                    minorminer_assert(q == topo.chimera_linear(y, x, 0, k));
+                    q++;
                 }
-                for (size_t k = 0; k < topo.shore; k++, q++) {
-                    if (edgemask[topo.cell_addr(1, y, x)]&mask_bit[k])
-                        edges.emplace_back(q, topo.chimera_linear(y, x-1, 1, k));
+                for (uint8_t k = 0; k < topo.shore; k++) {
+                    if (emask[topo.cell_index(1, horz(y), horz(x))]&mask_bit[k])
+                        edges.emplace_back(q, topo.chimera_linear(y, x-1u, 1, k));
+                    minorminer_assert(q == topo.chimera_linear(y, x, 1, k));
+                    q++;
                 }
             }
         }
@@ -147,17 +158,18 @@ class topo_cache {
 
     void compute_bad_edges() {
         size_t q = 0;
-        for(size_t y = 0, rowbase = 0; y < topo.dim[0]; y++, rowbase += topo.shore)
-            for(size_t x = 0; x < topo.dim[1]; x++) {
-                size_t qbase = q;
+        for(size_y y = 0; y < topo.dim_y; y++)
+            for(size_x x = 0; x < topo.dim_x; x++) {
                 //only iterate over the vertical qubits to avoid duplication
-                for(size_t k = 0, row = rowbase; k < topo.shore; k++, q++, row++) {
+                for(uint8_t k = 0; k < topo.shore; k++) {
                     uint8_t mask = badmask[q];
                     while(mask) {
                         uint8_t badk = first_bit[mask];
                         mask ^= mask_bit[badk];
-                        bad_edges.emplace_back(q, qbase + topo.shore + badk);
+                        bad_edges.emplace_back(q, topo.chimera_linear(y, x, 1, badk));
                     }
+                    minorminer_assert(q == topo.chimera_linear(y, x, 0, k));
+                    q++;
                 }
                 //skip over those horizontal qubits
                 q += topo.shore;
@@ -191,10 +203,7 @@ class topo_cache {
             //maaaaybe we want to hand control of this parameter to the user?
             if(mask_num < 64) mask_num++;
             else return false;
-            //this is a somewhat ad-hoc, unoptimized implementation.  we should
-            //take a proper seed from the user, etc.
-            std::random_device r;
-            std::ranlux48 rng(r());
+            //this is a somewhat ad-hoc, unoptimized implementation.
             std::shuffle(bad_edges.begin(), bad_edges.end(), rng);
             std::map<size_t, std::set<size_t>> adj;
             for(auto &e : bad_edges) {
@@ -233,12 +242,20 @@ class topo_cache {
         memcpy(child_edgemask, edgemask, topo.num_cells());
 
         for(auto &q: bad_nodes) {
-            size_t u, w, z, k;
-            topo.linemajor(q, u, w, z, k);
-            child_nodemask[topo.cell_addr(u, w, z)] &= ~mask_bit[k];
-            child_edgemask[topo.cell_addr(u, w, z)] &= ~mask_bit[k];
-            if(z + 1 < topo.dim[u])
-                child_edgemask[topo.cell_addr(u, w, z + 1)] &= ~mask_bit[k];
+            size_y y;
+            size_x x;
+            bool u;
+            uint8_t k;
+            topo.linear_chimera(q, y, x, u, k);
+            child_nodemask[topo.cell_index(y, x, u)] &= ~mask_bit[k];
+            child_edgemask[topo.cell_index(y, x, u)] &= ~mask_bit[k];
+            if(u) {
+                if (x+1u < topo.dim_x) 
+                    child_edgemask[topo.cell_index(y, x+1u, u)] &= ~mask_bit[k];
+            } else {
+                if (y+1u < topo.dim_y) 
+                    child_edgemask[topo.cell_index(y+1u, x, u)] &= ~mask_bit[k];
+            }
         }
         return true;
     }
diff --git a/include/busclique/util.hpp b/include/busclique/util.hpp
index a32ef06f..e3779dfb 100644
--- a/include/busclique/util.hpp
+++ b/include/busclique/util.hpp
@@ -25,6 +25,7 @@
 #include<map>
 #include "../debug.hpp"
 #include "../fastrng.hpp"
+#include "coordinate_types.hpp"
 
 namespace busclique {
 using std::numeric_limits;
@@ -150,57 +151,54 @@ class ignore_badmask {};
 class populate_badmask {};
 class topo_spec_base {
   public:
-    const size_t dim[2];
+    const size_y dim_y;
+    const size_x dim_x;
     const size_t shore;
     const uint64_t seed;
     
-    topo_spec_base(size_t d0, size_t d1, size_t s, uint64_t e) :
-        dim{d0, d1}, shore(s), seed(e) {}
+    topo_spec_base(size_t dim_y, size_t dim_x, size_t s, uint64_t e) :
+        dim_y(dim_y), dim_x(dim_x), shore(s), seed(e) {}
 
-    topo_spec_base(size_t d0, size_t d1, size_t s, uint32_t e) :
-        topo_spec_base(d0, d1, s, fastrng::amplify_seed(e)) {}
+    topo_spec_base(size_t dim_y, size_t dim_x, size_t s, uint32_t e) :
+        topo_spec_base(dim_y, dim_x, s, fastrng::amplify_seed(e)) {}
 
     template<typename serialize_tag, typename ...Args>
     size_t serialize(serialize_tag, uint8_t *output, const Args &...args) const {
-        return _serialize(serialize_tag{}, output, dim, shore, seed, args...);
+        return _serialize(serialize_tag{}, output, dim_y, dim_x, shore, seed, args...);
     }
 
-    inline size_t cell_addr(size_t u, size_t y, size_t x) const {
-        return x + dim[1]*(y + dim[0]*u);
+    //! cell_index is used for indexing into the nodemask and edgemask fields
+    //! of cell_cache and topo_cache objects.  These are used to store bitmasks
+    //! indicating the presence of nodes in the corresponding cell, or external
+    //! edges between the corresponding cell and its greater parallel neighbor
+    //! (that is, between the cells [y,x,0] and [y+1,x,0] or between the cells
+    //! [y,x,1] and [y,x+1,1]).
+    inline size_t cell_index(size_y y, size_x x, bool u) const {
+        return coordinate_converter::cell_index(y, x, u, dim_y, dim_x);
     }
 
-    inline size_t num_cells() const {
-        return dim[0]*dim[1]*2;
+    //! cell_index is used for indexing into the nodemask and edgemask fields
+    //! of cell_cache and topo_cache objects.  These are used to store bitmasks
+    //! indicating the presence of nodes in the corresponding cell, or external
+    //! edges between the corresponding cell and its greater parallel neighbor
+    //! (that is, between the cells [u,w,z] and [u,w,z+1])
+    inline size_t cell_index(bool u, size_w w, size_z z) const {
+        return coordinate_converter::cell_index(u, w, z, dim_y, dim_x);
     }
 
-    inline void linemajor(size_t q, size_t &u, size_t &w, size_t &z, size_t &k) const {
-        k = q % shore;
-        linemajor(q, u, w, z);
-    }
-    inline void linemajor(size_t q, size_t &u, size_t &w, size_t &z) const {
-        size_t p[2];
-                   q /= shore;
-        u = q % 2; q /= 2;
-        p[0] = q % dim[1];
-        p[1] = q / dim[1];
-        w = p[u];
-        z = p[1-u];
+    inline size_t num_cells() const {
+        return coordinate_converter::product(dim_y, dim_x, 2);
     }
 
-    inline size_t chimera_linear(size_t y, size_t x, size_t u, size_t k) const {
-        minorminer_assert(u < 2);
-        minorminer_assert(k < shore);
-        minorminer_assert(y < dim[0]);
-        minorminer_assert(x < dim[1]);
-        return k + shore*(u + 2*(x + dim[1]*y));
+    template<typename shore_t>
+    inline size_t chimera_linear(size_y y, size_x x, bool u, shore_t k) const {
+        return coordinate_converter::chimera_linear(y, x, u, k, dim_y, dim_x, shore_t(shore));
     }
 
-    inline void chimera_coordinates(size_t q,
-                                    size_t &y, size_t &x, size_t &u, size_t &k) const {
-        k = q % shore;  q /= shore;
-        u = q % 2;      q /= 2;
-        x = q % dim[1]; y  = q/dim[1];
-        minorminer_assert(chimera_linear(y, x, u, k) == q);
+    template<typename shore_t>
+    inline void linear_chimera(size_t q, 
+                               size_y &y, size_x &x, bool &u, shore_t &k) const {
+        coordinate_converter::linear_chimera(q, y, x, u, k, dim_y, dim_x, shore_t(shore));
     }
 };
 
@@ -231,24 +229,26 @@ class pegasus_spec_base : public topo_spec_base {
 
             //ensure some normalization: p < q
             if(q < p) std::swap(p, q);
-            size_t qu, qw, qk, qz;
-            size_t pu, pw, pk, pz;
+            bool    pu, qu;
+            size_w  pw, qw;
+            uint8_t pk, qk;
+            size_z  pz, qz;
             pegasus_coordinates(q, qu, qw, qk, qz);
             pegasus_coordinates(p, pu, pw, pk, pz);
             if(pu == qu) {
-                if(pw == qw && pk == qk && qz == pz + 1) {
+                if(pw == qw && pk == qk && qz == pz + 1u) {
                     //p < q; we place the edgemask on the larger qubit z
                     //and don't futz with the "pz == qz + 1" case
-                    size_t z = (qz*12 + 2*offsets[qu][qk/2])/2;
-                    size_t w = (qw*12 + qk)/2;
-                    edgemask[super::cell_addr(pu, w, z)] |= mask_bit[qk&1];
+                    size_z z = qz*6u + size_z(offsets[qu][qk/2]);
+                    size_w w = qw*6u + size_w(qk/2);
+                    edgemask[super::cell_index(pu, w, z)] |= mask_bit[qk&1];
                 } else if (pw == qw && pk == (qk^1) && qz == pz) {
                 } else { std::cout << "urp" << std::endl; throw 10; }
             } else {
                 if(std::is_same<badmask_behavior, populate_badmask>::value) {
                     //p < q, so pu = 0 and qu = 1
-                    size_t y = (qw*12 + qk) / 2;
-                    size_t x = (pw*12 + pk) / 2;
+                    size_y y = horz(qw)*6u + size_y(qk/2);
+                    size_x x = vert(pw)*6u + size_x(pk/2);
                     badmask[super::chimera_linear(y, x, 0, pk&1)] &= ~mask_bit[qk&1];
                     badmask[super::chimera_linear(y, x, 1, qk&1)] &= ~mask_bit[pk&1];
                 }
@@ -256,11 +256,13 @@ class pegasus_spec_base : public topo_spec_base {
         }
     }
 
-    inline void first_fragment(size_t q, size_t &u, size_t &w, size_t &k, size_t &z) const {
-        size_t qw, qk, qz;
+    inline void first_fragment(size_t q, bool &u, size_w &w, uint8_t &k, size_z &z) const {
+        size_w qw;
+        uint8_t qk;
+        size_z qz;
         pegasus_coordinates(q, u, qw, qk, qz);
-        z = (qz*12 + 2*offsets[u][qk/2])/2;
-        w = (qw*12 + qk)/2;
+        z = qz*6u + size_z(offsets[u][qk/2]);
+        w = qw*6u + size_w(qk/2);
         k = qk&1;
     }
 
@@ -268,20 +270,23 @@ class pegasus_spec_base : public topo_spec_base {
     inline void process_nodes(uint8_t *nodemask, uint8_t *edgemask, uint8_t *badmask,
                              const vector<size_t> &nodes, badmask_behavior) const {
         for(auto &q: nodes) {
-            size_t u, w, k, z;
+            bool    u;
+            size_w  w;
+            uint8_t k;
+            size_z  z;
             first_fragment(q, u, w, k, z);
-            nodemask[super::cell_addr(u, w, z)] |= mask_bit[k];
+            nodemask[super::cell_index(u, w, z)] |= mask_bit[k];
             if(std::is_same<badmask_behavior, populate_badmask>::value) {
-                if(u) { badmask[super::chimera_linear(w, z, 1, k)] = ~0; }
-                else  { badmask[super::chimera_linear(z, w, 0, k)] = ~0; }
+                if(u) { badmask[super::chimera_linear(horz(w), horz(z), 1, k)] = ~0; }
+                else  { badmask[super::chimera_linear(vert(z), vert(w), 0, k)] = ~0; }
             }
-            for(size_t i = 1; z++, i < 6; i++) {
-                size_t curr = super::cell_addr(u, w, z);
-                nodemask[curr] |= mask_bit[k];
-                edgemask[curr] |= mask_bit[k];
+            for(size_t i = 1; i < 6; i++) {
+                z++;
+                nodemask[super::cell_index(u, w, z)] |= mask_bit[k];
+                edgemask[super::cell_index(u, w, z)] |= mask_bit[k];
                 if(std::is_same<badmask_behavior, populate_badmask>::value) {
-                    if(u) { badmask[super::chimera_linear(w, z, 1, k)] = ~0; }
-                    else  { badmask[super::chimera_linear(z, w, 0, k)] = ~0; }
+                    if(u) { badmask[super::chimera_linear(horz(w), horz(z), 1, k)] = ~0; }
+                    else  { badmask[super::chimera_linear(vert(z), vert(w), 0, k)] = ~0; }
                 }
             }
         }
@@ -291,9 +296,12 @@ class pegasus_spec_base : public topo_spec_base {
     bool has_qubits(const cell_cache &cells, const vector<vector<size_t>> &emb) const {
         for(auto &chain: emb)
             for(auto &q: chain) {
-                size_t u, w, k, z0;
+                bool u;
+                size_w w;
+                uint8_t k;
+                size_z z0;
                 first_fragment(q, u, w, k, z0);
-                for(size_t z = z0; z < z0+6; z++)
+                for(size_z z = z0; z < z0+6u; z++)
                     if((cells.qmask(u, w, z) & mask_bit[k]) == 0)
                         return false;
             }
@@ -301,57 +309,59 @@ class pegasus_spec_base : public topo_spec_base {
     }
 
   public:
-    inline size_t pegasus_linear(size_t u, size_t w, size_t k, size_t z) const {
-        return z + (pdim-1)*(k + 12*(w + pdim*u));
+    inline size_t pegasus_linear(bool u, size_w w, uint8_t k, size_z z) const {
+        return coordinate_converter::linemajor_linear(u, w, k, z, pdim, uint8_t(12), pdim-1);
+
     }
 
     vector<size_t> fragment_nodes(size_t q) const {
-        size_t u, w, k, z0;
+        bool u;
+        size_w w;
+        uint8_t k;
+        size_z z0;
         first_fragment(q, u, w, k, z0);
         vector<size_t> fragments;
-        for(size_t z = z0+6; z-->z0;) {
+        for(size_z z = z0+6u; z-->z0;) {
             fragments.push_back(
-                u?super::chimera_linear(w, z, u, k):
-                  super::chimera_linear(z, w, u, k)
+                u?super::chimera_linear(horz(w), horz(z), u, k):
+                  super::chimera_linear(vert(z), vert(w), u, k)
             );
         }        
         return fragments;
     }
 
     inline void pegasus_coordinates(size_t q,
-                                    size_t &u, size_t &w, size_t &k, size_t &z) const {
-        z = q % (pdim-1); q /= pdim-1;
-        k = q % 12;       q /= 12;
-        w = q % pdim;     u = q/pdim;
+                                    bool &u, size_w &w, uint8_t &k, size_z &z) const {
+        coordinate_converter::linear_linemajor(q, u, w, k, z, pdim, uint8_t(12), pdim-1);
     }
 
-    void construct_line(size_t u, size_t w, size_t z0, size_t z1, size_t k,
+    void construct_line(bool u, size_w w, size_z z0, size_z z1, uint8_t k,
                         vector<size_t> &chain) const {
-        size_t qk = (2*w + k)%12;
-        size_t qw = (2*w + k)/12;
-        size_t qz0 = (z0*2 - 2*offsets[u][qk/2])/12;
-        size_t qz1 = (z1*2 - 2*offsets[u][qk/2])/12;
-        for(size_t qz = qz0; qz <= qz1; qz++)
+        uint8_t qk = (coordinate_index(w)*2 + k)%12u;
+        size_w qw = (w*2u + size_w(k))/12u;
+        size_z qz0 = (z0 - size_z(offsets[u][qk/2]))/6u;
+        size_z qz1 = (z1 - size_z(offsets[u][qk/2]))/6u;
+        for(size_z qz = qz0; qz <= qz1; qz++)
             chain.push_back(pegasus_linear(u, qw, qk, qz));
     }
 
-    inline size_t line_length(size_t u, size_t w, size_t z0, size_t z1) const {
-        size_t offset = offsets[u][w%6];
-        size_t qz0 = (z0 + 6 - offset)/6;
-        size_t qz1 = (z1 + 12 - offset)/6;
-        return qz1 - qz0;
+    inline size_t line_length(bool u, size_w w, size_z z0, size_z z1) const {
+        size_z offset = offsets[u][coordinate_index(w)%6];
+        size_z qz0 = (z0 + size_z(6) - offset)/6u;
+        size_z qz1 = (z1 + size_z(12) - offset)/6u;
+        return coordinate_index(qz1 - qz0);
     }
 
-    inline size_t biclique_length(size_t y0, size_t y1, size_t x0, size_t x1) const {
+    inline size_t biclique_length(size_y y0, size_y y1, size_x x0, size_x x1) const {
         minorminer_assert(y1 >= y0);
         minorminer_assert(x1 >= x0);
         size_t length = 0;
-        size_t ym = std::min(y1, y0+5);
-        size_t xm = std::min(x1, x0+5);
-        for(size_t x = x0; x <= xm; x++)
-            length = max(line_length(0, x, y0, y1), length);
-        for(size_t y = y0; y <= ym; y++)
-            length = max(line_length(1, y, x0, x1), length);
+        size_y ym = std::min(y1, y0+size_y(5));
+        size_x xm = std::min(x1, x0+size_x(5));
+        for(size_x x = x0; x <= xm; x++)
+            length = max(line_length(0, vert(x), vert(y0), vert(y1)), length);
+        for(size_y y = y0; y <= ym; y++)
+            length = max(line_length(1, horz(y), horz(x0), horz(x1)), length);
         return length;
     }
 };
@@ -371,17 +381,18 @@ class chimera_spec_base : public topo_spec_base {
                               badmask_behavior) const {
         for(auto &e: edges) {
             size_t p = e.first, q = e.second;
-            size_t pu, pw, pz, pk;
-            size_t qu, qw, qz, qk;
-            super::linemajor(p, pu, pw, pz, pk);
-            super::linemajor(q, qu, qw, qz, qk);
-            if(pu == qu && pw == qw && pk == qk) {
-                if(pz == qz+1)
-                    edgemask[super::cell_addr(pu, pw, pz)] |= mask_bit[pk];
-                if(qz == pz+1)
-                    edgemask[super::cell_addr(qu, qw, qz)] |= mask_bit[qk];
+            if(q < p) std::swap(p, q);
+            size_y  py, qy;
+            size_x  px, qx;
+            bool    pu, qu;
+            uint8_t pk, qk;
+            super::linear_chimera(p, py, px, pu, pk);
+            super::linear_chimera(q, qy, qx, qu, qk);
+            if(pu == qu) {
+                if (pk == qk && py + size_y(1-pu) == qy && px + size_x(pu) == qx)
+                    edgemask[super::cell_index(qy, qx, qu)] |= mask_bit[qk];
             } else if (std::is_same<badmask_behavior, populate_badmask>::value &&
-                       pu != qu && pw == qz && pz == qw) {
+                       pu != qu && py == qy && px == qx) {
                 badmask[p] &= ~mask_bit[qk];
                 badmask[q] &= ~mask_bit[pk];
             }
@@ -393,30 +404,36 @@ class chimera_spec_base : public topo_spec_base {
         for(auto &q: nodes) {
             //add the node q by updating nodemask on the k-th bit of q's line,
             //and initializing badmask[k] to all-ones
-            size_t u, w, z, k;
-            super::linemajor(q, u, w, z, k);
+            size_y  y;
+            size_x  x;
+            bool    u;
+            uint8_t k;
+            super::linear_chimera(q, y, x, u, k);
             if(std::is_same<badmask_behavior, populate_badmask>::value)
                 badmask[q] = ~0;
-            nodemask[super::cell_addr(u, w, z)] |= mask_bit[k];
+            nodemask[super::cell_index(y, x, u)] |= mask_bit[k];
         }
     }
 
-  public:
-    void construct_line(size_t u, size_t w, size_t z0, size_t z1, size_t k,
+  public:    
+    void construct_line(bool u, size_w w, size_z z0, size_z z1, uint8_t k,
                         vector<size_t> &chain) const {
-        size_t p[2]; 
-        size_t &z = p[u];
-        p[1-u] = w;
-        for(z = z0; z <= z1; z++)
-            chain.push_back(super::chimera_linear(p[0], p[1], u, k));
+        if(u) {
+            for(size_x x = horz(z0); x <= horz(z1); x++)
+                chain.push_back(super::chimera_linear(horz(w), x, 1, k));
+        } else {
+            for(size_y y = vert(z0); y <= vert(z1); y++)
+                chain.push_back(super::chimera_linear(y, vert(w), 0, k));
+        }
     }
 
-    inline size_t line_length(size_t, size_t, size_t z0, size_t z1) const {
-        return z1 - z0 + 1;
+    template<typename U, typename T>
+    inline size_t line_length(bool, U, T t0, T t1) const {
+        return coordinate_index(t1 - t0) + 1;
     }
 
-    inline size_t biclique_length(size_t y0, size_t y1, size_t x0, size_t x1) const {
-        return max(y1-y0, x1-x0) + 1;
+    inline size_t biclique_length(size_y y0, size_y y1, size_x x0, size_x x1) const {
+        return max(coordinate_index(y1-y0), coordinate_index(x1-x0)) + 1;
     }
 
     vector<size_t> fragment_nodes(size_t q) const {
@@ -447,11 +464,9 @@ class zephyr_spec_base : public topo_spec_base {
 
   private:
     inline void first_fragment(size_t q,
-                               size_t &u, size_t &w, size_t &k, size_t &z) const {
-        z = q % zdim;          q /= zdim;
-        k = q % super::shore;  q /= super::shore;
-        w = q % super::dim[0]; u = q/super::dim[0];
-        z+= z + (k&1);
+                               bool &u, size_w &w, uint8_t &k, size_z &z) const {
+        coordinate_converter::linear_linemajor(q, u, w, k, z, 2*zdim+1, uint8_t(super::shore), zdim);
+        z = z*2u + size_z(k&1);
     }
 
   protected:
@@ -464,23 +479,25 @@ class zephyr_spec_base : public topo_spec_base {
 
             //ensure some normalization: p < q
             if(q < p) std::swap(p, q);
-            size_t qu, qw, qk, qz;
-            size_t pu, pw, pk, pz;
+            bool    pu, qu;
+            size_w  pw, qw;
+            uint8_t pk, qk;
+            size_z  pz, qz;
             zephyr_coordinates(q, qu, qw, qk, qz);
             zephyr_coordinates(p, pu, pw, pk, pz);
             if(pu == qu) {
-                if(pw == qw && pk == qk && qz == pz + 1) {
+                if(pw == qw && pk == qk && qz == pz + size_z(1)) {
                     //p < q; we place the edgemask on the larger qubit z
                     //and don't futz with the "pz == qz + 1" case
-                    size_t fz = 2*qz + (qk&1);
-                    edgemask[super::cell_addr(qu, qw, fz)] |= mask_bit[qk];
-                } else if (pw == qw && pk == (qk^1) && (qz == pz || qz+1 == pz)) {
+                    size_z fz = qz*2u + size_z(qk&1);
+                    edgemask[super::cell_index(qu, qw, fz)] |= mask_bit[qk];
+                } else if (pw == qw && pk == (qk^1) && (qz == pz || qz+size_z(1) == pz)) {
                 } else { std::cout << "urp" << std::endl; throw 10; }
             } else {
                 if(std::is_same<badmask_behavior, populate_badmask>::value) {
                     //p < q, so pu = 0 and qu = 1
-                    size_t y = qw;
-                    size_t x = pw;
+                    size_y y = horz(qw);
+                    size_x x = vert(pw);
                     badmask[super::chimera_linear(y, x, 0, pk)] &= ~mask_bit[qk];
                     badmask[super::chimera_linear(y, x, 1, qk)] &= ~mask_bit[pk];
                 }
@@ -491,83 +508,87 @@ class zephyr_spec_base : public topo_spec_base {
     inline void process_nodes(uint8_t *nodemask, uint8_t *edgemask, uint8_t *badmask,
                              const vector<size_t> &nodes, badmask_behavior) const {
         for(auto &q: nodes) {
-            size_t u, w, k, z;
+            bool    u;
+            size_w  w;
+            uint8_t k;
+            size_z  z;
             first_fragment(q, u, w, k, z);
-            nodemask[super::cell_addr(u, w, z)] |= mask_bit[k];
+            nodemask[super::cell_index(u, w, z)] |= mask_bit[k];
             if(std::is_same<badmask_behavior, populate_badmask>::value) {
-                if(u) { badmask[super::chimera_linear(w, z, 1, k)] = ~0; }
-                else  { badmask[super::chimera_linear(z, w, 0, k)] = ~0; }
+                if(u) { badmask[super::chimera_linear(horz(w), horz(z), 1, k)] = ~0; }
+                else  { badmask[super::chimera_linear(vert(z), vert(w), 0, k)] = ~0; }
             }
             //advance to the second fragment
             z++;
-            size_t curr = super::cell_addr(u, w, z);
-            nodemask[curr] |= mask_bit[k];
-            edgemask[curr] |= mask_bit[k];
+            nodemask[super::cell_index(u, w, z)] |= mask_bit[k];
+            edgemask[super::cell_index(u, w, z)] |= mask_bit[k];
             if(std::is_same<badmask_behavior, populate_badmask>::value) {
-                if(u) { badmask[super::chimera_linear(w, z, 1, k)] = ~0; }
-                else  { badmask[super::chimera_linear(z, w, 0, k)] = ~0; }
+                if(u) { badmask[super::chimera_linear(horz(w), horz(z), 1, k)] = ~0; }
+                else  { badmask[super::chimera_linear(vert(z), vert(w), 0, k)] = ~0; }
             }
         }
     }
 
   public:
     inline void zephyr_coordinates(size_t q,
-                                   size_t &u, size_t &w, size_t &k, size_t &z) const {
-        z = q % zdim;          q /= zdim;
-        k = q % super::shore;  q /= super::shore;
-        w = q % super::dim[0]; u = q/super::dim[0];
+                                   bool &u, size_w &w, uint8_t &k, size_z &z) const {
+        coordinate_converter::linear_linemajor(q, u, w, k, z, 2*zdim+1, uint8_t(super::shore), zdim);
     }
-
-    inline size_t zephyr_linear(size_t u, size_t w, size_t k, size_t z) const {
-        return z + zdim*(k + super::shore*(w + super::dim[0]*u));
+  
+    inline size_t zephyr_linear(bool u, size_w w, uint8_t k, size_z z) const {
+        return coordinate_converter::linemajor_linear(u, w, k, z, 2*zdim+1, uint8_t(super::shore), zdim);
     }
 
-    void construct_line(size_t u, size_t w, size_t z0, size_t z1, size_t k,
+    void construct_line(bool u, size_w w, size_z z0, size_z z1, uint8_t k,
                         vector<size_t> &chain) const {
-        minorminer_assert(z0 >= (k&1u));
-        minorminer_assert(z1 >= (k&1u));
+        minorminer_assert(z0 >= size_z(k&1u));
+        minorminer_assert(z1 >= size_z(k&1u));
         minorminer_assert(z1 >= z0);
-        size_t qz0 = (z0-(k&1))/2;
-        size_t qz1 = (z1-(k&1))/2;
-        for(size_t qz = qz0; qz <= qz1; qz++)
+        size_z qz0 = (z0-size_z(k&1))/2u;
+        size_z qz1 = (z1-size_z(k&1))/2u;
+        for(size_z qz = qz0; qz <= qz1; qz++)
             chain.push_back(zephyr_linear(u, w, k, qz));
     }
 
-    inline size_t line_length(size_t, size_t, size_t z0, size_t z1, uint8_t k) const {
-        minorminer_assert(z0 >= (k&1u));
-        minorminer_assert(z1 >= (k&1u));
+    inline size_t line_length(bool u, size_w, size_z z0, size_z z1, uint8_t k) const {
+        minorminer_assert(z0 >= size_z(k&1u));
+        minorminer_assert(z1 >= size_z(k&1u));
         minorminer_assert(z1 >= z0);
-        minorminer_assert(z1 < super::dim[0]);
-        return (z1-(k&1))/2 - (z0-(k&1))/2 + 1;
+        minorminer_assert(u?(horz(z1)<super::dim_x):
+                            (vert(z1)<super::dim_y));
+        return coordinate_index(((z1-size_z(k&1))/2u - (z0-size_z(k&1))/2u)) + 1;
     }
 
-    inline size_t biclique_length(size_t y0, size_t y1, size_t x0, size_t x1) const {
+    inline size_t biclique_length(size_y y0, size_y y1, size_x x0, size_x x1) const {
         minorminer_assert(y1 >= y0);
         minorminer_assert(x1 >= x0);
         // we return a length of zero for the ranges (0, super::dim[0]-1)
         // because there are zero qubits that span the entire range.
         size_t length = 0;
-        if (y0 > 0)
-            length = max(length, line_length(0, 0, y0, y1, 1));
-        if (x0 > 0)
-            length = max(length, line_length(0, 0, x0, x1, 1));
-        if (y1 + 1 < super::dim[0])
-            length = max(length, line_length(0, 0, y0, y1, 0));
-        if (x1 + 1 < super::dim[0])
-            length = max(length, line_length(0, 0, x0, x1, 0));
+        if (y0 > size_y(0))
+            length = max(length, line_length(0, 0, vert(y0), vert(y1), 1));
+        if (x0 > size_x(0))
+            length = max(length, line_length(1, 0, horz(x0), horz(x1), 1));
+        if (y1 + 1u < super::dim_y)
+            length = max(length, line_length(0, 0, vert(y0), vert(y1), 0));
+        if (x1 + 1u < super::dim_x)
+            length = max(length, line_length(1, 0, horz(x0), horz(x1), 0));
         return length;
     }
 
     vector<size_t> fragment_nodes(size_t q) const {
-        size_t u, w, k, z0;
+        bool u;
+        size_w w;
+        uint8_t k;
+        size_z z0;
         first_fragment(q, u, w, k, z0);
         vector<size_t> fragments;
-        for(size_t z = z0+2; z-->z0;) {
+        for(size_z z = z0+size_z(2); z-->z0;) {
             fragments.push_back(
-                u?super::chimera_linear(w, z, u, k):
-                  super::chimera_linear(z, w, u, k)
+                u?super::chimera_linear(horz(w), horz(z), u, k):
+                  super::chimera_linear(vert(z), vert(w), u, k)
             );
-        }        
+        }
         return fragments;
     }
 };
@@ -595,12 +616,17 @@ class topo_spec_cellmask : public topo_spec {
         super::process_nodes(nodemask, edgemask, badmask, nodes, populate_badmask{});
     }
     inline void finish_badmask(uint8_t *nodemask, uint8_t *badmask) const {
-        for(size_t y = 0, q = 0; y < super::dim[0]; y++)
-            for(size_t x = 0; x < super::dim[1]; x++) {
-                for(size_t k = 0; k < super::shore; k++, q++)
-                    badmask[q] &= nodemask[super::cell_addr(1, y, x)];
-                for(size_t k = 0; k < super::shore; k++, q++)
-                    badmask[q] &= nodemask[super::cell_addr(0, x, y)];
+        size_t q = 0;
+        for(size_y y = 0; y < super::dim_y; y++)
+            for(size_x x = 0; x < super::dim_x; x++) {
+                for(uint8_t k = 0; k < super::shore; k++) {
+                    minorminer_assert(q == super::chimera_linear(y, x, 0, k));
+                    badmask[q++] &= nodemask[super::cell_index(y, x, 1)];
+                }
+                for(uint8_t k = 0; k < super::shore; k++) {
+                    minorminer_assert(q == super::chimera_linear(y, x, 1, k));
+                    badmask[q++] &= nodemask[super::cell_index(y, x, 0)];
+                }
             }
     }
     inline vector<size_t> fragment_nodes(vector<size_t> &nodes) const {
diff --git a/minorminer/busclique.pyx b/minorminer/busclique.pyx
index 822852a9..850566d3 100644
--- a/minorminer/busclique.pyx
+++ b/minorminer/busclique.pyx
@@ -440,7 +440,14 @@ class busgraph_cache:
         return {}
         
     def draw_fragment_embedding(self, emb, **kwargs):
-        self._graph.draw_fragment_embedding(emb, **kwargs)
+        m, n, t, nodes, edges = self._graph.fragment_graph_spec()
+
+        f = dnx.chimera_graph(m, n=n, t=t, node_list=nodes, edge_list=edges)
+        f_emb = {
+            k : self._graph.fragment_nodes(c)
+            for k, c in self._graph.delabel(emb).items()
+        }
+        dnx.draw_chimera_embedding(f, f_emb, **kwargs)
 
 cdef dict _make_clique_cache(vector[embedding_t] &embs):
     """
@@ -522,7 +529,7 @@ cdef dict _make_biclique_cache(vector[pair[pair[size_t, size_t],
 
 def _trivial_relabeler(emb):
     """This doesn't relabel anything"""
-    return emb
+    return {v: tuple(chain) for v, chain in emb.items()}
 
 def _make_relabeler(f):
     """
@@ -530,7 +537,7 @@ def _make_relabeler(f):
     chain in the embedding.
     """
     def _relabeler(emb):
-        return {v: list(f(chain)) for v, chain in emb.items()}
+        return {v: tuple(f(chain)) for v, chain in emb.items()}
     return _relabeler
 
 ctypedef fused topo_cache_t:
@@ -658,29 +665,19 @@ cdef class _zephyr_busgraph:
             return {}
         return self.relabel(dict(zip(nodes, emb)))
 
-    def draw_fragment_embedding(self, emb, **kwargs):
-        """Draw the an embedding on the host graph, as it's represented in the 
-        clique/biclique embedding algorithms.  In the case of Chimera, the
-        internal representation is identical to the host graph.  In the case of
-        Zephyr and Pegasus, each qubit is broken into 2 or 6 'fragments'
-        respectively, making a Chimera-structured graph with :math:`K_{2t, 2t}`
-        or :math:`K_{2, 2}` unit tiles.
-        
-        Args:
-            emb (dict): the embedding to draw
-            **kwargs: keyword parameters to be passed to :func:`~dwave_networkx.draw_chimera_embedding`
-            
-        """
-    
-        m = self.topo.topo.dim[0]
-        n = self.topo.topo.dim[1]
+    def fragment_graph_spec(self):
+        m = coordinate_index(self.topo.topo.dim_y)
+        n = coordinate_index(self.topo.topo.dim_x)
         t = self.topo.topo.shore
-        f = dnx.chimera_graph(m, n=n, t=t, edge_list = self.topo.fragment_edges())
-        f_emb = {
-            k : self.topo.topo.fragment_nodes(c)
-            for k, c in self.delabel(emb).items()
-        }
-        dnx.draw_chimera_embedding(f, f_emb, **kwargs)
+        nodes = self.topo.fragment_nodes()
+        edges = self.topo.fragment_edges()
+        return m, n, t, nodes, edges
+        
+    def fragment_nodes(self, nodes = None):
+        if nodes is None:        
+            return self.topo.fragment_nodes()
+        else:
+            return self.topo.topo.fragment_nodes(nodes)
 
 cdef class _pegasus_busgraph:
     cdef topo_cache[pegasus_spec] *topo
@@ -777,17 +774,19 @@ cdef class _pegasus_busgraph:
             return {}
         return self.relabel(dict(zip(nodes, emb)))
 
-    def draw_fragment_embedding(self, emb, **kwargs):
-        m = self.topo.topo.dim[0]
-        n = self.topo.topo.dim[1]
+    def fragment_graph_spec(self):
+        m = coordinate_index(self.topo.topo.dim_y)
+        n = coordinate_index(self.topo.topo.dim_x)
         t = self.topo.topo.shore
-        f = dnx.chimera_graph(m, n, t, edge_list = self.topo.fragment_edges());
-        f_emb = {
-            k : self.topo.topo.fragment_nodes(c)
-            for k, c in self.delabel(emb).items()
-        }
-        dnx.draw_chimera_embedding(f, f_emb, **kwargs)
+        nodes = self.topo.fragment_nodes()
+        edges = self.topo.fragment_edges()
+        return m, n, t, nodes, edges
 
+    def fragment_nodes(self, nodes = None):
+        if nodes is None:        
+            return self.topo.fragment_nodes()
+        else:
+            return self.topo.topo.fragment_nodes(nodes)
 
 cdef class _chimera_busgraph:
     """Class for managing a single Chimera graph, and dispatches various 
@@ -888,16 +887,19 @@ cdef class _chimera_busgraph:
             return {}
         return self.relabel(dict(zip(nodes, emb)))
 
-    def draw_fragment_embedding(self, emb, **kwargs):
-        m = self.topo.topo.dim[0]
-        n = self.topo.topo.dim[1]
+    def fragment_graph_spec(self):
+        m = coordinate_index(self.topo.topo.dim_y)
+        n = coordinate_index(self.topo.topo.dim_x)
         t = self.topo.topo.shore
-        f = dnx.chimera_graph(m, n, t, edge_list = self.topo.fragment_edges());
-        f_emb = {
-            k : self.topo.topo.fragment_nodes(c)
-            for k, c in self.delabel(emb).items()
-        }
-        dnx.draw_chimera_embedding(f, f_emb, **kwargs)
+        nodes = self.topo.fragment_nodes()
+        edges = self.topo.fragment_edges()
+        return m, n, t, nodes, edges
+
+    def fragment_nodes(self, nodes = None):
+        if nodes is None:        
+            return self.topo.fragment_nodes()
+        else:
+            return self.topo.topo.fragment_nodes(nodes)
 
 class copy_on_close_context:
     def __init__(self):
diff --git a/minorminer/busclique_h.pxi b/minorminer/busclique_h.pxi
index 5579606f..36e73ea0 100644
--- a/minorminer/busclique_h.pxi
+++ b/minorminer/busclique_h.pxi
@@ -21,19 +21,22 @@ ctypedef vector[pair[size_t,size_t]] edges_t
 
 cdef extern from "../include/busclique/util.hpp" namespace "busclique":
     cdef cppclass pegasus_spec:
-        size_t dim[2]
+        size_y dim_y
+        size_x dim_x
         size_t shore
         pegasus_spec(size_t, vector[uint8_t], vector[uint8_t], uint32_t)
         nodes_t fragment_nodes(nodes_t)
 
     cdef cppclass chimera_spec:
-        size_t dim[2]
+        size_y dim_y
+        size_x dim_x
         size_t shore
         chimera_spec(size_t, size_t, uint8_t, uint32_t)
         nodes_t fragment_nodes(nodes_t)
 
     cdef cppclass zephyr_spec:
-        size_t dim[2]
+        size_y dim_y
+        size_x dim_x
         size_t shore
         zephyr_spec(size_t, uint8_t, uint32_t)
         nodes_t fragment_nodes(nodes_t)
@@ -78,3 +81,11 @@ cdef extern from "../include/busclique/find_clique.hpp" namespace "busclique":
 cdef extern from "../include/busclique/find_biclique.hpp" namespace "busclique":
     void best_bicliques[T](topo_cache[T], vector[pair[pair[size_t, size_t], embedding_t]] &)
 
+cdef extern from "../include/busclique/coordinate_types.hpp" namespace "busclique":
+    cdef cppclass size_y:
+        pass
+    cdef cppclass size_x:
+        pass
+
+    size_t coordinate_index(size_y)
+    size_t coordinate_index(size_x)
diff --git a/setup.py b/setup.py
index f1c6b2c7..ff49118f 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@
     exec(open(path_to_package_info).read())
 
 extra_compile_args = {
-    'msvc': ['/std:c++latest', '/MT', '/EHsc'],
+    'msvc': ['/std:c++latest', '/MT', '/EHsc', '/O2' ],
     'unix': ['-std=c++11', '-Wall', '-Wno-format-security', '-Ofast', '-fomit-frame-pointer', '-DNDEBUG', '-fno-rtti'],
 }
 
@@ -40,9 +40,12 @@
 
 if '--debug' in sys.argv or '-g' in sys.argv or 'CPPDEBUG' in os.environ:
     extra_compile_args['msvc'].append('/DCPPDEBUG')
-    extra_compile_args['unix'] = ['-std=c++1y', '-Wall',
-                                  '-O0', '-g', '-fipa-pure-const', '-DCPPDEBUG']
+    extra_compile_args['unix'] = ['-std=c++1y', '-Wall',# '-O0',
+                                  '-g', '-fipa-pure-const', '-DCPPDEBUG']
 
+if 'SAFE_COORDS' in os.environ:
+    extra_compile_args['msvc'].append('/DSAFE_COORDS')
+    extra_compile_args['unix'].append('-DSAFE_COORDS')
 
 class build_ext_compiler_check(build_ext):
     def build_extensions(self):


Test patch:
diff --git a/minorminer/tests/test_busclique.py b/minorminer/tests/test_busclique.py
index de87728c..c9216e59 100644
--- a/minorminer/tests/test_busclique.py
+++ b/minorminer/tests/test_busclique.py
@@ -25,14 +25,14 @@ def subgraph_edge_yield_few_bad(g, p, b):
 
     g must be a chimera graph or a pegasus graph with coordinate labels
     """
-    if g.graph['family'] == 'pegasus':
+    if g.graph['family'] == 'pegasus' or g.graph['family'] == 'zephyr':
         def icpl(p, q):
             return p[0] != q[0]
     elif g.graph['family'] == 'chimera':
         def icpl(p, q):
             return p[2] != q[2]
     else:
-        raise ValueError("g is neither a pegasus nor a chimera")
+        raise ValueError("g is not a pegasus, zephyr or chimera")
 
     h = g.copy()
     iedges, oedges = [], []
@@ -58,8 +58,10 @@ def relabel(g, node_label, edge_label, *args, **kwargs):
         f = dnx.pegasus_graph
     elif g.graph['family'] == 'chimera':
         f = dnx.chimera_graph
+    elif g.graph['family'] == 'zephyr':
+        f = dnx.zephyr_graph
     else:
-        raise ValueError("g is neither a pegasus nor a chimera")
+        raise ValueError("g is not a pegasus, zephyr or chimera")
 
     return f(*args, edge_list = edges, node_list = nodes, **kwargs)
 
@@ -73,21 +75,27 @@ def __init__(self, *args, **kwargs):
 
         self.c16 = dnx.chimera_graph(16)
         self.p16 = dnx.pegasus_graph(16)
+        self.z6 = dnx.zephyr_graph(6)
         self.c16c = dnx.chimera_graph(16, coordinates=True, data = False)
         self.c428 = dnx.chimera_graph(4, n = 2, t = 8)
         self.c248 = dnx.chimera_graph(2, n = 4, t = 8)
         self.c42A = dnx.chimera_graph(4, n = 2, t = 9)
         c4c_0 = subgraph_node_yield(dnx.chimera_graph(4, coordinates = True), .95)
         p4c_0 = subgraph_node_yield(dnx.pegasus_graph(4, coordinates = True), .95)
+        z4c_0 = subgraph_node_yield(dnx.zephyr_graph(4, coordinates = True), .95)
         c4c = [c4c_0,
                subgraph_edge_yield(c4c_0, .95),
                subgraph_edge_yield_few_bad(c4c_0, .95, 6)]
         p4c = [p4c_0,
                subgraph_edge_yield(p4c_0, .95),
                subgraph_edge_yield_few_bad(p4c_0, .95, 6)]
+        z4c = [z4c_0,
+               subgraph_edge_yield(z4c_0, .95),
+               subgraph_edge_yield_few_bad(z4c_0, .95, 6)]
 
         p4coords = dnx.pegasus_coordinates(4)
         c4coords = dnx.chimera_coordinates(4, 4, 4)
+        z4coords = dnx.zephyr_coordinates(4)
 
         c4 = [relabel(c,
                       c4coords.iter_chimera_to_linear,
@@ -104,8 +112,15 @@ def __init__(self, *args, **kwargs):
                        p4coords.iter_pegasus_to_nice_pairs,
                        4, nice_coordinates = True) for p in p4c]
 
+        z4 = [relabel(z,
+                      z4coords.iter_zephyr_to_linear,
+                      z4coords.iter_zephyr_to_linear_pairs,
+                      4) for z in z4c]
+
+
         self.c4, self.c4_nd, self.c4_d = list(zip(c4, c4c))
         self.p4, self.p4_nd, self.p4_d = list(zip(p4, p4c, p4n))
+        self.z4, self.z4_nd, self.z4_d = list(zip(z4, z4c))
 
     def test_p16(self):
         def reconstruct(nodes):
@@ -123,31 +138,41 @@ def run_battery(self, name, g, reconstruct,
                           test_python = False, test_nocache = True):
         labels = g.graph['labels']
 
+        clique_init_ok = False
         test = self.clique_battery(g, reconstruct,
                                    test_python = test_python,
                                    test_nocache = test_nocache)
-        size, cl = next(test)
-        self.assertEqual(size, cliquesize)
-        self.assertEqual(cl, cliquelength)
-        s = nx.complete_graph(size)
-        for i, (h, emb, kind, check_cl) in enumerate(test):
-            print(name, labels, kind)
-            if check_cl:
-                self.assertEqual(max_chainlength(emb), cl)
-            verify_embedding(emb, s, h)
-
+        with self.subTest(msg=f"clique_battery init"):
+            emb0, size, cl = next(test)
+            s = nx.complete_graph(size)
+            verify_embedding(emb0, s, g)
+            self.assertEqual(size, cliquesize)
+            self.assertEqual(cl, cliquelength)
+            clique_init_ok = True
+        if clique_init_ok:
+            for i, (h, emb_f, kind, check_cl) in enumerate(test):
+                with self.subTest(msg=f"clique_battery({name}, {labels}, {kind})"):
+                    emb = emb_f()
+                    verify_embedding(emb, s, h)
+                    if check_cl:
+                        self.assertEqual(max_chainlength(emb), cl)
+
+        biclique_init_ok = False
         test = self.biclique_battery(g, reconstruct)
-        size, cl = next(test)
-        self.assertEqual(size, bicliquesize)
-        if bicliquelength is not None:
-            self.assertEqual(cl, bicliquelength)
-        s = nx.complete_bipartite_graph(size, size)
-        for i, (h, emb, kind) in enumerate(test):
-            print(name, labels, kind)
+        with self.subTest(msg=f"biclique_battery init"):
+            emb0, size, cl = next(test)
+            s = nx.complete_bipartite_graph(size, size)
+            verify_embedding(emb0, s, g)
             if bicliquelength is not None:
-                self.assertEqual(max_chainlength(emb), bicliquelength)
-            verify_embedding(emb, s, h)
-
+                self.assertEqual(cl, bicliquelength)
+            biclique_init_ok = True
+        if biclique_init_ok:
+            for i, (h, emb_f, kind) in enumerate(test):
+                with self.subTest(msg=f"biclique_battery({name}, {labels}, {kind})"):
+                    emb = emb_f()
+                    verify_embedding(emb, s, h)
+                    if bicliquelength is not None:
+                        self.assertEqual(max_chainlength(emb), bicliquelength)
 
     def clique_battery(self, g, reconstruct,
                        test_python = False, test_nocache = True):
@@ -156,15 +181,15 @@ def clique_battery(self, g, reconstruct,
         size = len(emb0)
         cl = max_chainlength(emb0)
         N = range(size)
-        yield size, cl
-        yield g, emb0, 'g:bcg.lc', True
-        yield g, bgcg.find_clique_embedding(size), 'g:bcg.fce', True
-        yield g, busclique.find_clique_embedding(size, g), 'g:bc.fce', True
+        yield emb0, size, cl
+        yield g, lambda:emb0, 'g:bcg.lc', True
+        yield g, lambda:bgcg.find_clique_embedding(size), 'g:bcg.fce', True
+        yield g, lambda:busclique.find_clique_embedding(size, g), 'g:bc.fce', True
         if test_nocache:
             yield (g,
-                   busclique.find_clique_embedding(size, g, use_cache = False),
+                   lambda:busclique.find_clique_embedding(size, g, use_cache = False),
                    'g:bc.fce,nc', True)
-        yield g, bgcg.largest_clique_by_chainlength(cl), 'g:bc.lcbc', True
+        yield g, lambda:bgcg.largest_clique_by_chainlength(cl), 'g:bc.lcbc', True
         if test_python:
             if g.graph['family'] == 'chimera':
                 if g.graph['labels'] == 'int':
@@ -172,23 +197,23 @@ def clique_battery(self, g, reconstruct,
                     args = size, g.graph['rows']
                     kwargs = dict(target_edges = g.edges)
                     yield (g,
-                           chimera.find_clique_embedding(*args, **kwargs),
+                           lambda:chimera.find_clique_embedding(*args, **kwargs),
                            'g:legacy.fce', True)
             if g.graph['family'] == 'pegasus':
                 kwargs = dict(target_graph = g)
-                yield (g, pegasus.find_clique_embedding(size, **kwargs),
+                yield (g, lambda:pegasus.find_clique_embedding(size, **kwargs),
                        'g:legacy.fce', False)
 
         nodes = set(itertools.chain.from_iterable(emb0.values()))
         h = reconstruct(nodes)
         bgch = busclique.busgraph_cache(h)
-        yield h, busclique.find_clique_embedding(N, h), 'h:bc.fce', True
+        yield h, lambda:busclique.find_clique_embedding(N, h), 'h:bc.fce', True
         if test_nocache:
-            yield (h, busclique.find_clique_embedding(N, h, use_cache = False), 
+            yield (h, lambda:busclique.find_clique_embedding(N, h, use_cache = False),
                    'h:bc.fce,nc', True)
-        yield h, bgch.largest_clique(), 'h:bgc.lc', True
-        yield h, bgch.find_clique_embedding(N), 'h:bgc.fce', True
-        yield h, bgch.largest_clique_by_chainlength(cl), 'h:bgc.lcbc', True
+        yield h, lambda:bgch.largest_clique(), 'h:bgc.lc', True
+        yield h, lambda:bgch.find_clique_embedding(N), 'h:bgc.fce', True
+        yield h, lambda:bgch.largest_clique_by_chainlength(cl), 'h:bgc.lcbc', True
         if test_python:
             if g.graph['family'] == 'chimera':
                 if g.graph['labels'] == 'int':
@@ -196,13 +221,12 @@ def clique_battery(self, g, reconstruct,
                     args = size, h.graph['rows']
                     kwargs = dict(target_edges = h.edges)
                     yield (h,
-                           chimera.find_clique_embedding(*args, **kwargs),
+                           lambda:chimera.find_clique_embedding(*args, **kwargs),
                            'h:legacy.fce', True)
             if g.graph['family'] == 'pegasus':
                 kwargs = dict(target_graph = h)
-                yield (h, pegasus.find_clique_embedding(size, **kwargs),
+                yield (h, lambda:pegasus.find_clique_embedding(size, **kwargs),
                        'h:legacy.fce', False)
-            
 
     def biclique_battery(self, g, reconstruct):
         bgcg = busclique.busgraph_cache(g)
@@ -210,11 +234,11 @@ def biclique_battery(self, g, reconstruct):
         size = len(emb0)//2
         cl = max_chainlength(emb0)
         N = range(size)
-        yield size, cl
-        yield g, emb0, 'bgc.lbb'
-        yield (g, bgcg.find_biclique_embedding(N, range(size, 2*size)), 
+        yield emb0, size, cl
+        yield g, lambda:emb0, 'bgc.lbb'
+        yield (g, lambda:bgcg.find_biclique_embedding(N, range(size, 2*size)),
                   'bgc.fbe,list')
-        yield g, bgcg.find_biclique_embedding(size, size), 'bgc.fbe,ints'
+        yield g, lambda:bgcg.find_biclique_embedding(size, size), 'bgc.fbe,ints'
 
     @classmethod
     def tearDownClass(cls):
@@ -249,20 +273,33 @@ def test_labelings(self):
         def reconstructor(g):
             return lambda nodes: g.subgraph(nodes).copy()
 
-        names = 'c4_nd', 'c4', 'c4_d', 'p4_nd', 'p4', 'p4_d'
-        nocache = False, True, True, False, True, True
-        topos = self.c4_nd, self.c4, self.c4_d, self.p4_nd, self.p4, self.p4_d
+        names = 'c4_nd', 'c4', 'c4_d', 'p4_nd', 'p4', 'p4_d', 'z4_nd', 'z4', 'z4_d'
+        nocache = False, True, True, False, True, True, False, True, True
+        python = False, True, True, False, True, True, False, False, False
+        topos = self.c4_nd, self.c4, self.c4_d, self.p4_nd, self.p4, self.p4_d, self.z4_nd, self.z4, self.z4_d
 
-        for (name, test_nocache, G) in zip(names, nocache, topos):
+        for (name, test_nocache, test_python, G) in zip(names, nocache, python, topos):
             g0 = G[0]
             bgc = busclique.busgraph_cache(g0)
             K = bgc.largest_clique()
+            skip_K = True
+            with self.subTest(msg = f"test_labelings setup for {name} cliques"):
+                verify_embedding(K, nx.complete_graph(len(K)), g0)
+                skip_K = False
+
             B = bgc.largest_balanced_biclique()
+            skip_B = True
+            with self.subTest(msg = f"test_labelings setup for {name} bicliques"):
+                b2 = len(B)//2
+                verify_embedding(B, nx.complete_bipartite_graph(b2, b2), g0)
+                skip_B = False
+            if skip_K or skip_B:
+                continue
             for g in G:
                 self.run_battery(name, g, reconstructor(g), 
                                  len(K), max_chainlength(K),
                                  len(B)//2, None,
-                                 test_python = test_nocache,
+                                 test_python = test_python,
                                  test_nocache = test_nocache)
 
     def test_k4_bug(self):
@@ -270,14 +307,27 @@ def test_k4_bug(self):
         p = dnx.pegasus_graph(16, edge_list = edges)
         k4 = busclique.find_clique_embedding(4, p)
 
-        self.assertEquals(k4, {})
+        self.assertEqual(k4, {})
         
-class TestZephyr(unittest.TestCase):
-    def __init__(self, *args, **kwargs):
-        super(TestZephyr, self).__init__(*args, **kwargs)
+    def test_seed_determinism(self):
+        for g in (self.c4_nd[0], self.z4_nd[0], self.p4_nd[0]):
+            seed = random.randint(0, 2**32-1)
+            with self.subTest(msg=f"seed determinism test: {g.graph['family']}"):
+                bgc = busclique.busgraph_cache(g, seed = seed)
+                Klarge = bgc.largest_clique()
+                nK = len(Klarge)
+                Blarge = bgc.largest_balanced_biclique()
+                nB = len(Blarge)//2
+                Kfamily = [bgc.find_clique_embedding(i) for i in range(nK+1)]
+                Bfamily = [bgc.find_biclique_embedding(i,i) for i in range(nB+1)]
+                # now, clear the cache and go again
+                busclique.busgraph_cache.clear_all_caches()
+                bgc = busclique.busgraph_cache(g, seed = seed)
+                self.assertEqual(Klarge, bgc.largest_clique())
+                self.assertEqual(Blarge, bgc.largest_balanced_biclique())
+                self.assertEqual(Kfamily, [bgc.find_clique_embedding(i) for i in range(nK+1)])
+                self.assertEqual(Bfamily, [bgc.find_biclique_embedding(i,i) for i in range(nB+1)])
 
-        self.z6 = dnx.zephyr_graph(6)
-    
     def test_perfect_z6_clique(self):
         k88 = nx.complete_graph(88)
         bgc = busclique.busgraph_cache(self.z6)
@@ -295,4 +345,85 @@ def test_perfect_z6_biclique(self):
         bgc = busclique.busgraph_cache(self.z6)
         e88_88_cache = bgc.largest_balanced_biclique()
         verify_embedding(e88_88_cache, k88_88, self.z6)
-                
+
+    def test_z3_determinism(self):
+        #first, check an explicit sample found with find_nondeterminism
+        z3 = dnx.zephyr_graph(3)
+        z3.remove_nodes_from([11, 43, 61, 77, 102, 107, 143, 145, 162, 174, 178, 185, 240, 333])
+        z3.remove_edges_from([
+            (28, 29), (28, 264), (37, 40), (55, 259), (73, 76), (96, 99), (97, 98), (98, 302),
+            (108, 111), (110, 298), (117, 118), (117, 218), (128, 131), (133, 135), (140, 142),
+            (158, 161), (160, 281), (169, 171), (187, 190), (207, 208), (212, 215), (235, 236),
+            (236, 239), (242, 245), (265, 266), (316, 317), (319, 321),
+        ])
+        emb0 = busclique.busgraph_cache(z3).largest_clique()
+        emb1 = busclique.find_clique_embedding(len(emb0), z3, use_cache=False)
+        self.assertEqual(sorted(emb0.values()), sorted(emb1.values()))
+
+        #then, do a quick scan for another example -- this will present as a flaky test if
+        #we merely got lucky hammering down the tet above
+        self.assertEqual(None, find_nondeterminism('zephyr', 3, 10))
+
+    def test_p4_determinism(self):
+        #first, check an explicit sample found with find_nondeterminism
+        p4 = dnx.pegasus_graph(4)
+        p4.remove_nodes_from([
+            38, 56, 69, 81, 92, 97, 107, 112, 113, 207, 209, 212, 240, 258, 264, 277, 281
+        ])
+        p4.remove_edges_from([
+            (12, 13), (14, 17), (27, 28), (28, 219), (29, 234), (32, 255), (37, 189), (44, 225),
+            (52, 53), (54, 55), (61, 62), (66, 67), (74, 77), (85, 86), (114, 115), (123, 176),
+            (152, 155), (154, 155), (156, 159), (193, 196), (198, 199), (204, 205), (241, 242),
+            (249, 250), (266, 269), (271, 272),
+        ])
+        emb0 = busclique.busgraph_cache(p4).largest_clique()
+        emb1 = busclique.find_clique_embedding(len(emb0), p4, use_cache=False)
+        self.assertEqual(sorted(emb0.values()), sorted(emb1.values()))
+
+        #then, do a quick scan for another example -- this will present as a flaky test if
+        #we merely got lucky hammering down the tet above
+        self.assertEqual(None, find_nondeterminism('pegasus', 4, 10))
+
+    def test_c4_determinism(self):
+        #first, check an explicit sample found with find_nondeterminism
+        c4 = dnx.chimera_graph(4)
+        c4.remove_nodes_from([1, 47, 60, 62, 59, 107])
+        c4.remove_edges_from([
+            (0, 32), (24, 56), (26, 58), (32, 64), (37, 35), (39, 34), (44, 42), (53, 51),
+            (64, 69), (87, 95), (96, 103),
+        ])
+        emb0 = busclique.busgraph_cache(c4).largest_clique()
+        emb1 = busclique.find_clique_embedding(len(emb0), c4, use_cache=False)
+        self.assertEqual(sorted(emb0.values()), sorted(emb1.values()))
+
+        #then, do a quick scan for another example -- this will present as a flaky test if
+        #we merely got lucky hammering down the tet above
+        self.assertEqual(None, find_nondeterminism('chimera', 4, 10))
+
+def find_nondeterminism(family, size=4, tries=10):
+    if family == 'pegasus':
+        generator = dnx.pegasus_graph
+        coordinates = dnx.pegasus_coordinates
+    elif family == 'zephyr':
+        generator = dnx.zephyr_graph
+        coordinates = dnx.zephyr_coordinates
+    elif family == 'chimera':
+        generator = dnx.chimera_graph
+        coordinates = dnx.chimera_coordinates
+
+    for i in range(tries):
+        H = generator(size, coordinates=True)
+        G = subgraph_edge_yield_few_bad(subgraph_node_yield(H, .95), .95, 6)
+        emb0 = busclique.busgraph_cache(G, seed = random.randint(0, 2**32-1)).largest_clique()
+        emb1 = busclique.find_clique_embedding(len(emb0), G, use_cache=False, seed=random.randint(0, 2**32-1))
+        if sorted(emb0.values()) != sorted(emb1.values()):
+            coords = coordinates(size)
+            bad_nodes = [v for v in H if v not in G]
+            node_f = getattr(coords, f"iter_{family}_to_linear");
+            edge_f = getattr(coords, f"iter_{family}_to_linear_pairs");
+            return (
+                list(node_f(bad_nodes)),
+                list(edge_f(e for e in H.edges if not G.has_edge(*e) and all(v in G for v in e)))
+            )
+
+
diff --git a/test_requirements.txt b/test_requirements.txt
new file mode 100644
index 00000000..9c386a88
--- /dev/null
+++ b/test_requirements.txt
@@ -0,0 +1,2 @@
+pytest>=6.0.0
+pytest-subtests
diff --git a/tests/requirements.txt b/tests/requirements.txt
deleted file mode 100644
index 5d9cdb3d..00000000
--- a/tests/requirements.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-nose>=1.3
diff --git a/tests/test_lib.py b/tests/test_lib.py
index d5ffdcb7..1e49f549 100644
--- a/tests/test_lib.py
+++ b/tests/test_lib.py
@@ -27,6 +27,7 @@
 import time
 import signal
 import multiprocessing
+import unittest
 from unittest import SkipTest
 
 # Given that this test is in the tests directory, the calibration data should be
@@ -353,491 +354,531 @@ def _skip():
     else:
         return lambda f: f
 
+class TestFindEmbedding(unittest.TestCase):
+    @staticmethod
+    @success_count(100, 5)
+    def test_path_label_00(n):
+        p = Path(n)
+        return find_embedding(p, p)
+
+    @staticmethod
+    @success_count(100, 5)
+    def test_path_label_01(n):
+        p = Path(n)
+        L = [str(i) for i in range(n)]
+        Lp = [(L[x], L[y]) for x, y in p.edges()]
+        return find_embedding(p, Lp)
+
+    @staticmethod
+    @success_count(100, 5)
+    def test_path_label_10(n):
+        p = Path(n)
+        L = [str(i) for i in range(n)]
+        Lp = [(L[x], L[y]) for x, y in p.edges()]
+        return find_embedding(Lp, p)
+
+    @staticmethod
+    @success_count(100, 5)
+    def test_path_label_11(n):
+        p = Path(n)
+        L = [str(i) for i in range(n)]
+        Lp = [(L[x], L[y]) for x, y in p.edges()]
+        return find_embedding(Lp, Lp)
+
+    @staticmethod
+    @success_count(30, 3)
+    def test_grid_init_restrict(n):
+        from random import choice
+        chim = Chimera(n, l=4)
+        mask = mask_wxw(n, 1, l=4)
+        grid = Grid(2 * n)
+
+        init = {(x, y): [choice(mask[x // 2, y // 2])]
+                for x in range(2 * n) for y in range(2 * n)}
+        doms = {(x, y): mask[x // 2, y // 2]
+                for x in range(2 * n) for y in range(2 * n)}
+
+        return find_embedding(grid, chim, initial_chains=init, restrict_chains=doms, skip_initialization=False)
+
+    @staticmethod
+    @success_count(30, 3)
+    def test_grid_init(n):
+        chim = Chimera(n, l=4)
+        mask = mask_wxw(n, 1, l=2)
+        grid = Grid(2 * n)
+
+        init = {(x, y): mask[x // 2, y // 2]
+                for x in range(2 * n) for y in range(2 * n)}
+
+        return find_embedding(grid, chim, initial_chains=init, skip_initialization=False)
+
+    @staticmethod
+    @success_count(30, 15, 7)
+    def test_nae3sat(n, m):
+        chim = Chimera(m)
+        prob = NAE3SAT(n)
+
+        return find_embedding(prob, chim)
+
+    @staticmethod
+    @success_count(30, 79, 6)
+    def test_expander(p, m):
+        prob = ChordalCycle(p)
+        chim = Chimera(m)
+
+        return find_embedding(prob, chim)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_cartesian(n):
+        prob = CartesianProduct(n)
+        chim = Chimera(n, l=n)
+
+        return find_embedding(prob, chim)
+
+    @staticmethod
+    @success_count(30, 45, 6)
+    def test_geometric_nohint(n, m):
+        prob = GeometricGraph(n)
+        chim = Chimera(m)
+
+        return find_embedding(prob, chim)
+
+    @staticmethod
+    @success_count(30, 55, 6)
+    def test_geometric_hint(n, m):
+        from random import randint
+        pos = {}
+        chains = {}
+        for i in range(n):
+            x = randint(0, m - 1)
+            k1 = randint(0, 3)
+            y = randint(0, m - 1)
+            k2 = randint(0, 3)
+            pos[i] = (4 * x + k2) / 4. / m, (4 * y + k1) / 4. / m
+            chains[i] = (x, y, 0, k1), (x, y, 1, k2)
+        prob = GeometricGraph(n, pos)
+        chim = Chimera(m)
+
+        return find_embedding(prob, chim, initial_chains={i: c for i, c in chains.items() if i in pos})
+
+    @staticmethod
+    @success_count(30, 3)
+    def test_grid_restrict(n):
+        chim = Chimera(n)
+        mask = mask_wxw(n, 1)
+        grid = Grid(2 * n)
+
+        doms = {(x, y): mask[x // 2, y // 2]
+                for x in range(2 * n) for y in range(2 * n)}
+
+        check_args(grid, chim, restrict_chains=doms)
+
+        return find_embedding(grid, chim, restrict_chains=doms)
+
+    @staticmethod
+    @success_perfect(100, 4)
+    def test_grid_with_answer_fast(n):
+        chim = Chimera(n)
+        grid = Grid(2 * n)
+
+        init = GridChimeraEmbedding(2 * n)
+
+        check_args(grid, chim, initial_chains=init, skip_initialization=True)
+
+        return find_embedding(grid, chim, initial_chains=init, skip_initialization=True, chainlength_patience=0)
+
+    @staticmethod
+    @success_perfect(100, 2)
+    def test_grid_with_answer_slow(n):
+        chim = Chimera(n)
+        grid = Grid(2 * n)
+
+        init = GridChimeraEmbedding(2 * n)
+
+        check_args(grid, chim, initial_chains=init, skip_initialization=True)
+
+        return find_embedding(grid, chim, initial_chains=init, skip_initialization=True, chainlength_patience=10)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_grid_suspend(n):
+        chim = Chimera(n)
+        mask = mask_wxw(n, 1)
+        grid = Grid(2 * n)
+
+        suspg = [((x, y), (x // 2, y // 2, 0))
+                 for x in range(2 * n) for y in range(2 * n)]
+        suspc = [((x, y, 0), m) for x in range(n)
+                 for y in range(n) for m in mask[x, y]]
+
+        chim.add_edges_from(suspc)
+        grid.add_edges_from(suspg)
+
+        suspension = {(x, y, 0): [(x, y, 0)] for x in range(n) for y in range(n)}
+
+        return find_embedding(grid, chim, fixed_chains=suspension, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30)
+    def test_suspend_example1():
+        K3 = nx.Graph([('A', 'B'), ('B', 'C'), ('C', 'A')])
+        C = dnx.chimera_graph(1, 2, coordinates=False)
+
+        # Example with one blob for one node. Source node will use at least one.
+        blob = [4, 5, 12, 13]
+        suspend_chains = {'A': [blob]}
+        return find_embedding(K3, C, suspend_chains=suspend_chains)
+
+    @staticmethod
+    @success_count(30)
+    def test_suspend_example2():
+        K3 = nx.Graph([('A', 'B'), ('B', 'C'), ('C', 'A')])
+        C = dnx.chimera_graph(1, 2, coordinates=False)
+
+        # Example with one blob for one node, and two blobs for another.
+        # Second source node is forced to use at least one in each blob.
+        blob_A0 = [4, 5]
+        blob_A1 = [12, 13]
+        blob_C0 = [6, 7, 14, 15]
+        suspend_chains = {'A': [blob_A0, blob_A1], 'C': [blob_C0]}
+        return find_embedding(K3, C, suspend_chains=suspend_chains)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_grid_plant_suspend(n):
+        chim = Chimera(n)
+        mask = mask_wxw(n, 1)
+        grid = Grid(2 * n)
+
+        suspg = [((x, y), (x // 2, y // 2, 0))
+                 for x in range(2 * n) for y in range(2 * n)]
+        suspc = [(m, (x, y, 0)) for x in range(n)
+                 for y in range(n) for m in mask[x, y]]
 
-@success_count(100, 5)
-def test_path_label_00(n):
-    p = Path(n)
-    return find_embedding(p, p)
+        chim.add_edges_from(suspc)
+        grid.add_edges_from(suspg)
 
-
-@success_count(100, 5)
-def test_path_label_01(n):
-    p = Path(n)
-    L = [str(i) for i in range(n)]
-    Lp = [(L[x], L[y]) for x, y in p.edges()]
-    return find_embedding(p, Lp)
-
-
-@success_count(100, 5)
-def test_path_label_10(n):
-    p = Path(n)
-    L = [str(i) for i in range(n)]
-    Lp = [(L[x], L[y]) for x, y in p.edges()]
-    return find_embedding(Lp, p)
-
-
-@success_count(100, 5)
-def test_path_label_11(n):
-    p = Path(n)
-    L = [str(i) for i in range(n)]
-    Lp = [(L[x], L[y]) for x, y in p.edges()]
-    return find_embedding(Lp, Lp)
-
-
-@success_count(30, 3)
-def test_grid_init_restrict(n):
-    from random import choice
-    chim = Chimera(n, l=4)
-    mask = mask_wxw(n, 1, l=4)
-    grid = Grid(2 * n)
-
-    init = {(x, y): [choice(mask[x // 2, y // 2])]
-            for x in range(2 * n) for y in range(2 * n)}
-    doms = {(x, y): mask[x // 2, y // 2]
-            for x in range(2 * n) for y in range(2 * n)}
-
-    return find_embedding(grid, chim, initial_chains=init, restrict_chains=doms, skip_initialization=False)
-
-
-@success_count(30, 3)
-def test_grid_init(n):
-    chim = Chimera(n, l=4)
-    mask = mask_wxw(n, 1, l=2)
-    grid = Grid(2 * n)
-
-    init = {(x, y): mask[x // 2, y // 2]
-            for x in range(2 * n) for y in range(2 * n)}
-
-    return find_embedding(grid, chim, initial_chains=init, skip_initialization=False)
-
-
-@success_count(30, 15, 7)
-def test_nae3sat(n, m):
-    chim = Chimera(m)
-    prob = NAE3SAT(n)
-
-    return find_embedding(prob, chim)
-
-
-@success_count(30, 79, 6)
-def test_expander(p, m):
-    prob = ChordalCycle(p)
-    chim = Chimera(m)
-
-    return find_embedding(prob, chim)
-
-
-@success_count(30, 5)
-def test_cartesian(n):
-    prob = CartesianProduct(n)
-    chim = Chimera(n, l=n)
-
-    return find_embedding(prob, chim)
-
-
-@success_count(30, 45, 6)
-def test_geometric_nohint(n, m):
-    prob = GeometricGraph(n)
-    chim = Chimera(m)
-
-    return find_embedding(prob, chim)
-
-
-@success_count(30, 55, 6)
-def test_geometric_hint(n, m):
-    from random import randint
-    pos = {}
-    chains = {}
-    for i in range(n):
-        x = randint(0, m - 1)
-        k1 = randint(0, 3)
-        y = randint(0, m - 1)
-        k2 = randint(0, 3)
-        pos[i] = (4 * x + k2) / 4. / m, (4 * y + k1) / 4. / m
-        chains[i] = (x, y, 0, k1), (x, y, 1, k2)
-    prob = GeometricGraph(n, pos)
-    chim = Chimera(m)
-
-    return find_embedding(prob, chim, initial_chains={i: c for i, c in chains.items() if i in pos})
-
-
-@success_count(30, 3)
-def test_grid_restrict(n):
-    chim = Chimera(n)
-    mask = mask_wxw(n, 1)
-    grid = Grid(2 * n)
-
-    doms = {(x, y): mask[x // 2, y // 2]
-            for x in range(2 * n) for y in range(2 * n)}
-
-    check_args(grid, chim, restrict_chains=doms)
-
-    return find_embedding(grid, chim, restrict_chains=doms)
-
-
-@success_perfect(100, 4)
-def test_grid_with_answer_fast(n):
-    chim = Chimera(n)
-    grid = Grid(2 * n)
-
-    init = GridChimeraEmbedding(2 * n)
-
-    check_args(grid, chim, initial_chains=init, skip_initialization=True)
-
-    return find_embedding(grid, chim, initial_chains=init, skip_initialization=True, chainlength_patience=0)
-
-
-@success_perfect(100, 2)
-def test_grid_with_answer_slow(n):
-    chim = Chimera(n)
-    grid = Grid(2 * n)
-
-    init = GridChimeraEmbedding(2 * n)
-
-    check_args(grid, chim, initial_chains=init, skip_initialization=True)
-
-    return find_embedding(grid, chim, initial_chains=init, skip_initialization=True, chainlength_patience=10)
-
-
-@success_count(30, 5)
-def test_grid_suspend(n):
-    chim = Chimera(n)
-    mask = mask_wxw(n, 1)
-    grid = Grid(2 * n)
-
-    suspg = [((x, y), (x // 2, y // 2, 0))
-             for x in range(2 * n) for y in range(2 * n)]
-    suspc = [((x, y, 0), m) for x in range(n)
-             for y in range(n) for m in mask[x, y]]
-
-    chim.add_edges_from(suspc)
-    grid.add_edges_from(suspg)
-
-    suspension = {(x, y, 0): [(x, y, 0)] for x in range(n) for y in range(n)}
-
-    return find_embedding(grid, chim, fixed_chains=suspension, chainlength_patience=0)
-
-
-@success_count(30)
-def test_suspend_example1():
-    K3 = nx.Graph([('A', 'B'), ('B', 'C'), ('C', 'A')])
-    C = dnx.chimera_graph(1, 2, coordinates=False)
-
-    # Example with one blob for one node. Source node will use at least one.
-    blob = [4, 5, 12, 13]
-    suspend_chains = {'A': [blob]}
-    return find_embedding(K3, C, suspend_chains=suspend_chains)
-
-
-@success_count(30)
-def test_suspend_example2():
-    K3 = nx.Graph([('A', 'B'), ('B', 'C'), ('C', 'A')])
-    C = dnx.chimera_graph(1, 2, coordinates=False)
-
-    # Example with one blob for one node, and two blobs for another.
-    # Second source node is forced to use at least one in each blob.
-    blob_A0 = [4, 5]
-    blob_A1 = [12, 13]
-    blob_C0 = [6, 7, 14, 15]
-    suspend_chains = {'A': [blob_A0, blob_A1], 'C': [blob_C0]}
-    return find_embedding(K3, C, suspend_chains=suspend_chains)
-
-
-@success_count(30, 5)
-def test_grid_plant_suspend(n):
-    chim = Chimera(n)
-    mask = mask_wxw(n, 1)
-    grid = Grid(2 * n)
-
-    suspg = [((x, y), (x // 2, y // 2, 0))
-             for x in range(2 * n) for y in range(2 * n)]
-    suspc = [(m, (x, y, 0)) for x in range(n)
-             for y in range(n) for m in mask[x, y]]
-
-    chim.add_edges_from(suspc)
-    grid.add_edges_from(suspg)
-
-    suspension = {(x, y, 0): [(x, y, 0)] for x in range(n) for y in range(n)}
-    init = {(x, y): mask[x // 2, y // 2]
-            for x in range(2 * n) for y in range(2 * n)}
-
-    return find_embedding(grid, chim, fixed_chains=suspension, initial_chains=init, chainlength_patience=0)
-
-
-@success_count(30, 5)
-def test_grid_suspend_chains(n):
-    chim = Chimera(n)
-    mask = mask_wxw(n, 1)
-    grid = Grid(2 * n)
-
-    suspension = {(x, y): [mask[x//2, y//2]]
-                  for x in range(2*n) for y in range(2*n)}
-
-    return find_embedding(grid, chim, suspend_chains=suspension, chainlength_patience=0)
-
-
-@success_count(30, 5)
-def test_grid_suspend_domain(n):
-    chim = Chimera(n)
-    mask = mask_wxw(n, 1)
-    grid = Grid(2 * n)
-
-    suspg = [((x, y), (x // 2, y // 2, 0))
-             for x in range(2 * n) for y in range(2 * n)]
-    suspc = [((x, y, 0), m) for x in range(n)
-             for y in range(n) for m in mask[x, y]]
-
-    chim.add_edges_from(suspc)
-    grid.add_edges_from(suspg)
-
-    suspension = {(x, y, 0): [(x, y, 0)] for x in range(n) for y in range(n)}
-    doms = {(x, y): mask[x // 2, y // 2]
-            for x in range(2 * n) for y in range(2 * n)}
-
-    check_args(grid, chim, fixed_chains=suspension,
-               skip_initialization=False, restrict_chains=doms)
-
-    return find_embedding(grid, chim, fixed_chains=suspension, restrict_chains=doms, chainlength_patience=0)
-
-
-@success_count(30, 5)
-def test_grid_cheat_domain(n):
-    chim = Chimera(n)
-    grid = Grid(2 * n)
-    cheat = GridChimeraEmbedding(2 * n)
-
-    return find_embedding(grid, chim, restrict_chains=cheat, chainlength_patience=0)
-
-
-@success_count(30, 2)
-def test_biclique_chimera(n):
-    chim = Chimera(n)
-    kliq = Biclique(4 * n)
-
-    return find_embedding(kliq, chim, chainlength_patience=0)
-
-
-@success_count(30, 5)
-def test_path_cheat_domain(n):
-    P = Path(n)
-    cheat = {p: [p] for p in range(n)}
-
-    return find_embedding(P, P, restrict_chains=cheat, chainlength_patience=0)
-
-
-@success_count(30, 6, 25)
-def test_clique(n, k):
-    chim = Chimera(n)
-    cliq = Clique(k)
-
-    return find_embedding(cliq, chim, chainlength_patience=0)
-
-
-@success_perfect(20, 25, 25)
-def test_clique_clique(n, k):
-    cliq = Clique(k)
-
-    return find_embedding(cliq, cliq, chainlength_patience=0)
-
-
-@success_perfect(3, 16)
-def test_clique_large_nosegfault(n):
-    chim = Chimera(n)
-    cliq = Clique(4 * n + 2)
-
-    return not find_embedding(cliq, chim, chainlength_patience=0, timeout=1)
-
-
-@success_count(30, 6, 25)
-def test_clique_parallel(n, k):
-    chim = Chimera(n)
-    cliq = Clique(k)
-
-    return find_embedding(cliq, chim, chainlength_patience=0, threads=2)
-
-
-@success_count(30, 3, 13)
-def test_clique_term(n, k):
-    chim = Chimera(n)
-    cliq = Clique(k)
-    chim.add_edge((n // 2, n // 2, 0, 0), k)
-    cliq.add_edge(0, k)
-    fix = {k: [k]}
-    return find_embedding(cliq, chim, fixed_chains=fix, chainlength_patience=0)
-
-
-@success_count(30, 8)
-def test_grid_heal_A(n):
-    from random import randint
-    grid = Grid(2 * n)
-    chim = Chimera(n + 2)
-    breaks = {(x, x, x % 2, randint(0, 3)) for x in range(1, 4)}
-    chim = [e for e in chim.edges() if not breaks.intersection(e)]
-
-    emb = GridChimeraEmbedding(2 * n)
-    i_emb = {}
-    for v, chain in emb.items():
-        remainder = {(x + 1, y + 1, u, k)
-                     for x, y, u, k in chain}.difference(breaks)
-        if remainder:
-            i_emb[v] = remainder
-
-    return find_embedding(grid, chim, initial_chains=i_emb, chainlength_patience=0)
-
-
-@success_count(30, 4)
-def test_grid_heal_B(n):
-    from random import randint
-    grid = Grid(2 * n)
-    chim = Chimera(n + 2)
-    breaks = {(x, x, x % 2, randint(0, 3)) for x in range(1, 4)}
-
-    chim.add_edges_from((b, (b, None)) for b in breaks)
-    grid.add_edges_from((b, (b, None)) for b in breaks)
-    f_emb = {(b, None): [(b, None)] for b in breaks}
-
-    emb = GridChimeraEmbedding(2 * n)
-
-    return find_embedding(grid, chim, initial_chains=emb, fixed_chains=f_emb, chainlength_patience=0)
-
-
-@success_perfect(1000, 3)
-def test_fail_impossible(n):
-    Kn = Clique(n)  # we're gonna try to embed this here clique
-    Pn = Path(n)  # into this here path, and it ain't gonna work
-
-    return not find_embedding(Kn, Pn)
-
-
-@success_perfect(1, 16, .1)
-def test_fail_timeout(n, t):
-    Kn = Clique(4 * n + 1)  # we're gonna try to embed this here clique
-    # into this here chimera, and it might work but we'll time out
-    Cn = Chimera(n)
-
-    return not find_embedding(Kn, Cn, tries=1e6, max_no_improvement=1e6, inner_rounds=1e6, timeout=t, threads=4)
-
-
-@success_count(30)
-def test_chainlength_fast():
-    C = Chimera(4)
-    K = Clique(16)
-    e = find_embedding(K, C, tries=1, chainlength_patience=1)
-    if not len(e):
-        return False
-    return max(len(c) for c in e.values()) <= 7
-
-
-@success_count(30)
-def test_chainlength_slow():
-    C = Chimera(4)
-    K = Clique(16)
-    e = find_embedding(K, C, tries=1, chainlength_patience=10)
-    if not len(e):
-        return False
-    return max(len(c) for c in e.values()) <= 6
-
-
-@success_count(30)
-def test_isolated_variables_as_if_that_was_smart():
-    # embedding isolated notes is literally the worst way to use a quantum computer.
-    #                    WHY AM I ASKED FOR THIS FEATURE
-    # like i'd understand if people were using this package for arbitary covering/packing problems on graphs, but
-    #                               SHAAAAAME
-    # that's not what's happening here
-    C = Chimera(4)
-    K = Clique(16)
-    K.add_edges_from((x, x) for x in range(16, 32))
-    C.add_edges_from((x, x) for x in range(16, 32))
-    return find_embedding(K, C, tries=1, chainlength_patience=0)
-
-
-@success_count(30)
-def test_isolated_variables_parallel():
-    C = Chimera(4)
-    K = Clique(16)
-    K.add_edges_from((x, x) for x in range(16, 32))
-    C.add_edges_from((x, x) for x in range(16, 32))
-    return find_embedding(K, C, tries=1, chainlength_patience=0, threads=10)
-
-
-@success_perfect(1)
-def test_empty_source():
-    # re: issue 64
-    C = Clique(2)
-    return find_embedding([], C, tries=1) == {}
-
-
-@success_perfect(1)
-def test_empty_target():
-    # re: issue 64
-    C = Clique(2)
-    try:
-        _ = find_embedding(C, [], tries=1)
+        suspension = {(x, y, 0): [(x, y, 0)] for x in range(n) for y in range(n)}
+        init = {(x, y): mask[x // 2, y // 2]
+                for x in range(2 * n) for y in range(2 * n)}
+
+        return find_embedding(grid, chim, fixed_chains=suspension, initial_chains=init, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_grid_suspend_chains(n):
+        chim = Chimera(n)
+        mask = mask_wxw(n, 1)
+        grid = Grid(2 * n)
+
+        suspension = {(x, y): [mask[x//2, y//2]]
+                      for x in range(2*n) for y in range(2*n)}
+
+        return find_embedding(grid, chim, suspend_chains=suspension, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_grid_suspend_domain(n):
+        chim = Chimera(n)
+        mask = mask_wxw(n, 1)
+        grid = Grid(2 * n)
+
+        suspg = [((x, y), (x // 2, y // 2, 0))
+                 for x in range(2 * n) for y in range(2 * n)]
+        suspc = [((x, y, 0), m) for x in range(n)
+                 for y in range(n) for m in mask[x, y]]
+
+        chim.add_edges_from(suspc)
+        grid.add_edges_from(suspg)
+
+        suspension = {(x, y, 0): [(x, y, 0)] for x in range(n) for y in range(n)}
+        doms = {(x, y): mask[x // 2, y // 2]
+                for x in range(2 * n) for y in range(2 * n)}
+
+        check_args(grid, chim, fixed_chains=suspension,
+                   skip_initialization=False, restrict_chains=doms)
+
+        return find_embedding(grid, chim, fixed_chains=suspension, restrict_chains=doms, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_grid_cheat_domain(n):
+        chim = Chimera(n)
+        grid = Grid(2 * n)
+        cheat = GridChimeraEmbedding(2 * n)
+
+        return find_embedding(grid, chim, restrict_chains=cheat, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 2)
+    def test_biclique_chimera(n):
+        chim = Chimera(n)
+        kliq = Biclique(4 * n)
+
+        return find_embedding(kliq, chim, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 5)
+    def test_path_cheat_domain(n):
+        P = Path(n)
+        cheat = {p: [p] for p in range(n)}
+
+        return find_embedding(P, P, restrict_chains=cheat, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 6, 25)
+    def test_clique(n, k):
+        chim = Chimera(n)
+        cliq = Clique(k)
+
+        return find_embedding(cliq, chim, chainlength_patience=0)
+
+    @staticmethod
+    @success_perfect(20, 25, 25)
+    def test_clique_clique(n, k):
+        cliq = Clique(k)
+
+        return find_embedding(cliq, cliq, chainlength_patience=0)
+
+    @staticmethod
+    @success_perfect(3, 16)
+    def test_clique_large_nosegfault(n):
+        chim = Chimera(n)
+        cliq = Clique(4 * n + 2)
+
+        return not find_embedding(cliq, chim, chainlength_patience=0, timeout=1)
+
+    @staticmethod
+    @success_count(30, 6, 25)
+    def test_clique_parallel(n, k):
+        chim = Chimera(n)
+        cliq = Clique(k)
+
+        return find_embedding(cliq, chim, chainlength_patience=0, threads=2)
+
+    @staticmethod
+    @success_count(30, 3, 13)
+    def test_clique_term(n, k):
+        chim = Chimera(n)
+        cliq = Clique(k)
+        chim.add_edge((n // 2, n // 2, 0, 0), k)
+        cliq.add_edge(0, k)
+        fix = {k: [k]}
+        return find_embedding(cliq, chim, fixed_chains=fix, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 8)
+    def test_grid_heal_A(n):
+        from random import randint
+        grid = Grid(2 * n)
+        chim = Chimera(n + 2)
+        breaks = {(x, x, x % 2, randint(0, 3)) for x in range(1, 4)}
+        chim = [e for e in chim.edges() if not breaks.intersection(e)]
+
+        emb = GridChimeraEmbedding(2 * n)
+        i_emb = {}
+        for v, chain in emb.items():
+            remainder = {(x + 1, y + 1, u, k)
+                         for x, y, u, k in chain}.difference(breaks)
+            if remainder:
+                i_emb[v] = remainder
+
+        return find_embedding(grid, chim, initial_chains=i_emb, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30, 4)
+    def test_grid_heal_B(n):
+        from random import randint
+        grid = Grid(2 * n)
+        chim = Chimera(n + 2)
+        breaks = {(x, x, x % 2, randint(0, 3)) for x in range(1, 4)}
+
+        chim.add_edges_from((b, (b, None)) for b in breaks)
+        grid.add_edges_from((b, (b, None)) for b in breaks)
+        f_emb = {(b, None): [(b, None)] for b in breaks}
+
+        emb = GridChimeraEmbedding(2 * n)
+
+        return find_embedding(grid, chim, initial_chains=emb, fixed_chains=f_emb, chainlength_patience=0)
+
+    @staticmethod
+    @success_perfect(1000, 3)
+    def test_fail_impossible(n):
+        Kn = Clique(n)  # we're gonna try to embed this here clique
+        Pn = Path(n)  # into this here path, and it ain't gonna work
+
+        return not find_embedding(Kn, Pn)
+
+    @staticmethod
+    @success_perfect(1, 16, .1)
+    def test_fail_timeout(n, t):
+        Kn = Clique(4 * n + 1)  # we're gonna try to embed this here clique
+        # into this here chimera, and it might work but we'll time out
+        Cn = Chimera(n)
+
+        return not find_embedding(Kn, Cn, tries=1e6, max_no_improvement=1e6, inner_rounds=1e6, timeout=t, threads=4)
+
+    @staticmethod
+    @success_count(30)
+    def test_chainlength_fast():
+        C = Chimera(4)
+        K = Clique(16)
+        e = find_embedding(K, C, tries=1, chainlength_patience=1)
+        if not len(e):
+            return False
+        return max(len(c) for c in e.values()) <= 7
+
+    @staticmethod
+    @success_count(30)
+    def test_chainlength_slow():
+        C = Chimera(4)
+        K = Clique(16)
+        e = find_embedding(K, C, tries=1, chainlength_patience=10)
+        if not len(e):
+            return False
+        return max(len(c) for c in e.values()) <= 6
+
+    @staticmethod
+    @success_count(30)
+    def test_isolated_variables_as_if_that_was_smart():
+        # embedding isolated notes is literally the worst way to use a quantum computer.
+        #                    WHY AM I ASKED FOR THIS FEATURE
+        # like i'd understand if people were using this package for arbitary covering/packing problems on graphs, but
+        #                               SHAAAAAME
+        # that's not what's happening here
+        C = Chimera(4)
+        K = Clique(16)
+        K.add_edges_from((x, x) for x in range(16, 32))
+        C.add_edges_from((x, x) for x in range(16, 32))
+        return find_embedding(K, C, tries=1, chainlength_patience=0)
+
+    @staticmethod
+    @success_count(30)
+    def test_isolated_variables_parallel():
+        C = Chimera(4)
+        K = Clique(16)
+        K.add_edges_from((x, x) for x in range(16, 32))
+        C.add_edges_from((x, x) for x in range(16, 32))
+        return find_embedding(K, C, tries=1, chainlength_patience=0, threads=10)
+
+
+    @staticmethod
+    @success_perfect(1)
+    def test_empty_source():
+        # re: issue 64
+        C = Clique(2)
+        return find_embedding([], C, tries=1) == {}
+
+    @staticmethod
+    @success_perfect(1)
+    def test_empty_target():
+        # re: issue 64
+        C = Clique(2)
+        try:
+            _ = find_embedding(C, [], tries=1)
+            return False
+        except ValueError:
+            return True
+
+    @staticmethod
+    @success_perfect(1)
+    def test_point_source():
+        # re: issue 64
+        C = Clique(2)
+        return find_embedding([(0, 0)], C, tries=1)
+
+    @staticmethod
+    @success_perfect(1)
+    def test_point_target():
+        # re: issue 64
+        C = Clique(2)
+        try:
+            _ = find_embedding(C, [(0, 0)], tries=1)
+        except RuntimeError:
+            return True
         return False
-    except ValueError:
-        return True
 
 
-@success_perfect(1)
-def test_point_source():
-    # re: issue 64
-    C = Clique(2)
-    return find_embedding([(0, 0)], C, tries=1)
-
-
-@success_perfect(1)
-def test_point_target():
-    # re: issue 64
-    C = Clique(2)
-    try:
-        _ = find_embedding(C, [(0, 0)], tries=1)
-    except RuntimeError:
-        return True
-    return False
-
-
-@success_perfect(30)
-def test_fixed_chain_issue91():
-    K2 = Clique(2)
-    K3 = Clique(3)
-    return find_embedding(K2, K3, fixed_chains={0: [0, 1]})
-
-@success_perfect(10)
-def test_isolated_nodes_networkx():
-    E10 = nx.Graph()
-    E10.add_nodes_from(range(10))
-    K10 = nx.complete_graph(10)
-    return find_embedding(E10, K10)
-
-@success_count(30)
-def test_qubit_components():
-    # we only embed into the largest connected component -- we make a random set of components which
-    # cannot exceed the size of the actual Chimera graph (so the performance of this test will be stable)
-    from random import randint
-    C = Chimera(4)
-    for _ in range(50):
-        C.add_edge(randint(0, 100), randint(0, 100))
-    K = Clique(16)
-    K.add_edges_from((x, x) for x in range(16, 32))
-    return find_embedding(K, C, tries=1, chainlength_patience=0)
-
-
-@success_count(30)
-def test_variable_components():
-    # embed two problems at once?  why not
-    C = Chimera(4)
-    K = Clique(8)
-    K.add_edges_from([(u+8, v+8) for u, v in K.edges()])
-    return find_embedding(K, C, tries=1, chainlength_patience=0)
-
-
-@success_count(30)
-def test_variable_components_many():
-    from random import randint
-    C = Chimera(8)
-    K = [(randint(0, 128), randint(0, 128)) for _ in range(64)]
-    return find_embedding(K, C, tries=1, chainlength_patience=0)
+    @staticmethod
+    @success_perfect(30)
+    def test_fixed_chain_issue91():
+        K2 = Clique(2)
+        K3 = Clique(3)
+        return find_embedding(K2, K3, fixed_chains={0: [0, 1]})
+
+    @staticmethod
+    @success_perfect(10)
+    def test_isolated_nodes_networkx():
+        E10 = nx.Graph()
+        E10.add_nodes_from(range(10))
+        K10 = nx.complete_graph(10)
+        return find_embedding(E10, K10)
+
+    @staticmethod
+    @success_count(30)
+    def test_qubit_components():
+        # we only embed into the largest connected component -- we make a random set of components which
+        # cannot exceed the size of the actual Chimera graph (so the performance of this test will be stable)
+        from random import randint
+        C = Chimera(4)
+        for _ in range(50):
+            C.add_edge(randint(0, 100), randint(0, 100))
+        K = Clique(16)
+        K.add_edges_from((x, x) for x in range(16, 32))
+        return find_embedding(K, C, tries=1, chainlength_patience=0)
+
+
+    @staticmethod
+    @success_count(30)
+    def test_variable_components():
+        # embed two problems at once?  why not
+        C = Chimera(4)
+        K = Clique(8)
+        K.add_edges_from([(u+8, v+8) for u, v in K.edges()])
+        return find_embedding(K, C, tries=1, chainlength_patience=0)
+
+
+    @staticmethod
+    @success_count(30)
+    def test_variable_components_many():
+        from random import randint
+        C = Chimera(8)
+        K = [(randint(0, 128), randint(0, 128)) for _ in range(64)]
+        return find_embedding(K, C, tries=1, chainlength_patience=0)
+
+    @staticmethod
+    @skip_if(
+        # there appears to be a bug in the osx, py3.8+ version of multiprocessing.
+        # this has turned into a yak-shaving exercise and I'm not doing any more here
+        # for the time being -- #TODO if anybody has a mac and they wanna dig in, please do
+        (sys.version_info[:2] >= (3, 8) and sys.platform == 'darwin') or
+
+        # TODO this test seems to actually work on windows but it's doing something funky
+        # to our appveyor framework.  Giving up on yak-shaving 'cause we're going to retire
+        # appveyor soon
+        (sys.platform == 'win32')
+    )
+    @staticmethod
+    @success_perfect(1)
+    def test_interactive_interrupt():
+        return run_interactive_interrupt(True) == 0
+
+    @staticmethod
+    @skip_if(
+        # there appears to be a bug in the osx, py3.8+ version of multiprocessing.
+        # this has turned into a yak-shaving exercise and I'm not doing any more here
+        # for the time being -- #TODO if anybody has a mac and they wanna dig in, please do
+        (sys.version_info[:2] >= (3, 8) and sys.platform == 'darwin') or
+
+        # TODO this test seems to actually work on windows but it's doing something funky
+        # to our appveyor framework.  Giving up on yak-shaving 'cause we're going to retire
+        # appveyor soon
+        (sys.platform == 'win32')
+    )
+    @success_perfect(1)
+    def test_headless_interrupt():
+        return run_interactive_interrupt(False) == 2
 
 
 def _long_running_successful_problem(interactive):
@@ -872,38 +913,6 @@ def run_interactive_interrupt(interactive):
     return p.exitcode
 
 
-@skip_if(
-    # there appears to be a bug in the osx, py3.8+ version of multiprocessing.
-    # this has turned into a yak-shaving exercise and I'm not doing any more here
-    # for the time being -- #TODO if anybody has a mac and they wanna dig in, please do
-    (sys.version_info[:2] >= (3, 8) and sys.platform == 'darwin') or
-
-    # TODO this test seems to actually work on windows but it's doing something funky
-    # to our appveyor framework.  Giving up on yak-shaving 'cause we're going to retire
-    # appveyor soon
-    (sys.platform == 'win32')
-)
-@success_perfect(1)
-def test_interactive_interrupt():
-    return run_interactive_interrupt(True) == 0
-
-
-@skip_if(
-    # there appears to be a bug in the osx, py3.8+ version of multiprocessing.
-    # this has turned into a yak-shaving exercise and I'm not doing any more here
-    # for the time being -- #TODO if anybody has a mac and they wanna dig in, please do
-    (sys.version_info[:2] >= (3, 8) and sys.platform == 'darwin') or
-
-    # TODO this test seems to actually work on windows but it's doing something funky
-    # to our appveyor framework.  Giving up on yak-shaving 'cause we're going to retire
-    # appveyor soon
-    (sys.platform == 'win32')
-)
-@success_perfect(1)
-def test_headless_interrupt():
-    return run_interactive_interrupt(False) == 2
-
-
 def chainlength_diagnostic(n=100, old=False, chainlength_argument=0, verbose=0, m=8):
     C = Chimera(m)
     K = Clique(4 * m)