We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] mug2vec assumes undirected graphs but doesn't check for this
## Expected Behavior
Should run on a set of directed graphs

## Actual Behavior
Breaks on this line 
https://github.com/microsoft/graspologic/blob/2125f27bc3f2739f4f2c784d5b700417df63c5d7/graspologic/embed/mug2vec.py#L141
since `omni_embedding` is a tuple 


--- 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/embed/mug2vec.py`
Content:
```
1 # Copyright (c) Microsoft Corporation and contributors.
2 # Licensed under the MIT License.
3 
4 from typing import Any, Optional
5 
6 import numpy as np
7 from sklearn.base import BaseEstimator
8 from typing_extensions import Literal
9 
10 from graspologic.types import List
11 
12 from ..types import GraphRepresentation
13 from ..utils import pass_to_ranks
14 from .mds import ClassicalMDS
15 from .omni import OmnibusEmbed
16 
17 
18 class mug2vec(BaseEstimator):
19     r"""
20     Multigraphs-2-vectors (mug2vec).
21 
22     mug2vec is a sequence of three algorithms that learns a feature vector for each
23     input graph.
24 
25     Steps:
26 
27     1. Pass to ranks - ranks all edge weights from smallest to largest valued edges
28     then normalize by a constant.
29 
30     2. Omnibus embedding - jointly learns a low dimensional matrix representation for
31     all graphs under the random dot product model (RDPG).
32 
33     3. Classical MDS (cMDS) - learns a feature vector for each graph by computing
34     Euclidean distance between each pair of graph embeddings from omnibus embedding,
35     followed by an eigen decomposition.
36 
37     Parameters
38     ----------
39     pass_to_ranks: {'simple-nonzero' (default), 'simple-all', 'zero-boost'} string, or None
40 
41         - 'simple-nonzero'
42             assigns ranks to all non-zero edges, settling ties using
43             the average. Ranks are then scaled by
44             :math:`\frac{rank(\text{non-zero edges})}{\text{total non-zero edges} + 1}`
45         - 'simple-all'
46             assigns ranks to all non-zero edges, settling ties using
47             the average. Ranks are then scaled by
48             :math:`\frac{rank(\text{non-zero edges})}{n^2 + 1}`
49             where n is the number of nodes
50         - 'zero-boost'
51             preserves the edge weight for all 0s, but ranks the other
52             edges as if the ranks of all 0 edges has been assigned. If there are
53             10 0-valued edges, the lowest non-zero edge gets weight 11 / (number
54             of possible edges). Ties settled by the average of the weight that those
55             edges would have received. Number of possible edges is determined
56             by the type of graph (loopless or looped, directed or undirected).
57         - None
58             No pass to ranks applied.
59 
60     omnibus_components, cmds_components : int or None, default = None
61         Desired dimensionality of output data. If "full",
62         ``n_components`` must be ``<= min(X.shape)``. Otherwise, ``n_components`` must be
63         ``< min(X.shape)``. If None, then optimal dimensions will be chosen by
64         :func:`~graspologic.embed.select_dimension` using ``n_elbows`` argument.
65 
66     omnibus_n_elbows, cmds_n_elbows: int, optional, default: 2
67         If ``n_components`` is None, then compute the optimal embedding dimension using
68         :func:`~graspologic.embed.select_dimension`. Otherwise, ignored.
69 
70     svd_seed : int or None (default ``None``)
71         Allows you to seed the randomized svd solver used in the Omnibus embedding
72         for deterministic, albeit pseudo-randomized behavior.
73 
74     Attributes
75     ----------
76     omnibus_n_components_ : int
77         Equals the parameter ``n_components``. If input ``n_components`` was None,
78         then equals the optimal embedding dimension.
79 
80     cmds_n_components_ : int
81         Equals the parameter ``n_components``. If input ``n_components`` was None,
82         then equals the optimal embedding dimension.
83 
84     embeddings_ : array, shape (n_components, n_features)
85         Embeddings from the pipeline. Each graph is a point in ``n_features``
86         dimensions.
87 
88     See also
89     --------
90     graspologic.utils.pass_to_ranks
91     graspologic.embed.OmnibusEmbed
92     graspologic.embed.ClassicalMDS
93     graspologic.embed.select_dimension
94     """
95 
96     def __init__(
97         self,
98         pass_to_ranks: Literal[
99             "simple-nonzero", "simple-all", "zero-boost"
100         ] = "simple-nonzero",
101         omnibus_components: Optional[int] = None,
102         omnibus_n_elbows: int = 2,
103         cmds_components: Optional[int] = None,
104         cmds_n_elbows: int = 2,
105         svd_seed: Optional[int] = None,
106     ) -> None:
107         self.pass_to_ranks = pass_to_ranks
108         self.omnibus_components = omnibus_components
109         self.omnibus_n_elbows = omnibus_n_elbows
110         self.cmds_components = cmds_components
111         self.cmds_n_elbows = cmds_n_elbows
112         self.svd_seed = svd_seed
113 
114     def _check_inputs(self) -> None:
115         variables = self.get_params()
116         variables.pop("pass_to_ranks")
117 
118         for name, val in variables.items():
119             if val is not None:
120                 if not isinstance(val, int):
121                     msg = "{} must be an int or None.".format(name)
122                     raise ValueError(msg)
123                 elif val <= 0:
124                     msg = "{} must be > 0.".format(name)
125                     raise ValueError(msg)
126 
127     def fit(
128         self, graphs: List[GraphRepresentation], y: Optional[Any] = None
129     ) -> "mug2vec":
130         """
131         Computes a vector for each graph.
132 
133         Parameters
134         ----------
135         graphs : list of nx.Graph or ndarray, or ndarray
136             If list of nx.Graph, each Graph must contain same number of nodes.
137             If list of ndarray, each array must have shape (n_vertices, n_vertices).
138             If ndarray, then array must have shape (n_graphs, n_vertices, n_vertices).
139 
140         y : Ignored
141 
142         Returns
143         -------
144         self : returns an instance of self.
145         """
146         # Check these prior to PTR just in case
147         self._check_inputs()
148 
149         if self.pass_to_ranks is not None:
150             graphs = [pass_to_ranks(g, self.pass_to_ranks) for g in graphs]
151 
152         omni = OmnibusEmbed(
153             n_components=self.omnibus_components,
154             n_elbows=self.omnibus_n_elbows,
155             svd_seed=self.svd_seed,
156         )
157         omnibus_embedding = omni.fit_transform(graphs)
158 
159         self.omnibus_n_components_ = omnibus_embedding.shape[-1]
160 
161         cmds = ClassicalMDS(
162             n_components=self.cmds_components,
163             n_elbows=self.cmds_n_elbows,
164             svd_seed=self.svd_seed,
165         )
166         self.embeddings_: np.ndarray = cmds.fit_transform(omnibus_embedding)
167         self.cmds_components_ = self.embeddings_.shape[-1]
168 
169         return self
170 
171     def fit_transform(
172         self, graphs: List[GraphRepresentation], y: Optional[Any] = None
173     ) -> np.ndarray:
174         """
175         Computes a vector for each graph.
176 
177         Parameters
178         ----------
179         graphs : list of nx.Graph or ndarray, or ndarray
180             If list of nx.Graph, each Graph must contain same number of nodes.
181             If list of ndarray, each array must have shape (n_vertices, n_vertices).
182             If ndarray, then array must have shape (n_graphs, n_vertices, n_vertices).
183 
184         y : Ignored
185 
186         Returns
187         -------
188         embeddings : embeddings generated by fit.
189         """
190         self.fit(graphs)
191 
192         return self.embeddings_
193
```
--- 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/embed/mug2vec.py b/graspologic/embed/mug2vec.py
--- a/graspologic/embed/mug2vec.py
+++ b/graspologic/embed/mug2vec.py
@@ -153,10 +153,11 @@
             n_components=self.omnibus_components,
             n_elbows=self.omnibus_n_elbows,
             svd_seed=self.svd_seed,
+            concat=True,
         )
         omnibus_embedding = omni.fit_transform(graphs)
 
-        self.omnibus_n_components_ = omnibus_embedding.shape[-1]
+        self.omnibus_n_components_ = len(omni.singular_values_)
 
         cmds = ClassicalMDS(
             n_components=self.cmds_components,

{"golden_diff": "diff --git a/graspologic/embed/mug2vec.py b/graspologic/embed/mug2vec.py\n--- a/graspologic/embed/mug2vec.py\n+++ b/graspologic/embed/mug2vec.py\n@@ -153,10 +153,11 @@\n             n_components=self.omnibus_components,\n             n_elbows=self.omnibus_n_elbows,\n             svd_seed=self.svd_seed,\n+            concat=True,\n         )\n         omnibus_embedding = omni.fit_transform(graphs)\n \n-        self.omnibus_n_components_ = omnibus_embedding.shape[-1]\n+        self.omnibus_n_components_ = len(omni.singular_values_)\n \n         cmds = ClassicalMDS(\n             n_components=self.cmds_components,\n", "issue": "[BUG] mug2vec assumes undirected graphs but doesn't check for this\n## Expected Behavior\r\nShould run on a set of directed graphs\r\n\r\n## Actual Behavior\r\nBreaks on this line \r\nhttps://github.com/microsoft/graspologic/blob/2125f27bc3f2739f4f2c784d5b700417df63c5d7/graspologic/embed/mug2vec.py#L141\r\nsince `omni_embedding` is a tuple \r\n\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation and contributors.\n# Licensed under the MIT License.\n\nfrom typing import Any, Optional\n\nimport numpy as np\nfrom sklearn.base import BaseEstimator\nfrom typing_extensions import Literal\n\nfrom graspologic.types import List\n\nfrom ..types import GraphRepresentation\nfrom ..utils import pass_to_ranks\nfrom .mds import ClassicalMDS\nfrom .omni import OmnibusEmbed\n\n\nclass mug2vec(BaseEstimator):\n    r\"\"\"\n    Multigraphs-2-vectors (mug2vec).\n\n    mug2vec is a sequence of three algorithms that learns a feature vector for each\n    input graph.\n\n    Steps:\n\n    1. Pass to ranks - ranks all edge weights from smallest to largest valued edges\n    then normalize by a constant.\n\n    2. Omnibus embedding - jointly learns a low dimensional matrix representation for\n    all graphs under the random dot product model (RDPG).\n\n    3. Classical MDS (cMDS) - learns a feature vector for each graph by computing\n    Euclidean distance between each pair of graph embeddings from omnibus embedding,\n    followed by an eigen decomposition.\n\n    Parameters\n    ----------\n    pass_to_ranks: {'simple-nonzero' (default), 'simple-all', 'zero-boost'} string, or None\n\n        - 'simple-nonzero'\n            assigns ranks to all non-zero edges, settling ties using\n            the average. Ranks are then scaled by\n            :math:`\\frac{rank(\\text{non-zero edges})}{\\text{total non-zero edges} + 1}`\n        - 'simple-all'\n            assigns ranks to all non-zero edges, settling ties using\n            the average. Ranks are then scaled by\n            :math:`\\frac{rank(\\text{non-zero edges})}{n^2 + 1}`\n            where n is the number of nodes\n        - 'zero-boost'\n            preserves the edge weight for all 0s, but ranks the other\n            edges as if the ranks of all 0 edges has been assigned. If there are\n            10 0-valued edges, the lowest non-zero edge gets weight 11 / (number\n            of possible edges). Ties settled by the average of the weight that those\n            edges would have received. Number of possible edges is determined\n            by the type of graph (loopless or looped, directed or undirected).\n        - None\n            No pass to ranks applied.\n\n    omnibus_components, cmds_components : int or None, default = None\n        Desired dimensionality of output data. If \"full\",\n        ``n_components`` must be ``<= min(X.shape)``. Otherwise, ``n_components`` must be\n        ``< min(X.shape)``. If None, then optimal dimensions will be chosen by\n        :func:`~graspologic.embed.select_dimension` using ``n_elbows`` argument.\n\n    omnibus_n_elbows, cmds_n_elbows: int, optional, default: 2\n        If ``n_components`` is None, then compute the optimal embedding dimension using\n        :func:`~graspologic.embed.select_dimension`. Otherwise, ignored.\n\n    svd_seed : int or None (default ``None``)\n        Allows you to seed the randomized svd solver used in the Omnibus embedding\n        for deterministic, albeit pseudo-randomized behavior.\n\n    Attributes\n    ----------\n    omnibus_n_components_ : int\n        Equals the parameter ``n_components``. If input ``n_components`` was None,\n        then equals the optimal embedding dimension.\n\n    cmds_n_components_ : int\n        Equals the parameter ``n_components``. If input ``n_components`` was None,\n        then equals the optimal embedding dimension.\n\n    embeddings_ : array, shape (n_components, n_features)\n        Embeddings from the pipeline. Each graph is a point in ``n_features``\n        dimensions.\n\n    See also\n    --------\n    graspologic.utils.pass_to_ranks\n    graspologic.embed.OmnibusEmbed\n    graspologic.embed.ClassicalMDS\n    graspologic.embed.select_dimension\n    \"\"\"\n\n    def __init__(\n        self,\n        pass_to_ranks: Literal[\n            \"simple-nonzero\", \"simple-all\", \"zero-boost\"\n        ] = \"simple-nonzero\",\n        omnibus_components: Optional[int] = None,\n        omnibus_n_elbows: int = 2,\n        cmds_components: Optional[int] = None,\n        cmds_n_elbows: int = 2,\n        svd_seed: Optional[int] = None,\n    ) -> None:\n        self.pass_to_ranks = pass_to_ranks\n        self.omnibus_components = omnibus_components\n        self.omnibus_n_elbows = omnibus_n_elbows\n        self.cmds_components = cmds_components\n        self.cmds_n_elbows = cmds_n_elbows\n        self.svd_seed = svd_seed\n\n    def _check_inputs(self) -> None:\n        variables = self.get_params()\n        variables.pop(\"pass_to_ranks\")\n\n        for name, val in variables.items():\n            if val is not None:\n                if not isinstance(val, int):\n                    msg = \"{} must be an int or None.\".format(name)\n                    raise ValueError(msg)\n                elif val <= 0:\n                    msg = \"{} must be > 0.\".format(name)\n                    raise ValueError(msg)\n\n    def fit(\n        self, graphs: List[GraphRepresentation], y: Optional[Any] = None\n    ) -> \"mug2vec\":\n        \"\"\"\n        Computes a vector for each graph.\n\n        Parameters\n        ----------\n        graphs : list of nx.Graph or ndarray, or ndarray\n            If list of nx.Graph, each Graph must contain same number of nodes.\n            If list of ndarray, each array must have shape (n_vertices, n_vertices).\n            If ndarray, then array must have shape (n_graphs, n_vertices, n_vertices).\n\n        y : Ignored\n\n        Returns\n        -------\n        self : returns an instance of self.\n        \"\"\"\n        # Check these prior to PTR just in case\n        self._check_inputs()\n\n        if self.pass_to_ranks is not None:\n            graphs = [pass_to_ranks(g, self.pass_to_ranks) for g in graphs]\n\n        omni = OmnibusEmbed(\n            n_components=self.omnibus_components,\n            n_elbows=self.omnibus_n_elbows,\n            svd_seed=self.svd_seed,\n        )\n        omnibus_embedding = omni.fit_transform(graphs)\n\n        self.omnibus_n_components_ = omnibus_embedding.shape[-1]\n\n        cmds = ClassicalMDS(\n            n_components=self.cmds_components,\n            n_elbows=self.cmds_n_elbows,\n            svd_seed=self.svd_seed,\n        )\n        self.embeddings_: np.ndarray = cmds.fit_transform(omnibus_embedding)\n        self.cmds_components_ = self.embeddings_.shape[-1]\n\n        return self\n\n    def fit_transform(\n        self, graphs: List[GraphRepresentation], y: Optional[Any] = None\n    ) -> np.ndarray:\n        \"\"\"\n        Computes a vector for each graph.\n\n        Parameters\n        ----------\n        graphs : list of nx.Graph or ndarray, or ndarray\n            If list of nx.Graph, each Graph must contain same number of nodes.\n            If list of ndarray, each array must have shape (n_vertices, n_vertices).\n            If ndarray, then array must have shape (n_graphs, n_vertices, n_vertices).\n\n        y : Ignored\n\n        Returns\n        -------\n        embeddings : embeddings generated by fit.\n        \"\"\"\n        self.fit(graphs)\n\n        return self.embeddings_\n", "path": "graspologic/embed/mug2vec.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation and contributors.\n# Licensed under the MIT License.\n\nfrom typing import Any, Optional\n\nimport numpy as np\nfrom sklearn.base import BaseEstimator\nfrom typing_extensions import Literal\n\nfrom graspologic.types import List\n\nfrom ..types import GraphRepresentation\nfrom ..utils import pass_to_ranks\nfrom .mds import ClassicalMDS\nfrom .omni import OmnibusEmbed\n\n\nclass mug2vec(BaseEstimator):\n    r\"\"\"\n    Multigraphs-2-vectors (mug2vec).\n\n    mug2vec is a sequence of three algorithms that learns a feature vector for each\n    input graph.\n\n    Steps:\n\n    1. Pass to ranks - ranks all edge weights from smallest to largest valued edges\n    then normalize by a constant.\n\n    2. Omnibus embedding - jointly learns a low dimensional matrix representation for\n    all graphs under the random dot product model (RDPG).\n\n    3. Classical MDS (cMDS) - learns a feature vector for each graph by computing\n    Euclidean distance between each pair of graph embeddings from omnibus embedding,\n    followed by an eigen decomposition.\n\n    Parameters\n    ----------\n    pass_to_ranks: {'simple-nonzero' (default), 'simple-all', 'zero-boost'} string, or None\n\n        - 'simple-nonzero'\n            assigns ranks to all non-zero edges, settling ties using\n            the average. Ranks are then scaled by\n            :math:`\\frac{rank(\\text{non-zero edges})}{\\text{total non-zero edges} + 1}`\n        - 'simple-all'\n            assigns ranks to all non-zero edges, settling ties using\n            the average. Ranks are then scaled by\n            :math:`\\frac{rank(\\text{non-zero edges})}{n^2 + 1}`\n            where n is the number of nodes\n        - 'zero-boost'\n            preserves the edge weight for all 0s, but ranks the other\n            edges as if the ranks of all 0 edges has been assigned. If there are\n            10 0-valued edges, the lowest non-zero edge gets weight 11 / (number\n            of possible edges). Ties settled by the average of the weight that those\n            edges would have received. Number of possible edges is determined\n            by the type of graph (loopless or looped, directed or undirected).\n        - None\n            No pass to ranks applied.\n\n    omnibus_components, cmds_components : int or None, default = None\n        Desired dimensionality of output data. If \"full\",\n        ``n_components`` must be ``<= min(X.shape)``. Otherwise, ``n_components`` must be\n        ``< min(X.shape)``. If None, then optimal dimensions will be chosen by\n        :func:`~graspologic.embed.select_dimension` using ``n_elbows`` argument.\n\n    omnibus_n_elbows, cmds_n_elbows: int, optional, default: 2\n        If ``n_components`` is None, then compute the optimal embedding dimension using\n        :func:`~graspologic.embed.select_dimension`. Otherwise, ignored.\n\n    svd_seed : int or None (default ``None``)\n        Allows you to seed the randomized svd solver used in the Omnibus embedding\n        for deterministic, albeit pseudo-randomized behavior.\n\n    Attributes\n    ----------\n    omnibus_n_components_ : int\n        Equals the parameter ``n_components``. If input ``n_components`` was None,\n        then equals the optimal embedding dimension.\n\n    cmds_n_components_ : int\n        Equals the parameter ``n_components``. If input ``n_components`` was None,\n        then equals the optimal embedding dimension.\n\n    embeddings_ : array, shape (n_components, n_features)\n        Embeddings from the pipeline. Each graph is a point in ``n_features``\n        dimensions.\n\n    See also\n    --------\n    graspologic.utils.pass_to_ranks\n    graspologic.embed.OmnibusEmbed\n    graspologic.embed.ClassicalMDS\n    graspologic.embed.select_dimension\n    \"\"\"\n\n    def __init__(\n        self,\n        pass_to_ranks: Literal[\n            \"simple-nonzero\", \"simple-all\", \"zero-boost\"\n        ] = \"simple-nonzero\",\n        omnibus_components: Optional[int] = None,\n        omnibus_n_elbows: int = 2,\n        cmds_components: Optional[int] = None,\n        cmds_n_elbows: int = 2,\n        svd_seed: Optional[int] = None,\n    ) -> None:\n        self.pass_to_ranks = pass_to_ranks\n        self.omnibus_components = omnibus_components\n        self.omnibus_n_elbows = omnibus_n_elbows\n        self.cmds_components = cmds_components\n        self.cmds_n_elbows = cmds_n_elbows\n        self.svd_seed = svd_seed\n\n    def _check_inputs(self) -> None:\n        variables = self.get_params()\n        variables.pop(\"pass_to_ranks\")\n\n        for name, val in variables.items():\n            if val is not None:\n                if not isinstance(val, int):\n                    msg = \"{} must be an int or None.\".format(name)\n                    raise ValueError(msg)\n                elif val <= 0:\n                    msg = \"{} must be > 0.\".format(name)\n                    raise ValueError(msg)\n\n    def fit(\n        self, graphs: List[GraphRepresentation], y: Optional[Any] = None\n    ) -> \"mug2vec\":\n        \"\"\"\n        Computes a vector for each graph.\n\n        Parameters\n        ----------\n        graphs : list of nx.Graph or ndarray, or ndarray\n            If list of nx.Graph, each Graph must contain same number of nodes.\n            If list of ndarray, each array must have shape (n_vertices, n_vertices).\n            If ndarray, then array must have shape (n_graphs, n_vertices, n_vertices).\n\n        y : Ignored\n\n        Returns\n        -------\n        self : returns an instance of self.\n        \"\"\"\n        # Check these prior to PTR just in case\n        self._check_inputs()\n\n        if self.pass_to_ranks is not None:\n            graphs = [pass_to_ranks(g, self.pass_to_ranks) for g in graphs]\n\n        omni = OmnibusEmbed(\n            n_components=self.omnibus_components,\n            n_elbows=self.omnibus_n_elbows,\n            svd_seed=self.svd_seed,\n            concat=True,\n        )\n        omnibus_embedding = omni.fit_transform(graphs)\n\n        self.omnibus_n_components_ = len(omni.singular_values_)\n\n        cmds = ClassicalMDS(\n            n_components=self.cmds_components,\n            n_elbows=self.cmds_n_elbows,\n            svd_seed=self.svd_seed,\n        )\n        self.embeddings_: np.ndarray = cmds.fit_transform(omnibus_embedding)\n        self.cmds_components_ = self.embeddings_.shape[-1]\n\n        return self\n\n    def fit_transform(\n        self, graphs: List[GraphRepresentation], y: Optional[Any] = None\n    ) -> np.ndarray:\n        \"\"\"\n        Computes a vector for each graph.\n\n        Parameters\n        ----------\n        graphs : list of nx.Graph or ndarray, or ndarray\n            If list of nx.Graph, each Graph must contain same number of nodes.\n            If list of ndarray, each array must have shape (n_vertices, n_vertices).\n            If ndarray, then array must have shape (n_graphs, n_vertices, n_vertices).\n\n        y : Ignored\n\n        Returns\n        -------\n        embeddings : embeddings generated by fit.\n        \"\"\"\n        self.fit(graphs)\n\n        return self.embeddings_\n", "path": "graspologic/embed/mug2vec.py"}]}