mirror of
https://github.com/luxfi/torus-ml.git
synced 2026-10-30 15:15:23 +00:00
The distribution called itself torus-ml while shipping a package directory named concrete, so the name a user installs and the name they import disagreed — and most of the tree already said torus.ml, which could not resolve against src/concrete. Moved src/concrete to src/torus and pointed the include at it. The dependency was Zama's concrete-python from their index. It is torus-fhe from ours, which is what luxfhe/torus already ships: that frontend declares torus-fhe and ships a torus/fhe module, so every 'from torus.fhe' in this tree was already naming our compiler and only the dependency lagged. Two distributions share one namespace, which is the shape the upstream this forked from used and the reason the split works: torus-fhe gives torus.fhe, torus-ml gives torus.ml. Swept every other spelling — identifiers, enum members, workflow variables, filenames, notebooks. LICENSE and NOTICE keep the upstream attribution a fork owes; the figures carry the old name in JPEG metadata a text sweep cannot reach.
33 KiB
33 KiB
In [1]:
import shutil
import time
from pathlib import Path
from tempfile import TemporaryDirectory
import numpy
import pandas
from torus.ml.pandas import ClientEngine, load_encrypted_dataframe
from torus.ml.pytest.utils import pandas_dataframe_are_equal
numpy.random.seed(0)
DATA_PATH = Path("data/encrypted_pandas")
# pylint: disable=pointless-statement, consider-using-withIn [2]:
CLIENT_1_DIR = DATA_PATH / "client_1"
df_left = pandas.read_csv(CLIENT_1_DIR / "df_left.csv")
df_leftOut [2]:
| index | total_bill | tip | sex | smoker | |
|---|---|---|---|---|---|
| 0 | client_1 | 12.54 | 2.50 | Male | No |
| 1 | client_2 | 11.17 | 1.50 | Female | No |
| 2 | client_3 | 20.29 | 2.75 | Female | No |
| 3 | client_4 | 14.07 | 2.50 | Male | No |
| 4 | client_5 | 15.69 | 3.00 | Male | Yes |
| 5 | client_6 | 18.29 | 3.00 | Male | No |
| 6 | client_7 | 16.93 | 3.07 | Female | No |
| 7 | client_8 | 24.27 | 2.03 | Male | Yes |
| 8 | client_9 | 8.77 | 2.00 | Male | No |
In [3]:
schema = {"index": {index_value: i + 1 for i, index_value in enumerate(df_left["index"].values)}}In [4]:
client_1_temp_dir = TemporaryDirectory(dir=str(CLIENT_1_DIR))
client_1_temp_path = Path(client_1_temp_dir.name)
# Define the directory where to store the serialized keys
client_1_keys_path = client_1_temp_path / "keys"
client_1 = ClientEngine(keys_path=client_1_keys_path)In [5]:
df_left_enc = client_1.encrypt_from_pandas(df_left, schema=schema)In [6]:
df_left_enc.get_schema()Out [6]:
| index | total_bill | tip | sex | smoker | |
|---|---|---|---|---|---|
| dtype | object | float64 | float64 | object | object |
| str_to_int | {'client_1': 1, 'client_2': 2, 'client_3': 3, ... | NaN | NaN | {'Male': 1, 'Female': 2} | {'No': 1, 'Yes': 2} |
| scale | NaN | 0.903226 | 8.917197 | NaN | NaN |
| zero_point | NaN | 6.92129 | 12.375796 | NaN | NaN |
In [7]:
df_left_enc_path = client_1_temp_path / "df_left_enc"
df_left_enc.save(df_left_enc_path)In [8]:
CLIENT_2_DIR = DATA_PATH / "client_2"
df_right = pandas.read_csv(CLIENT_2_DIR / "df_right.csv")
df_rightOut [8]:
| index | day | time | size | |
|---|---|---|---|---|
| 0 | client_2 | Thur | Lunch | 2 |
| 1 | client_5 | Sat | Dinner | 3 |
| 2 | client_9 | Sun | Dinner | 2 |
In [9]:
client_2_temp_dir = TemporaryDirectory(dir=str(CLIENT_2_DIR))
client_2_temp_path = Path(client_2_temp_dir.name)
# Define the directory where to store the serialized keys
client_2_keys_path = client_2_temp_path / "keys"
# Copy the first user's keys
shutil.copy2(client_1_keys_path, client_2_keys_path)
client_2 = ClientEngine(keys_path=client_2_keys_path)In [10]:
df_right_enc = client_2.encrypt_from_pandas(df_right, schema=schema)
df_right_encOut [10]:
| index | day | time | size |
|---|---|---|---|
| ..3a5cea3d89.. | ..5e1c826a69.. | ..a79208da47.. | ..4a46bbeabe.. |
| ..c959c81aa2.. | ..a992e555a0.. | ..ad73b4157e.. | ..947b705b07.. |
| ..ac944db8d7.. | ..76d5b0360e.. | ..c0544afe88.. | ..fa7f74cea1.. |
In [11]:
df_right_enc_path = client_2_temp_path / "df_right_enc"
df_right_enc.save(df_right_enc_path)In [12]:
df_left_enc = load_encrypted_dataframe(df_left_enc_path)
df_right_enc = load_encrypted_dataframe(df_right_enc_path)In [13]:
start = time.time()
df_joined_enc_server = df_left_enc.merge(df_right_enc, how="left", on="index")
end = time.time() - start
print(f"Total execution time: {end:.2f}s")Total execution time: 6.84s
In [14]:
df_joined_enc_server_path = client_1_temp_path / "df_joined_enc"
df_joined_enc_server.save(df_joined_enc_server_path)In [15]:
df_joined_enc = load_encrypted_dataframe(df_joined_enc_server_path)In [16]:
df_joined_cml = client_1.decrypt_to_pandas(df_joined_enc)
df_joined_cmlOut [16]:
| index | total_bill | tip | sex | smoker | day | time | size | |
|---|---|---|---|---|---|---|---|---|
| 0 | client_1 | 12.091429 | 2.509286 | Male | No | NaN | NaN | NaN |
| 1 | client_2 | 10.984286 | 1.500000 | Female | No | Thur | Lunch | 2.0 |
| 2 | client_3 | 19.841429 | 2.733571 | Female | No | NaN | NaN | NaN |
| 3 | client_4 | 14.305714 | 2.509286 | Male | No | NaN | NaN | NaN |
| 4 | client_5 | 15.412857 | 2.957857 | Male | Yes | Sat | Dinner | 3.0 |
| 5 | client_6 | 18.734286 | 2.957857 | Male | No | NaN | NaN | NaN |
| 6 | client_7 | 16.520000 | 3.070000 | Female | No | NaN | NaN | NaN |
| 7 | client_8 | 24.270000 | 2.060714 | Male | Yes | NaN | NaN | NaN |
| 8 | client_9 | 8.770000 | 1.948571 | Male | No | Sun | Dinner | 2.0 |
In [17]:
df_joined_pandas = pandas.merge(df_left, df_right, how="left", on="index")
df_joined_pandasOut [17]:
| index | total_bill | tip | sex | smoker | day | time | size | |
|---|---|---|---|---|---|---|---|---|
| 0 | client_1 | 12.54 | 2.50 | Male | No | NaN | NaN | NaN |
| 1 | client_2 | 11.17 | 1.50 | Female | No | Thur | Lunch | 2.0 |
| 2 | client_3 | 20.29 | 2.75 | Female | No | NaN | NaN | NaN |
| 3 | client_4 | 14.07 | 2.50 | Male | No | NaN | NaN | NaN |
| 4 | client_5 | 15.69 | 3.00 | Male | Yes | Sat | Dinner | 3.0 |
| 5 | client_6 | 18.29 | 3.00 | Male | No | NaN | NaN | NaN |
| 6 | client_7 | 16.93 | 3.07 | Female | No | NaN | NaN | NaN |
| 7 | client_8 | 24.27 | 2.03 | Male | Yes | NaN | NaN | NaN |
| 8 | client_9 | 8.77 | 2.00 | Male | No | Sun | Dinner | 2.0 |
In [18]:
df_are_equal = pandas_dataframe_are_equal(
df_joined_pandas, df_joined_cml, float_rtol=0.1, equal_nan=True
)
print("Torus ML data-frame is equal to Pandas data-frame:", df_are_equal, "\n")Torus ML data-frame is equal to Pandas data-frame: True
In [19]:
# Clean the temporary directories and their content
client_1_temp_dir.cleanup()
client_2_temp_dir.cleanup()