We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix to repeated compilation of numba
- [x] Does this PR have a descriptive title that could go in our release notes?
- [ ] Does this PR add any new dependencies?
- [ ] Does this PR modify any existing APIs?
   - [ ] Is the change to the API backwards compatible?
- [ ] Have you built the documentation (reference and/or tutorial) and verified the generated documentation is appropriate?

Fixes #946

Utilizes the addition of a global function at the top of the file that calls the nb.jit function a single time instead of in the constructor. This speeds up the compilation and running speed of the code.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `graspologic/models/edge_swaps.py`
Content:
```
1 from typing import Optional
2 
3 import numba as nb
4 import numpy as np
5 from beartype import beartype
6 from scipy.sparse import csr_matrix, lil_matrix
7 from sklearn.utils import check_scalar
8 
9 from graspologic.preconditions import check_argument
10 from graspologic.types import AdjacencyMatrix, Tuple
11 from graspologic.utils import import_graph, is_loopless, is_symmetric, is_unweighted
12 
13 
14 # Code based on: https://github.com/joelnish/double-edge-swap-mcmc/blob/master/dbl_edge_mcmc.py
15 class EdgeSwapper:
16     """
17     Degree Preserving Edge Swaps
18 
19     This class allows for performing degree preserving edge swaps to
20     generate new networks with the same degree sequence as the input network.
21 
22     Attributes
23     ----------
24     adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)
25         The initial adjacency matrix to perform edge swaps on. Must be unweighted and undirected.
26 
27     edge_list : np.ndarray, shape (n_verts, 2)
28         The corresponding edgelist for the input network
29 
30     seed: int, optional
31         Random seed to make outputs reproducible, must be positive
32 
33 
34     References
35     ----------
36     .. [1] Fosdick, B. K., Larremore, D. B., Nishimura, J., & Ugander, J. (2018).
37            Configuring random graph models with fixed degree sequences.
38            Siam Review, 60(2), 315-355.
39 
40     .. [2] Carstens, C. J., & Horadam, K. J. (2017).
41            Switching edges to randomize networks: what goes wrong and how to fix it.
42            Journal of Complex Networks, 5(3), 337-351.
43 
44     .. [3] https://github.com/joelnish/double-edge-swap-mcmc/blob/master/dbl_edge_mcmc.py
45     """
46 
47     @beartype
48     def __init__(self, adjacency: AdjacencyMatrix, seed: Optional[int] = None):
49 
50         weight_check = is_unweighted(adjacency)
51         check_argument(weight_check, "adjacency must be unweighted")
52 
53         loop_check = is_loopless(adjacency)
54         check_argument(loop_check, "adjacency cannot have loops")
55 
56         direct_check = is_symmetric(adjacency)
57         check_argument(direct_check, "adjacency must be undirected")
58 
59         max_seed = np.iinfo(np.uint32).max
60         if seed is None:
61             seed = np.random.randint(max_seed, dtype=np.int64)
62         seed = check_scalar(
63             seed, "seed", (int, np.integer), min_val=0, max_val=max_seed
64         )
65         self._rng = np.random.default_rng(seed)
66 
67         adjacency = import_graph(adjacency, copy=True)
68 
69         if isinstance(adjacency, csr_matrix):
70             # more efficient for manipulations which change sparsity structure
71             adjacency = lil_matrix(adjacency)
72             self._edge_swap_function = _edge_swap
73         else:
74             # for numpy input, use numba for JIT compilation
75             # NOTE: not convinced numba is helping much here, look into optimizing
76             self._edge_swap_function = nb.jit(_edge_swap)
77 
78         self.adjacency = adjacency
79 
80         edge_list = self._do_setup()
81         check_argument(len(edge_list) >= 2, "there must be at least 2 edges")
82         self.edge_list = edge_list
83 
84     def _do_setup(self) -> np.ndarray:
85         """
86         Computes the edge_list from the adjancency matrix
87 
88         Returns
89         -------
90         edge_list : np.ndarray, shape (n_verts, 2)
91             The corresponding edge_list of adjacency
92         """
93 
94         # get edges for upper triangle of undirected graph
95         row_inds, col_inds = np.nonzero(self.adjacency)
96         upper = row_inds < col_inds
97         row_inds = row_inds[upper]
98         col_inds = col_inds[upper]
99         edge_list = np.stack((row_inds, col_inds)).T
100         return edge_list
101 
102     def swap_edges(self, n_swaps: int = 1) -> Tuple[AdjacencyMatrix, np.ndarray]:
103         """
104         Performs a number of edge swaps on the graph
105 
106         Parameters
107         ----------
108         n_swaps : int (default 1), optional
109             The number of edge swaps to be performed
110 
111         Returns
112         -------
113         adjacency : np.ndarray OR csr.matrix, shape (n_verts, n_verts)
114             The adjancency matrix after a number of edge swaps are performed on the graph
115 
116         edge_list : np.ndarray (n_verts, 2)
117             The edge_list after a number of edge swaps are perfomed on the graph
118         """
119 
120         # Note: for some reason could not get reproducibility w/o setting seed
121         # inside of the _edge_swap_function itself
122         max_seed = np.iinfo(np.int32).max
123         for _ in range(n_swaps):
124             self.adjacency, self.edge_list = self._edge_swap_function(
125                 self.adjacency,
126                 self.edge_list,
127                 seed=self._rng.integers(max_seed),
128             )
129 
130         adjacency = self.adjacency
131         if isinstance(adjacency, lil_matrix):
132             adjacency = csr_matrix(adjacency)
133         else:
134             adjacency = adjacency.copy()
135 
136         return adjacency, self.edge_list.copy()
137 
138 
139 def _edge_swap(
140     adjacency: AdjacencyMatrix, edge_list: np.ndarray, seed: Optional[int] = None
141 ) -> Tuple[AdjacencyMatrix, np.ndarray]:
142     """
143     Performs the edge swap on the adjacency matrix. If adjacency is
144     np.ndarray, then nopython=True is used in numba, but if adjacency
145     is csr_matrix, then forceobj=True is used in numba
146 
147     Parameters
148     ----------
149     adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)
150         The initial adjacency matrix in which edge swaps are performed on it
151 
152     edge_list : np.ndarray, shape (n_verts, 2)
153         The corresponding edge_list of adjacency
154 
155     seed: int, optional
156         Random seed to make outputs reproducible, must be positive
157 
158     Returns
159     -------
160     adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)
161         The adjancency matrix after an edge swap is performed on the graph
162 
163     edge_list : np.ndarray (n_verts, 2)
164         The edge_list after an edge swap is perfomed on the graph
165     """
166 
167     # need to use np.random here instead of the generator for numba compatibility
168     if seed is not None:
169         np.random.seed(seed)
170 
171     # choose two indices at random
172     # NOTE: using np.random here for current numba compatibility
173     orig_inds = np.random.choice(len(edge_list), size=2, replace=False)
174 
175     u, v = edge_list[orig_inds[0]]
176 
177     # two types of swap orientations for undirected graph
178     if np.random.rand() < 0.5:
179         x, y = edge_list[orig_inds[1]]
180     else:
181         y, x = edge_list[orig_inds[1]]
182 
183     # ensures no initial loops
184     if u == v or x == y:
185         return adjacency, edge_list
186 
187     # ensures no loops after swap (must be swap on 4 distinct nodes)
188     if u == x or v == y:
189         return adjacency, edge_list
190 
191     # save edge values
192     w_ux = adjacency[u, x]
193     w_vy = adjacency[v, y]
194 
195     # ensures no multigraphs after swap
196     if w_ux >= 1 or w_vy >= 1:
197         return adjacency, edge_list
198 
199     # perform the swap
200     adjacency[u, v] = 0
201     adjacency[v, u] = 0
202     adjacency[x, y] = 0
203     adjacency[y, x] = 0
204 
205     adjacency[u, x] = 1
206     adjacency[x, u] = 1
207     adjacency[v, y] = 1
208     adjacency[y, v] = 1
209 
210     # update edge list
211     edge_list[orig_inds[0]] = [u, x]
212     edge_list[orig_inds[1]] = [v, y]
213     return adjacency, edge_list
214
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/graspologic/models/edge_swaps.py b/graspologic/models/edge_swaps.py
--- a/graspologic/models/edge_swaps.py
+++ b/graspologic/models/edge_swaps.py
@@ -73,7 +73,7 @@
         else:
             # for numpy input, use numba for JIT compilation
             # NOTE: not convinced numba is helping much here, look into optimizing
-            self._edge_swap_function = nb.jit(_edge_swap)
+            self._edge_swap_function = _edge_swap_numba
 
         self.adjacency = adjacency
 
@@ -211,3 +211,6 @@
     edge_list[orig_inds[0]] = [u, x]
     edge_list[orig_inds[1]] = [v, y]
     return adjacency, edge_list
+
+
+_edge_swap_numba = nb.jit(_edge_swap)

{"golden_diff": "diff --git a/graspologic/models/edge_swaps.py b/graspologic/models/edge_swaps.py\n--- a/graspologic/models/edge_swaps.py\n+++ b/graspologic/models/edge_swaps.py\n@@ -73,7 +73,7 @@\n         else:\n             # for numpy input, use numba for JIT compilation\n             # NOTE: not convinced numba is helping much here, look into optimizing\n-            self._edge_swap_function = nb.jit(_edge_swap)\n+            self._edge_swap_function = _edge_swap_numba\n \n         self.adjacency = adjacency\n \n@@ -211,3 +211,6 @@\n     edge_list[orig_inds[0]] = [u, x]\n     edge_list[orig_inds[1]] = [v, y]\n     return adjacency, edge_list\n+\n+\n+_edge_swap_numba = nb.jit(_edge_swap)\n", "issue": "Fix to repeated compilation of numba\n- [x] Does this PR have a descriptive title that could go in our release notes?\r\n- [ ] Does this PR add any new dependencies?\r\n- [ ] Does this PR modify any existing APIs?\r\n   - [ ] Is the change to the API backwards compatible?\r\n- [ ] Have you built the documentation (reference and/or tutorial) and verified the generated documentation is appropriate?\r\n\r\nFixes #946\r\n\r\nUtilizes the addition of a global function at the top of the file that calls the nb.jit function a single time instead of in the constructor. This speeds up the compilation and running speed of the code.\n", "before_files": [{"content": "from typing import Optional\n\nimport numba as nb\nimport numpy as np\nfrom beartype import beartype\nfrom scipy.sparse import csr_matrix, lil_matrix\nfrom sklearn.utils import check_scalar\n\nfrom graspologic.preconditions import check_argument\nfrom graspologic.types import AdjacencyMatrix, Tuple\nfrom graspologic.utils import import_graph, is_loopless, is_symmetric, is_unweighted\n\n\n# Code based on: https://github.com/joelnish/double-edge-swap-mcmc/blob/master/dbl_edge_mcmc.py\nclass EdgeSwapper:\n    \"\"\"\n    Degree Preserving Edge Swaps\n\n    This class allows for performing degree preserving edge swaps to\n    generate new networks with the same degree sequence as the input network.\n\n    Attributes\n    ----------\n    adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)\n        The initial adjacency matrix to perform edge swaps on. Must be unweighted and undirected.\n\n    edge_list : np.ndarray, shape (n_verts, 2)\n        The corresponding edgelist for the input network\n\n    seed: int, optional\n        Random seed to make outputs reproducible, must be positive\n\n\n    References\n    ----------\n    .. [1] Fosdick, B. K., Larremore, D. B., Nishimura, J., & Ugander, J. (2018).\n           Configuring random graph models with fixed degree sequences.\n           Siam Review, 60(2), 315-355.\n\n    .. [2] Carstens, C. J., & Horadam, K. J. (2017).\n           Switching edges to randomize networks: what goes wrong and how to fix it.\n           Journal of Complex Networks, 5(3), 337-351.\n\n    .. [3] https://github.com/joelnish/double-edge-swap-mcmc/blob/master/dbl_edge_mcmc.py\n    \"\"\"\n\n    @beartype\n    def __init__(self, adjacency: AdjacencyMatrix, seed: Optional[int] = None):\n\n        weight_check = is_unweighted(adjacency)\n        check_argument(weight_check, \"adjacency must be unweighted\")\n\n        loop_check = is_loopless(adjacency)\n        check_argument(loop_check, \"adjacency cannot have loops\")\n\n        direct_check = is_symmetric(adjacency)\n        check_argument(direct_check, \"adjacency must be undirected\")\n\n        max_seed = np.iinfo(np.uint32).max\n        if seed is None:\n            seed = np.random.randint(max_seed, dtype=np.int64)\n        seed = check_scalar(\n            seed, \"seed\", (int, np.integer), min_val=0, max_val=max_seed\n        )\n        self._rng = np.random.default_rng(seed)\n\n        adjacency = import_graph(adjacency, copy=True)\n\n        if isinstance(adjacency, csr_matrix):\n            # more efficient for manipulations which change sparsity structure\n            adjacency = lil_matrix(adjacency)\n            self._edge_swap_function = _edge_swap\n        else:\n            # for numpy input, use numba for JIT compilation\n            # NOTE: not convinced numba is helping much here, look into optimizing\n            self._edge_swap_function = nb.jit(_edge_swap)\n\n        self.adjacency = adjacency\n\n        edge_list = self._do_setup()\n        check_argument(len(edge_list) >= 2, \"there must be at least 2 edges\")\n        self.edge_list = edge_list\n\n    def _do_setup(self) -> np.ndarray:\n        \"\"\"\n        Computes the edge_list from the adjancency matrix\n\n        Returns\n        -------\n        edge_list : np.ndarray, shape (n_verts, 2)\n            The corresponding edge_list of adjacency\n        \"\"\"\n\n        # get edges for upper triangle of undirected graph\n        row_inds, col_inds = np.nonzero(self.adjacency)\n        upper = row_inds < col_inds\n        row_inds = row_inds[upper]\n        col_inds = col_inds[upper]\n        edge_list = np.stack((row_inds, col_inds)).T\n        return edge_list\n\n    def swap_edges(self, n_swaps: int = 1) -> Tuple[AdjacencyMatrix, np.ndarray]:\n        \"\"\"\n        Performs a number of edge swaps on the graph\n\n        Parameters\n        ----------\n        n_swaps : int (default 1), optional\n            The number of edge swaps to be performed\n\n        Returns\n        -------\n        adjacency : np.ndarray OR csr.matrix, shape (n_verts, n_verts)\n            The adjancency matrix after a number of edge swaps are performed on the graph\n\n        edge_list : np.ndarray (n_verts, 2)\n            The edge_list after a number of edge swaps are perfomed on the graph\n        \"\"\"\n\n        # Note: for some reason could not get reproducibility w/o setting seed\n        # inside of the _edge_swap_function itself\n        max_seed = np.iinfo(np.int32).max\n        for _ in range(n_swaps):\n            self.adjacency, self.edge_list = self._edge_swap_function(\n                self.adjacency,\n                self.edge_list,\n                seed=self._rng.integers(max_seed),\n            )\n\n        adjacency = self.adjacency\n        if isinstance(adjacency, lil_matrix):\n            adjacency = csr_matrix(adjacency)\n        else:\n            adjacency = adjacency.copy()\n\n        return adjacency, self.edge_list.copy()\n\n\ndef _edge_swap(\n    adjacency: AdjacencyMatrix, edge_list: np.ndarray, seed: Optional[int] = None\n) -> Tuple[AdjacencyMatrix, np.ndarray]:\n    \"\"\"\n    Performs the edge swap on the adjacency matrix. If adjacency is\n    np.ndarray, then nopython=True is used in numba, but if adjacency\n    is csr_matrix, then forceobj=True is used in numba\n\n    Parameters\n    ----------\n    adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)\n        The initial adjacency matrix in which edge swaps are performed on it\n\n    edge_list : np.ndarray, shape (n_verts, 2)\n        The corresponding edge_list of adjacency\n\n    seed: int, optional\n        Random seed to make outputs reproducible, must be positive\n\n    Returns\n    -------\n    adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)\n        The adjancency matrix after an edge swap is performed on the graph\n\n    edge_list : np.ndarray (n_verts, 2)\n        The edge_list after an edge swap is perfomed on the graph\n    \"\"\"\n\n    # need to use np.random here instead of the generator for numba compatibility\n    if seed is not None:\n        np.random.seed(seed)\n\n    # choose two indices at random\n    # NOTE: using np.random here for current numba compatibility\n    orig_inds = np.random.choice(len(edge_list), size=2, replace=False)\n\n    u, v = edge_list[orig_inds[0]]\n\n    # two types of swap orientations for undirected graph\n    if np.random.rand() < 0.5:\n        x, y = edge_list[orig_inds[1]]\n    else:\n        y, x = edge_list[orig_inds[1]]\n\n    # ensures no initial loops\n    if u == v or x == y:\n        return adjacency, edge_list\n\n    # ensures no loops after swap (must be swap on 4 distinct nodes)\n    if u == x or v == y:\n        return adjacency, edge_list\n\n    # save edge values\n    w_ux = adjacency[u, x]\n    w_vy = adjacency[v, y]\n\n    # ensures no multigraphs after swap\n    if w_ux >= 1 or w_vy >= 1:\n        return adjacency, edge_list\n\n    # perform the swap\n    adjacency[u, v] = 0\n    adjacency[v, u] = 0\n    adjacency[x, y] = 0\n    adjacency[y, x] = 0\n\n    adjacency[u, x] = 1\n    adjacency[x, u] = 1\n    adjacency[v, y] = 1\n    adjacency[y, v] = 1\n\n    # update edge list\n    edge_list[orig_inds[0]] = [u, x]\n    edge_list[orig_inds[1]] = [v, y]\n    return adjacency, edge_list\n", "path": "graspologic/models/edge_swaps.py"}], "after_files": [{"content": "from typing import Optional\n\nimport numba as nb\nimport numpy as np\nfrom beartype import beartype\nfrom scipy.sparse import csr_matrix, lil_matrix\nfrom sklearn.utils import check_scalar\n\nfrom graspologic.preconditions import check_argument\nfrom graspologic.types import AdjacencyMatrix, Tuple\nfrom graspologic.utils import import_graph, is_loopless, is_symmetric, is_unweighted\n\n\n# Code based on: https://github.com/joelnish/double-edge-swap-mcmc/blob/master/dbl_edge_mcmc.py\nclass EdgeSwapper:\n    \"\"\"\n    Degree Preserving Edge Swaps\n\n    This class allows for performing degree preserving edge swaps to\n    generate new networks with the same degree sequence as the input network.\n\n    Attributes\n    ----------\n    adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)\n        The initial adjacency matrix to perform edge swaps on. Must be unweighted and undirected.\n\n    edge_list : np.ndarray, shape (n_verts, 2)\n        The corresponding edgelist for the input network\n\n    seed: int, optional\n        Random seed to make outputs reproducible, must be positive\n\n\n    References\n    ----------\n    .. [1] Fosdick, B. K., Larremore, D. B., Nishimura, J., & Ugander, J. (2018).\n           Configuring random graph models with fixed degree sequences.\n           Siam Review, 60(2), 315-355.\n\n    .. [2] Carstens, C. J., & Horadam, K. J. (2017).\n           Switching edges to randomize networks: what goes wrong and how to fix it.\n           Journal of Complex Networks, 5(3), 337-351.\n\n    .. [3] https://github.com/joelnish/double-edge-swap-mcmc/blob/master/dbl_edge_mcmc.py\n    \"\"\"\n\n    @beartype\n    def __init__(self, adjacency: AdjacencyMatrix, seed: Optional[int] = None):\n\n        weight_check = is_unweighted(adjacency)\n        check_argument(weight_check, \"adjacency must be unweighted\")\n\n        loop_check = is_loopless(adjacency)\n        check_argument(loop_check, \"adjacency cannot have loops\")\n\n        direct_check = is_symmetric(adjacency)\n        check_argument(direct_check, \"adjacency must be undirected\")\n\n        max_seed = np.iinfo(np.uint32).max\n        if seed is None:\n            seed = np.random.randint(max_seed, dtype=np.int64)\n        seed = check_scalar(\n            seed, \"seed\", (int, np.integer), min_val=0, max_val=max_seed\n        )\n        self._rng = np.random.default_rng(seed)\n\n        adjacency = import_graph(adjacency, copy=True)\n\n        if isinstance(adjacency, csr_matrix):\n            # more efficient for manipulations which change sparsity structure\n            adjacency = lil_matrix(adjacency)\n            self._edge_swap_function = _edge_swap\n        else:\n            # for numpy input, use numba for JIT compilation\n            # NOTE: not convinced numba is helping much here, look into optimizing\n            self._edge_swap_function = _edge_swap_numba\n\n        self.adjacency = adjacency\n\n        edge_list = self._do_setup()\n        check_argument(len(edge_list) >= 2, \"there must be at least 2 edges\")\n        self.edge_list = edge_list\n\n    def _do_setup(self) -> np.ndarray:\n        \"\"\"\n        Computes the edge_list from the adjancency matrix\n\n        Returns\n        -------\n        edge_list : np.ndarray, shape (n_verts, 2)\n            The corresponding edge_list of adjacency\n        \"\"\"\n\n        # get edges for upper triangle of undirected graph\n        row_inds, col_inds = np.nonzero(self.adjacency)\n        upper = row_inds < col_inds\n        row_inds = row_inds[upper]\n        col_inds = col_inds[upper]\n        edge_list = np.stack((row_inds, col_inds)).T\n        return edge_list\n\n    def swap_edges(self, n_swaps: int = 1) -> Tuple[AdjacencyMatrix, np.ndarray]:\n        \"\"\"\n        Performs a number of edge swaps on the graph\n\n        Parameters\n        ----------\n        n_swaps : int (default 1), optional\n            The number of edge swaps to be performed\n\n        Returns\n        -------\n        adjacency : np.ndarray OR csr.matrix, shape (n_verts, n_verts)\n            The adjancency matrix after a number of edge swaps are performed on the graph\n\n        edge_list : np.ndarray (n_verts, 2)\n            The edge_list after a number of edge swaps are perfomed on the graph\n        \"\"\"\n\n        # Note: for some reason could not get reproducibility w/o setting seed\n        # inside of the _edge_swap_function itself\n        max_seed = np.iinfo(np.int32).max\n        for _ in range(n_swaps):\n            self.adjacency, self.edge_list = self._edge_swap_function(\n                self.adjacency,\n                self.edge_list,\n                seed=self._rng.integers(max_seed),\n            )\n\n        adjacency = self.adjacency\n        if isinstance(adjacency, lil_matrix):\n            adjacency = csr_matrix(adjacency)\n        else:\n            adjacency = adjacency.copy()\n\n        return adjacency, self.edge_list.copy()\n\n\ndef _edge_swap(\n    adjacency: AdjacencyMatrix, edge_list: np.ndarray, seed: Optional[int] = None\n) -> Tuple[AdjacencyMatrix, np.ndarray]:\n    \"\"\"\n    Performs the edge swap on the adjacency matrix. If adjacency is\n    np.ndarray, then nopython=True is used in numba, but if adjacency\n    is csr_matrix, then forceobj=True is used in numba\n\n    Parameters\n    ----------\n    adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)\n        The initial adjacency matrix in which edge swaps are performed on it\n\n    edge_list : np.ndarray, shape (n_verts, 2)\n        The corresponding edge_list of adjacency\n\n    seed: int, optional\n        Random seed to make outputs reproducible, must be positive\n\n    Returns\n    -------\n    adjacency : np.ndarray OR csr_matrix, shape (n_verts, n_verts)\n        The adjancency matrix after an edge swap is performed on the graph\n\n    edge_list : np.ndarray (n_verts, 2)\n        The edge_list after an edge swap is perfomed on the graph\n    \"\"\"\n\n    # need to use np.random here instead of the generator for numba compatibility\n    if seed is not None:\n        np.random.seed(seed)\n\n    # choose two indices at random\n    # NOTE: using np.random here for current numba compatibility\n    orig_inds = np.random.choice(len(edge_list), size=2, replace=False)\n\n    u, v = edge_list[orig_inds[0]]\n\n    # two types of swap orientations for undirected graph\n    if np.random.rand() < 0.5:\n        x, y = edge_list[orig_inds[1]]\n    else:\n        y, x = edge_list[orig_inds[1]]\n\n    # ensures no initial loops\n    if u == v or x == y:\n        return adjacency, edge_list\n\n    # ensures no loops after swap (must be swap on 4 distinct nodes)\n    if u == x or v == y:\n        return adjacency, edge_list\n\n    # save edge values\n    w_ux = adjacency[u, x]\n    w_vy = adjacency[v, y]\n\n    # ensures no multigraphs after swap\n    if w_ux >= 1 or w_vy >= 1:\n        return adjacency, edge_list\n\n    # perform the swap\n    adjacency[u, v] = 0\n    adjacency[v, u] = 0\n    adjacency[x, y] = 0\n    adjacency[y, x] = 0\n\n    adjacency[u, x] = 1\n    adjacency[x, u] = 1\n    adjacency[v, y] = 1\n    adjacency[y, v] = 1\n\n    # update edge list\n    edge_list[orig_inds[0]] = [u, x]\n    edge_list[orig_inds[1]] = [v, y]\n    return adjacency, edge_list\n\n\n_edge_swap_numba = nb.jit(_edge_swap)\n", "path": "graspologic/models/edge_swaps.py"}]}