We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Don't trigger axes computation when doing binary operations
When we do a binary operation and return a Series object, we don't need to trigger axes computation in columnarize.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `modin/core/dataframe/algebra/binary.py`
Content:
```
1 # Licensed to Modin Development Team under one or more contributor license agreements.
2 # See the NOTICE file distributed with this work for additional information regarding
3 # copyright ownership.  The Modin Development Team licenses this file to you under the
4 # Apache License, Version 2.0 (the "License"); you may not use this file except in
5 # compliance with the License.  You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software distributed under
10 # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 # ANY KIND, either express or implied. See the License for the specific language
12 # governing permissions and limitations under the License.
13 
14 """Module houses builder class for Binary operator."""
15 
16 import numpy as np
17 import pandas
18 
19 from .operator import Operator
20 
21 
22 def coerce_int_to_float64(dtype: np.dtype) -> np.dtype:
23     """
24     Coerce dtype to float64 if it is a variant of integer.
25 
26     If dtype is integer, function returns float64 datatype.
27     If not, returns the datatype argument itself.
28 
29     Parameters
30     ----------
31     dtype : np.dtype
32         NumPy datatype.
33 
34     Returns
35     -------
36     dtype : np.dtype
37         Returns float64 for all int datatypes or returns the datatype itself
38         for other types.
39 
40     Notes
41     -----
42     Used to precompute datatype in case of division in pandas.
43     """
44     if dtype in np.sctypes["int"] + np.sctypes["uint"]:
45         return np.dtype(np.float64)
46     else:
47         return dtype
48 
49 
50 def compute_dtypes_common_cast(first, second) -> np.dtype:
51     """
52     Precompute data types for binary operations by finding common type between operands.
53 
54     Parameters
55     ----------
56     first : PandasQueryCompiler
57         First operand for which the binary operation would be performed later.
58     second : PandasQueryCompiler
59         Second operand for which the binary operation would be performed later.
60 
61     Returns
62     -------
63     dtypes
64         The pandas series with precomputed dtypes.
65 
66     Notes
67     -----
68     The dtypes of the operands are supposed to be known.
69     """
70     dtypes_first = first._modin_frame._dtypes.to_dict()
71     dtypes_second = second._modin_frame._dtypes.to_dict()
72     columns_first = set(first.columns)
73     columns_second = set(second.columns)
74     common_columns = columns_first.intersection(columns_second)
75     mismatch_columns = columns_first.union(columns_second) - common_columns
76     # If at least one column doesn't match, the result of the non matching column would be nan.
77     nan_dtype = np.dtype(type(np.nan))
78     dtypes = pandas.Series(
79         [
80             pandas.core.dtypes.cast.find_common_type(
81                 [
82                     dtypes_first[x],
83                     dtypes_second[x],
84                 ]
85             )
86             for x in common_columns
87         ],
88         index=common_columns,
89     )
90     dtypes = pandas.concat(
91         [
92             dtypes,
93             pandas.Series(
94                 [nan_dtype] * (len(mismatch_columns)),
95                 index=mismatch_columns,
96             ),
97         ]
98     )
99     dtypes = dtypes.sort_index()
100     return dtypes
101 
102 
103 def compute_dtypes_boolean(first, second) -> np.dtype:
104     """
105     Precompute data types for boolean operations.
106 
107     Parameters
108     ----------
109     first : PandasQueryCompiler
110         First operand for which the binary operation would be performed later.
111     second : PandasQueryCompiler
112         Second operand for which the binary operation would be performed later.
113 
114     Returns
115     -------
116     dtypes
117         The pandas series with precomputed dtypes.
118 
119     Notes
120     -----
121     Finds a union of columns and finds dtypes for all these columns.
122     """
123     columns_first = set(first.columns)
124     columns_second = set(second.columns)
125     columns_union = columns_first.union(columns_second)
126     dtypes = pandas.Series([np.dtype(bool)] * len(columns_union), index=columns_union)
127     dtypes = dtypes.sort_index()
128     return dtypes
129 
130 
131 class Binary(Operator):
132     """Builder class for Binary operator."""
133 
134     @classmethod
135     def register(
136         cls,
137         func,
138         join_type="outer",
139         labels="replace",
140         infer_dtypes=None,
141     ):
142         """
143         Build template binary operator.
144 
145         Parameters
146         ----------
147         func : callable(pandas.DataFrame, [pandas.DataFrame, list-like, scalar]) -> pandas.DataFrame
148             Binary function to execute. Have to be able to accept at least two arguments.
149         join_type : {'left', 'right', 'outer', 'inner', None}, default: 'outer'
150             Type of join that will be used if indices of operands are not aligned.
151         labels : {"keep", "replace", "drop"}, default: "replace"
152             Whether keep labels from left Modin DataFrame, replace them with labels
153             from joined DataFrame or drop altogether to make them be computed lazily later.
154         infer_dtypes : {"common_cast", "float", "bool", None}, default: None
155             How dtypes should be inferred.
156                 * If "common_cast", casts to common dtype of operand columns.
157                 * If "float", performs type casting by finding common dtype.
158                   If the common dtype is any of the integer types, perform type casting to float.
159                   Used in case of truediv.
160                 * If "bool", dtypes would be a boolean series with same size as that of operands.
161                 * If ``None``, do not infer new dtypes (they will be computed manually once accessed).
162 
163         Returns
164         -------
165         callable
166             Function that takes query compiler and executes binary operation.
167         """
168 
169         def caller(
170             query_compiler, other, broadcast=False, *args, dtypes=None, **kwargs
171         ):
172             """
173             Apply binary `func` to passed operands.
174 
175             Parameters
176             ----------
177             query_compiler : QueryCompiler
178                 Left operand of `func`.
179             other : QueryCompiler, list-like object or scalar
180                 Right operand of `func`.
181             broadcast : bool, default: False
182                 If `other` is a one-column query compiler, indicates whether it is a Series or not.
183                 Frames and Series have to be processed differently, however we can't distinguish them
184                 at the query compiler level, so this parameter is a hint that passed from a high level API.
185             *args : args,
186                 Arguments that will be passed to `func`.
187             dtypes : "copy" or None, default: None
188                 Whether to keep old dtypes or infer new dtypes from data.
189             **kwargs : kwargs,
190                 Arguments that will be passed to `func`.
191 
192             Returns
193             -------
194             QueryCompiler
195                 Result of binary function.
196             """
197             axis = kwargs.get("axis", 0)
198             if isinstance(other, type(query_compiler)):
199                 if broadcast:
200                     assert (
201                         len(other.columns) == 1
202                     ), "Invalid broadcast argument for `broadcast_apply`, too many columns: {}".format(
203                         len(other.columns)
204                     )
205                     # Transpose on `axis=1` because we always represent an individual
206                     # column or row as a single-column Modin DataFrame
207                     if axis == 1:
208                         other = other.transpose()
209                     return query_compiler.__constructor__(
210                         query_compiler._modin_frame.broadcast_apply(
211                             axis,
212                             lambda left, right: func(
213                                 left, right.squeeze(), *args, **kwargs
214                             ),
215                             other._modin_frame,
216                             join_type=join_type,
217                             labels=labels,
218                             dtypes=dtypes,
219                         )
220                     )
221                 else:
222                     if (
223                         other._modin_frame._dtypes is not None
224                         and query_compiler._modin_frame._dtypes is not None
225                     ):
226                         if infer_dtypes == "bool":
227                             dtypes = compute_dtypes_boolean(query_compiler, other)
228                         if infer_dtypes == "common_cast":
229                             dtypes = compute_dtypes_common_cast(query_compiler, other)
230                         elif infer_dtypes == "float":
231                             dtypes = compute_dtypes_common_cast(query_compiler, other)
232                             dtypes = dtypes.apply(coerce_int_to_float64)
233                     return query_compiler.__constructor__(
234                         query_compiler._modin_frame.n_ary_op(
235                             lambda x, y: func(x, y, *args, **kwargs),
236                             [other._modin_frame],
237                             join_type=join_type,
238                             dtypes=dtypes,
239                         )
240                     )
241             else:
242                 # TODO: it's possible to chunk the `other` and broadcast them to partitions
243                 # accordingly, in that way we will be able to use more efficient `._modin_frame.map()`
244                 if isinstance(other, (dict, list, np.ndarray, pandas.Series)):
245                     new_modin_frame = query_compiler._modin_frame.apply_full_axis(
246                         axis,
247                         lambda df: func(df, other, *args, **kwargs),
248                         new_index=query_compiler.index,
249                         new_columns=query_compiler.columns,
250                         dtypes=dtypes,
251                     )
252                 else:
253                     new_modin_frame = query_compiler._modin_frame.map(
254                         lambda df: func(df, other, *args, **kwargs),
255                         dtypes=dtypes,
256                     )
257                 return query_compiler.__constructor__(new_modin_frame)
258 
259         return caller
260
```
--- 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/modin/core/dataframe/algebra/binary.py b/modin/core/dataframe/algebra/binary.py
--- a/modin/core/dataframe/algebra/binary.py
+++ b/modin/core/dataframe/algebra/binary.py
@@ -15,6 +15,7 @@
 
 import numpy as np
 import pandas
