Repository: modin-project/modin

Issue:
CalciteSerializer does not support unsigned integers
### Modin version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the latest released version of Modin.

- [X] I have confirmed this bug exists on the main branch of Modin. (In order to do this you can follow [this guide](https://modin.readthedocs.io/en/stable/getting_started/installation.html#installing-from-the-github-master-branch).)


### Reproducible Example

```python
import modin.pandas as pd

df = pd.DataFrame({"A": [255, 1]})
assert df.astype(np.uint8).sum()[0] == 256
```


### Issue Description

The root cause of the issue https://github.com/intel-ai/hdk/issues/168 is that uint serialization is not supported by CalciteSerializer.

### Expected Behavior

See the example.

### Error Logs

<details>

```python-traceback

modin/experimental/core/execution/native/implementations/hdk_on_native/calcite_serializer.py", line 352, in serialize_dtype
    return {"type": type(self)._DTYPE_STRINGS[dtype.name], "nullable": True}
KeyError: 'uint8'

```

</details>


### Installed Versions

<details>

INSTALLED VERSIONS
------------------
commit           : 53d9096d1259156fc3390ccd3831158ab33da2f1
python           : 3.10.8.final.0
python-bits      : 64
OS               : Linux
OS-release       : 5.15.79.1-microsoft-standard-WSL2
Version          : #1 SMP Wed Nov 23 01:01:46 UTC 2022
machine          : x86_64
processor        : x86_64
byteorder        : little
LC_ALL           : None
LANG             : en_GB.UTF-8
LOCALE           : en_GB.UTF-8

Modin dependencies
------------------
modin            : 0.18.0+43.g53d9096d.dirty
hdk              : present

pandas dependencies
-------------------
pandas           : 1.5.2
numpy            : 1.23.5
pytz             : 2022.6
dateutil         : 2.8.2
setuptools       : 65.5.1
pip              : 22.3.1
Cython           : None
pytest           : 7.2.0
hypothesis       : None
sphinx           : 5.3.0
blosc            : None
feather          : None
xlsxwriter       : None
lxml.etree       : None
html5lib         : None
pymysql          : None
psycopg2         : None
jinja2           : 3.1.2
IPython          : None
pandas_datareader: None
bs4              : None
bottleneck       : None
brotli           : 
fastparquet      : 2022.12.0
fsspec           : 2022.11.0
gcsfs            : None
matplotlib       : 3.6.2
numba            : None
numexpr          : None
odfpy            : None
openpyxl         : 3.0.10
pandas_gbq       : None
pyarrow          : 8.0.1
pyreadstat       : None
pyxlsb           : None
s3fs             : 2022.11.0
scipy            : 1.9.3
snappy           : None
sqlalchemy       : 1.4.44
tables           : None
tabulate         : None
xarray           : 2022.12.0
xlrd             : 2.0.1
xlwt             : None
zstandard        : None
tzdata           : None

</details>



Discussion:

Resolving patch:
diff --git a/modin/experimental/core/execution/native/implementations/hdk_on_native/calcite_serializer.py b/modin/experimental/core/execution/native/implementations/hdk_on_native/calcite_serializer.py
--- a/modin/experimental/core/execution/native/implementations/hdk_on_native/calcite_serializer.py
+++ b/modin/experimental/core/execution/native/implementations/hdk_on_native/calcite_serializer.py
@@ -32,10 +32,18 @@
     CalciteJoinNode,
     CalciteUnionNode,
 )
+from modin.error_message import ErrorMessage
 import json
 import numpy as np
 
 
+def _warn_if_unsigned(dtype):  # noqa: GL08
+    if np.issubdtype(dtype, np.unsignedinteger):
+        ErrorMessage.single_warning(
+            "HDK does not support unsigned integer types, such types will be rounded up to the signed equivalent."
+        )
+
+
 class CalciteSerializer:
     """
     Serializer for calcite node sequence.
@@ -45,16 +53,32 @@ class CalciteSerializer:
     a request in JSON format which can be fed to HDK.
     """
 
-    dtype_strings = {
+    _DTYPE_STRINGS = {
         "int8": "TINYINT",
         "int16": "SMALLINT",
         "int32": "INTEGER",
         "int64": "BIGINT",
+        "uint8": "SMALLINT",
+        "uint16": "INTEGER",
+        "uint32": "BIGINT",
+        "uint64": "BIGINT",
         "bool": "BOOLEAN",
         "float32": "FLOAT",
         "float64": "DOUBLE",
     }
 
+    _INT_OPTS = {
+        np.int8: ("TINYINT", 3),
+        np.int16: ("SMALLINT", 5),
+        np.int32: ("INTEGER", 10),
+        np.int64: ("BIGINT", 19),
+        np.uint8: ("SMALLINT", 5),
+        np.uint16: ("INTEGER", 10),
+        np.uint32: ("BIGINT", 19),
+        np.uint64: ("BIGINT", 19),
+        int: ("BIGINT", 19),
+    }
+
     def serialize(self, plan):
         """
         Serialize a sequence of Calcite nodes into JSON format.
@@ -252,7 +276,7 @@ def serialize_literal(self, literal):
                 "type_scale": -2147483648,
                 "type_precision": len(literal.val),
             }
-        if type(literal.val) in (int, np.int8, np.int16, np.int32, np.int64):
+        if type(literal.val) in self._INT_OPTS.keys():
             target_type, precision = self.opts_for_int_type(type(literal.val))
             return {
                 "literal": int(literal.val),
@@ -304,15 +328,11 @@ def opts_for_int_type(self, int_type):
         -------
         tuple
         """
-        if int_type is np.int8:
-            return "TINYINT", 3
-        if int_type is np.int16:
-            return "SMALLINT", 5
-        if int_type is np.int32:
-            return "INTEGER", 10
-        if int_type in (np.int64, int):
-            return "BIGINT", 19
-        raise NotImplementedError(f"Unsupported integer type {int_type.__name__}")
+        try:
+            _warn_if_unsigned(int_type)
+            return self._INT_OPTS[int_type]
+        except KeyError:
+            raise NotImplementedError(f"Unsupported integer type {int_type.__name__}")
 
     def serialize_dtype(self, dtype):
         """
@@ -328,7 +348,8 @@ def serialize_dtype(self, dtype):
         dict
             Serialized data type.
         """
-        return {"type": type(self).dtype_strings[dtype.name], "nullable": True}
+        _warn_if_unsigned(dtype)
+        return {"type": self._DTYPE_STRINGS[dtype.name], "nullable": True}
 
     def serialize_input_idx(self, expr):
         """
diff --git a/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py b/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py
--- a/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py
+++ b/modin/experimental/core/execution/native/implementations/hdk_on_native/expr.py
@@ -669,7 +669,21 @@ class LiteralExpr(BaseExpr):
 
     def __init__(self, val):
         assert val is None or isinstance(
-            val, (int, float, bool, str, np.int8, np.int16, np.int32, np.int64)
+            val,
+            (
+                int,
+                float,
+                bool,
+                str,
+                np.int8,
+                np.int16,
+                np.int32,
+                np.int64,
+                np.uint8,
+                np.uint16,
+                np.uint32,
+                np.uint64,
+            ),
         ), f"unsupported literal value {val} of type {type(val)}"
         self.val = val
         if val is None:


Test patch:
diff --git a/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py b/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py
--- a/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py
+++ b/modin/experimental/core/execution/native/implementations/hdk_on_native/test/test_dataframe.py
@@ -2028,6 +2028,40 @@ def test_uint_overflow(self, md_df_constructor):
             with ForceHdkImport(md_df):
                 pass
 
+    def test_uint_serialization(self):
+        # Tests for CalciteSerializer.serialize_literal()
+        df = pd.DataFrame({"A": [np.nan, 1]})
+        assert (
+            df.fillna(np.uint8(np.iinfo(np.uint8).max)).sum()[0]
+            == np.iinfo(np.uint8).max + 1
+        )
+        assert (
+            df.fillna(np.uint16(np.iinfo(np.uint16).max)).sum()[0]
+            == np.iinfo(np.uint16).max + 1
+        )
+        assert (
+            df.fillna(np.uint32(np.iinfo(np.uint32).max)).sum()[0]
+            == np.iinfo(np.uint32).max + 1
+        )
+        # HDK represents 'uint64' as 'int64' internally due to a lack of support
+        # for unsigned ints, that's why using 'int64.max' here
+        assert (
+            df.fillna(np.uint64(np.iinfo(np.int64).max - 1)).sum()[0]
+            == np.iinfo(np.int64).max
+        )
+
+        # Tests for CalciteSerializer.serialize_dtype()
+        df = pd.DataFrame({"A": [np.iinfo(np.uint8).max, 1]})
+        assert df.astype(np.uint8).sum()[0] == np.iinfo(np.uint8).max + 1
+        df = pd.DataFrame({"A": [np.iinfo(np.uint16).max, 1]})
+        assert df.astype(np.uint16).sum()[0] == np.iinfo(np.uint16).max + 1
+        df = pd.DataFrame({"A": [np.iinfo(np.uint32).max, 1]})
+        assert df.astype(np.uint32).sum()[0] == np.iinfo(np.uint32).max + 1
+        # HDK represents 'uint64' as 'int64' internally due to a lack of support
+        # for unsigned ints, that's why using 'int64.max' here
+        df = pd.DataFrame({"A": [np.iinfo(np.int64).max - 1, 1]})
+        assert df.astype(np.uint64).sum()[0] == np.iinfo(np.int64).max
+
 
 class TestDropna:
     data = {