+from pandas.api.types import is_scalar
 
 from .operator import Operator
 
@@ -195,6 +196,8 @@
                 Result of binary function.
             """
             axis = kwargs.get("axis", 0)
+            shape_hint = None
+            self_columns = query_compiler._modin_frame._columns_cache
             if isinstance(other, type(query_compiler)):
                 if broadcast:
                     assert (
@@ -206,6 +209,17 @@
                     # column or row as a single-column Modin DataFrame
                     if axis == 1:
                         other = other.transpose()
+
+                    if (
+                        self_columns is not None
+                        and other._modin_frame._columns_cache is not None
+                    ):
+                        if (
+                            len(self_columns) == 1
+                            and len(other.columns) == 1
+                            and self_columns.equals(other.columns)
+                        ):
+                            shape_hint = "column"
                     return query_compiler.__constructor__(
                         query_compiler._modin_frame.broadcast_apply(
                             axis,
@@ -216,7 +230,8 @@
                             join_type=join_type,
                             labels=labels,
                             dtypes=dtypes,
-                        )
+                        ),
+                        shape_hint=shape_hint,
                     )
                 else:
                     if (
@@ -230,13 +245,24 @@
                         elif infer_dtypes == "float":
                             dtypes = compute_dtypes_common_cast(query_compiler, other)
                             dtypes = dtypes.apply(coerce_int_to_float64)
+                    if (
+                        self_columns is not None
+                        and other._modin_frame._columns_cache is not None
+                    ):
+                        if (
+                            len(self_columns) == 1
+                            and len(other.columns) == 1
+                            and query_compiler.columns.equals(other.columns)
+                        ):
+                            shape_hint = "column"
                     return query_compiler.__constructor__(
                         query_compiler._modin_frame.n_ary_op(
                             lambda x, y: func(x, y, *args, **kwargs),
                             [other._modin_frame],
                             join_type=join_type,
                             dtypes=dtypes,
-                        )
+                        ),
+                        shape_hint=shape_hint,
                     )
             else:
                 # TODO: it's possible to chunk the `other` and broadcast them to partitions
@@ -250,10 +276,18 @@
                         dtypes=dtypes,
                     )
                 else:
+                    if (
+                        self_columns is not None
+                        and len(self_columns) == 1
+                        and is_scalar(other)
+                    ):
+                        shape_hint = "column"
                     new_modin_frame = query_compiler._modin_frame.map(
                         lambda df: func(df, other, *args, **kwargs),
                         dtypes=dtypes,
                     )
-                return query_compiler.__constructor__(new_modin_frame)
+                return query_compiler.__constructor__(
+                    new_modin_frame, shape_hint=shape_hint
+                )
 
         return caller

{"golden_diff": "diff --git a/modin/core/dataframe/algebra/binary.py b/modin/core/dataframe/algebra/binary.py\n--- a/modin/core/dataframe/algebra/binary.py\n+++ b/modin/core/dataframe/algebra/binary.py\n@@ -15,6 +15,7 @@\n \n import numpy as np\n import pandas\n+from pandas.api.types import is_scalar\n \n from .operator import Operator\n \n@@ -195,6 +196,8 @@\n                 Result of binary function.\n             \"\"\"\n             axis = kwargs.get(\"axis\", 0)\n+            shape_hint = None\n+            self_columns = query_compiler._modin_frame._columns_cache\n             if isinstance(other, type(query_compiler)):\n                 if broadcast:\n                     assert (\n@@ -206,6 +209,17 @@\n                     # column or row as a single-column Modin DataFrame\n                     if axis == 1:\n                         other = other.transpose()\n+\n+                    if (\n+                        self_columns is not None\n+                        and other._modin_frame._columns_cache is not None\n+                    ):\n+                        if (\n+                            len(self_columns) == 1\n+                            and len(other.columns) == 1\n+                            and self_columns.equals(other.columns)\n+                        ):\n+                            shape_hint = \"column\"\n                     return query_compiler.__constructor__(\n                         query_compiler._modin_frame.broadcast_apply(\n                             axis,\n@@ -216,7 +230,8 @@\n                             join_type=join_type,\n                             labels=labels,\n                             dtypes=dtypes,\n-                        )\n+                        ),\n+                        shape_hint=shape_hint,\n                     )\n                 else:\n                     if (\n@@ -230,13 +245,24 @@\n                         elif infer_dtypes == \"float\":\n                             dtypes = compute_dtypes_common_cast(query_compiler, other)\n                             dtypes = dtypes.apply(coerce_int_to_float64)\n+                    if (\n+                        self_columns is not None\n+                        and other._modin_frame._columns_cache is not None\n+                    ):\n+                        if (\n+                            len(self_columns) == 1\n+                            and len(other.columns) == 1\n+                            and query_compiler.columns.equals(other.columns)\n+                        ):\n+                            shape_hint = \"column\"\n                     return query_compiler.__constructor__(\n                         query_compiler._modin_frame.n_ary_op(\n                             lambda x, y: func(x, y, *args, **kwargs),\n                             [other._modin_frame],\n                             join_type=join_type,\n                             dtypes=dtypes,\n-                        )\n+                        ),\n+                        shape_hint=shape_hint,\n                     )\n             else:\n                 # TODO: it's possible to chunk the `other` and broadcast them to partitions\n@@ -250,10 +276,18 @@\n                         dtypes=dtypes,\n                     )\n                 else:\n+                    if (\n+                        self_columns is not None\n+                        and len(self_columns) == 1\n+                        and is_scalar(other)\n+                    ):\n+                        shape_hint = \"column\"\n                     new_modin_frame = query_compiler._modin_frame.map(\n                         lambda df: func(df, other, *args, **kwargs),\n                         dtypes=dtypes,\n                     )\n-                return query_compiler.__constructor__(new_modin_frame)\n+                return query_compiler.__constructor__(\n+                    new_modin_frame, shape_hint=shape_hint\n+                )\n \n         return caller\n", "issue": "Don't trigger axes computation when doing binary operations\nWhen we do a binary operation and return a Series object, we don't need to trigger axes computation in columnarize.\r\n\n", "before_files": [{"content": "# Licensed to Modin Development Team under one or more contributor license agreements.\n# See the NOTICE file distributed with this work for additional information regarding\n# copyright ownership.  The Modin Development Team licenses this file to you under the\n# Apache License, Version 2.0 (the \"License\"); you may not use this file except in\n# compliance with the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under\n# the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific language\n# governing permissions and limitations under the License.\n\n\"\"\"Module houses builder class for Binary operator.\"\"\"\n\nimport numpy as np\nimport pandas\n\nfrom .operator import Operator\n\n\ndef coerce_int_to_float64(dtype: np.dtype) -> np.dtype:\n    \"\"\"\n    Coerce dtype to float64 if it is a variant of integer.\n\n    If dtype is integer, function returns float64 datatype.\n    If not, returns the datatype argument itself.\n\n    Parameters\n    ----------\n    dtype : np.dtype\n        NumPy datatype.\n\n    Returns\n    -------\n    dtype : np.dtype\n        Returns float64 for all int datatypes or returns the datatype itself\n        for other types.\n\n    Notes\n    -----\n    Used to precompute datatype in case of division in pandas.\n    \"\"\"\n    if dtype in np.sctypes[\"int\"] + np.sctypes[\"uint\"]:\n        return np.dtype(np.float64)\n    else:\n        return dtype\n\n\ndef compute_dtypes_common_cast(first, second) -> np.dtype:\n    \"\"\"\n    Precompute data types for binary operations by finding common type between operands.\n\n    Parameters\n    ----------\n    first : PandasQueryCompiler\n        First operand for which the binary operation would be performed later.\n    second : PandasQueryCompiler\n        Second operand for which the binary operation would be performed later.\n\n    Returns\n    -------\n    dtypes\n        The pandas series with precomputed dtypes.\n\n    Notes\n    -----\n    The dtypes of the operands are supposed to be known.\n    \"\"\"\n    dtypes_first = first._modin_frame._dtypes.to_dict()\n    dtypes_second = second._modin_frame._dtypes.to_dict()\n    columns_first = set(first.columns)\n    columns_second = set(second.columns)\n    common_columns = columns_first.intersection(columns_second)\n    mismatch_columns = columns_first.union(columns_second) - common_columns\n    # If at least one column doesn't match, the result of the non matching column would be nan.\n    nan_dtype = np.dtype(type(np.nan))\n    dtypes = pandas.Series(\n        [\n            pandas.core.dtypes.cast.find_common_type(\n                [\n                    dtypes_first[x],\n                    dtypes_second[x],\n                ]\n            )\n            for x in common_columns\n        ],\n        index=common_columns,\n    )\n    dtypes = pandas.concat(\n        [\n            dtypes,\n            pandas.Series(\n                [nan_dtype] * (len(mismatch_columns)),\n                index=mismatch_columns,\n            ),\n        ]\n    )\n    dtypes = dtypes.sort_index()\n    return dtypes\n\n\ndef compute_dtypes_boolean(first, second) -> np.dtype:\n    \"\"\"\n    Precompute data types for boolean operations.\n\n    Parameters\n    ----------\n    first : PandasQueryCompiler\n        First operand for which the binary operation would be performed later.\n    second : PandasQueryCompiler\n        Second operand for which the binary operation would be performed later.\n\n    Returns\n    -------\n    dtypes\n        The pandas series with precomputed dtypes.\n\n    Notes\n    -----\n    Finds a union of columns and finds dtypes for all these columns.\n    \"\"\"\n    columns_first = set(first.columns)\n    columns_second = set(second.columns)\n    columns_union = columns_first.union(columns_second)\n    dtypes = pandas.Series([np.dtype(bool)] * len(columns_union), index=columns_union)\n    dtypes = dtypes.sort_index()\n    return dtypes\n\n\nclass Binary(Operator):\n    \"\"\"Builder class for Binary operator.\"\"\"\n\n    @classmethod\n    def register(\n        cls,\n        func,\n        join_type=\"outer\",\n        labels=\"replace\",\n        infer_dtypes=None,\n    ):\n        \"\"\"\n        Build template binary operator.\n\n        Parameters\n        ----------\n        func : callable(pandas.DataFrame, [pandas.DataFrame, list-like, scalar]) -> pandas.DataFrame\n            Binary function to execute. Have to be able to accept at least two arguments.\n        join_type : {'left', 'right', 'outer', 'inner', None}, default: 'outer'\n            Type of join that will be used if indices of operands are not aligned.\n        labels : {\"keep\", \"replace\", \"drop\"}, default: \"replace\"\n            Whether keep labels from left Modin DataFrame, replace them with labels\n            from joined DataFrame or drop altogether to make them be computed lazily later.\n        infer_dtypes : {\"common_cast\", \"float\", \"bool\", None}, default: None\n            How dtypes should be inferred.\n                * If \"common_cast\", casts to common dtype of operand columns.\n                * If \"float\", performs type casting by finding common dtype.\n                  If the common dtype is any of the integer types, perform type casting to float.\n                  Used in case of truediv.\n                * If \"bool\", dtypes would be a boolean series with same size as that of operands.\n                * If ``None``, do not infer new dtypes (they will be computed manually once accessed).\n\n        Returns\n        -------\n        callable\n            Function that takes query compiler and executes binary operation.\n        \"\"\"\n\n        def caller(\n            query_compiler, other, broadcast=False, *args, dtypes=None, **kwargs\n        ):\n            \"\"\"\n            Apply binary `func` to passed operands.\n\n            Parameters\n            ----------\n            query_compiler : QueryCompiler\n                Left operand of `func`.\n            other : QueryCompiler, list-like object or scalar\n                Right operand of `func`.\n            broadcast : bool, default: False\n                If `other` is a one-column query compiler, indicates whether it is a Series or not.\n                Frames and Series have to be processed differently, however we can't distinguish them\n                at the query compiler level, so this parameter is a hint that passed from a high level API.\n            *args : args,\n                Arguments that will be passed to `func`.\n            dtypes : \"copy\" or None, default: None\n                Whether to keep old dtypes or infer new dtypes from data.\n            **kwargs : kwargs,\n                Arguments that will be passed to `func`.\n\n            Returns\n            -------\n            QueryCompiler\n                Result of binary function.\n            \"\"\"\n            axis = kwargs.get(\"axis\", 0)\n            if isinstance(other, type(query_compiler)):\n                if broadcast:\n                    assert (\n                        len(other.columns) == 1\n                    ), \"Invalid broadcast argument for `broadcast_apply`, too many columns: {}\".format(\n                        len(other.columns)\n                    )\n                    # Transpose on `axis=1` because we always represent an individual\n                    # column or row as a single-column Modin DataFrame\n                    if axis == 1:\n                        other = other.transpose()\n                    return query_compiler.__constructor__(\n                        query_compiler._modin_frame.broadcast_apply(\n                            axis,\n                            lambda left, right: func(\n                                left, right.squeeze(), *args, **kwargs\n                            ),\n                            other._modin_frame,\n                            join_type=join_type,\n                            labels=labels,\n                            dtypes=dtypes,\n                        )\n                    )\n                else:\n                    if (\n                        other._modin_frame._dtypes is not None\n                        and query_compiler._modin_frame._dtypes is not None\n                    ):\n                        if infer_dtypes == \"bool\":\n                            dtypes = compute_dtypes_boolean(query_compiler, other)\n                        if infer_dtypes == \"common_cast\":\n                            dtypes = compute_dtypes_common_cast(query_compiler, other)\n                        elif infer_dtypes == \"float\":\n                            dtypes = compute_dtypes_common_cast(query_compiler, other)\n                            dtypes = dtypes.apply(coerce_int_to_float64)\n                    return query_compiler.__constructor__(\n                        query_compiler._modin_frame.n_ary_op(\n                            lambda x, y: func(x, y, *args, **kwargs),\n                            [other._modin_frame],\n                            join_type=join_type,\n                            dtypes=dtypes,\n                        )\n                    )\n            else:\n                # TODO: it's possible to chunk the `other` and broadcast them to partitions\n                # accordingly, in that way we will be able to use more efficient `._modin_frame.map()`\n                if isinstance(other, (dict, list, np.ndarray, pandas.Series)):\n                    new_modin_frame = query_compiler._modin_frame.apply_full_axis(\n                        axis,\n                        lambda df: func(df, other, *args, **kwargs),\n                        new_index=query_compiler.index,\n                        new_columns=query_compiler.columns,\n                        dtypes=dtypes,\n                    )\n                else:\n                    new_modin_frame = query_compiler._modin_frame.map(\n                        lambda df: func(df, other, *args, **kwargs),\n                        dtypes=dtypes,\n                    )\n                return query_compiler.__constructor__(new_modin_frame)\n\n        return caller\n", "path": "modin/core/dataframe/algebra/binary.py"}], "after_files": [{"content": "# Licensed to Modin Development Team under one or more contributor license agreements.\n# See the NOTICE file distributed with this work for additional information regarding\n# copyright ownership.  The Modin Development Team licenses this file to you under the\n# Apache License, Version 2.0 (the \"License\"); you may not use this file except in\n# compliance with the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under\n# the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific language\n# governing permissions and limitations under the License.\n\n\"\"\"Module houses builder class for Binary operator.\"\"\"\n\nimport numpy as np\nimport pandas\nfrom pandas.api.types import is_scalar\n\nfrom .operator import Operator\n\n\ndef coerce_int_to_float64(dtype: np.dtype) -> np.dtype:\n    \"\"\"\n    Coerce dtype to float64 if it is a variant of integer.\n\n    If dtype is integer, function returns float64 datatype.\n    If not, returns the datatype argument itself.\n\n    Parameters\n    ----------\n    dtype : np.dtype\n        NumPy datatype.\n\n    Returns\n    -------\n    dtype : np.dtype\n        Returns float64 for all int datatypes or returns the datatype itself\n        for other types.\n\n    Notes\n    -----\n    Used to precompute datatype in case of division in pandas.\n    \"\"\"\n    if dtype in np.sctypes[\"int\"] + np.sctypes[\"uint\"]:\n        return np.dtype(np.float64)\n    else:\n        return dtype\n\n\ndef compute_dtypes_common_cast(first, second) -> np.dtype:\n    \"\"\"\n    Precompute data types for binary operations by finding common type between operands.\n\n    Parameters\n    ----------\n    first : PandasQueryCompiler\n        First operand for which the binary operation would be performed later.\n    second : PandasQueryCompiler\n        Second operand for which the binary operation would be performed later.\n\n    Returns\n    -------\n    dtypes\n        The pandas series with precomputed dtypes.\n\n    Notes\n    -----\n    The dtypes of the operands are supposed to be known.\n    \"\"\"\n    dtypes_first = first._modin_frame._dtypes.to_dict()\n    dtypes_second = second._modin_frame._dtypes.to_dict()\n    columns_first = set(first.columns)\n    columns_second = set(second.columns)\n    common_columns = columns_first.intersection(columns_second)\n    mismatch_columns = columns_first.union(columns_second) - common_columns\n    # If at least one column doesn't match, the result of the non matching column would be nan.\n    nan_dtype = np.dtype(type(np.nan))\n    dtypes = pandas.Series(\n        [\n            pandas.core.dtypes.cast.find_common_type(\n                [\n                    dtypes_first[x],\n                    dtypes_second[x],\n                ]\n            )\n            for x in common_columns\n        ],\n        index=common_columns,\n    )\n    dtypes = pandas.concat(\n        [\n            dtypes,\n            pandas.Series(\n                [nan_dtype] * (len(mismatch_columns)),\n                index=mismatch_columns,\n            ),\n        ]\n    )\n    dtypes = dtypes.sort_index()\n    return dtypes\n\n\ndef compute_dtypes_boolean(first, second) -> np.dtype:\n    \"\"\"\n    Precompute data types for boolean operations.\n\n    Parameters\n    ----------\n    first : PandasQueryCompiler\n        First operand for which the binary operation would be performed later.\n    second : PandasQueryCompiler\n        Second operand for which the binary operation would be performed later.\n\n    Returns\n    -------\n    dtypes\n        The pandas series with precomputed dtypes.\n\n    Notes\n    -----\n    Finds a union of columns and finds dtypes for all these columns.\n    \"\"\"\n    columns_first = set(first.columns)\n    columns_second = set(second.columns)\n    columns_union = columns_first.union(columns_second)\n    dtypes = pandas.Series([np.dtype(bool)] * len(columns_union), index=columns_union)\n    dtypes = dtypes.sort_index()\n    return dtypes\n\n\nclass Binary(Operator):\n    \"\"\"Builder class for Binary operator.\"\"\"\n\n    @classmethod\n    def register(\n        cls,\n        func,\n        join_type=\"outer\",\n        labels=\"replace\",\n        infer_dtypes=None,\n    ):\n        \"\"\"\n        Build template binary operator.\n\n        Parameters\n        ----------\n        func : callable(pandas.DataFrame, [pandas.DataFrame, list-like, scalar]) -> pandas.DataFrame\n            Binary function to execute. Have to be able to accept at least two arguments.\n        join_type : {'left', 'right', 'outer', 'inner', None}, default: 'outer'\n            Type of join that will be used if indices of operands are not aligned.\n        labels : {\"keep\", \"replace\", \"drop\"}, default: \"replace\"\n            Whether keep labels from left Modin DataFrame, replace them with labels\n            from joined DataFrame or drop altogether to make them be computed lazily later.\n        infer_dtypes : {\"common_cast\", \"float\", \"bool\", None}, default: None\n            How dtypes should be inferred.\n                * If \"common_cast\", casts to common dtype of operand columns.\n                * If \"float\", performs type casting by finding common dtype.\n                  If the common dtype is any of the integer types, perform type casting to float.\n                  Used in case of truediv.\n                * If \"bool\", dtypes would be a boolean series with same size as that of operands.\n                * If ``None``, do not infer new dtypes (they will be computed manually once accessed).\n\n        Returns\n        -------\n        callable\n            Function that takes query compiler and executes binary operation.\n        \"\"\"\n\n        def caller(\n            query_compiler, other, broadcast=False, *args, dtypes=None, **kwargs\n        ):\n            \"\"\"\n            Apply binary `func` to passed operands.\n\n            Parameters\n            ----------\n            query_compiler : QueryCompiler\n                Left operand of `func`.\n            other : QueryCompiler, list-like object or scalar\n                Right operand of `func`.\n            broadcast : bool, default: False\n                If `other` is a one-column query compiler, indicates whether it is a Series or not.\n                Frames and Series have to be processed differently, however we can't distinguish them\n                at the query compiler level, so this parameter is a hint that passed from a high level API.\n            *args : args,\n                Arguments that will be passed to `func`.\n            dtypes : \"copy\" or None, default: None\n                Whether to keep old dtypes or infer new dtypes from data.\n            **kwargs : kwargs,\n                Arguments that will be passed to `func`.\n\n            Returns\n            -------\n            QueryCompiler\n                Result of binary function.\n            \"\"\"\n            axis = kwargs.get(\"axis\", 0)\n            shape_hint = None\n            self_columns = query_compiler._modin_frame._columns_cache\n            if isinstance(other, type(query_compiler)):\n                if broadcast:\n                    assert (\n                        len(other.columns) == 1\n                    ), \"Invalid broadcast argument for `broadcast_apply`, too many columns: {}\".format(\n                        len(other.columns)\n                    )\n                    # Transpose on `axis=1` because we always represent an individual\n                    # column or row as a single-column Modin DataFrame\n                    if axis == 1:\n                        other = other.transpose()\n\n                    if (\n                        self_columns is not None\n                        and other._modin_frame._columns_cache is not None\n                    ):\n                        if (\n                            len(self_columns) == 1\n                            and len(other.columns) == 1\n                            and self_columns.equals(other.columns)\n                        ):\n                            shape_hint = \"column\"\n                    return query_compiler.__constructor__(\n                        query_compiler._modin_frame.broadcast_apply(\n                            axis,\n                            lambda left, right: func(\n                                left, right.squeeze(), *args, **kwargs\n                            ),\n                            other._modin_frame,\n                            join_type=join_type,\n                            labels=labels,\n                            dtypes=dtypes,\n                        ),\n                        shape_hint=shape_hint,\n                    )\n                else:\n                    if (\n                        other._modin_frame._dtypes is not None\n                        and query_compiler._modin_frame._dtypes is not None\n                    ):\n                        if infer_dtypes == \"bool\":\n                            dtypes = compute_dtypes_boolean(query_compiler, other)\n                        if infer_dtypes == \"common_cast\":\n                            dtypes = compute_dtypes_common_cast(query_compiler, other)\n                        elif infer_dtypes == \"float\":\n                            dtypes = compute_dtypes_common_cast(query_compiler, other)\n                            dtypes = dtypes.apply(coerce_int_to_float64)\n                    if (\n                        self_columns is not None\n                        and other._modin_frame._columns_cache is not None\n                    ):\n                        if (\n                            len(self_columns) == 1\n                            and len(other.columns) == 1\n                            and query_compiler.columns.equals(other.columns)\n                        ):\n                            shape_hint = \"column\"\n                    return query_compiler.__constructor__(\n                        query_compiler._modin_frame.n_ary_op(\n                            lambda x, y: func(x, y, *args, **kwargs),\n                            [other._modin_frame],\n                            join_type=join_type,\n                            dtypes=dtypes,\n                        ),\n                        shape_hint=shape_hint,\n                    )\n            else:\n                # TODO: it's possible to chunk the `other` and broadcast them to partitions\n                # accordingly, in that way we will be able to use more efficient `._modin_frame.map()`\n                if isinstance(other, (dict, list, np.ndarray, pandas.Series)):\n                    new_modin_frame = query_compiler._modin_frame.apply_full_axis(\n                        axis,\n                        lambda df: func(df, other, *args, **kwargs),\n                        new_index=query_compiler.index,\n                        new_columns=query_compiler.columns,\n                        dtypes=dtypes,\n                    )\n                else:\n                    if (\n                        self_columns is not None\n                        and len(self_columns) == 1\n                        and is_scalar(other)\n                    ):\n                        shape_hint = \"column\"\n                    new_modin_frame = query_compiler._modin_frame.map(\n                        lambda df: func(df, other, *args, **kwargs),\n                        dtypes=dtypes,\n                    )\n                return query_compiler.__constructor__(\n                    new_modin_frame, shape_hint=shape_hint\n                )\n\n        return caller\n", "path": "modin/core/dataframe/algebra/binary.py"}]}