pryvx 2.4.0__py3-none-any.whl → 2.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pryvx/__init__.py +2 -1
- pryvx/fl_client.py +9 -1
- pryvx/fl_ensemble.py +16 -0
- pryvx/fl_server.py +28 -10
- pryvx/gdp.py +14 -2
- pryvx/ldp.py +26 -0
- pryvx/phe.py +24 -0
- {pryvx-2.4.0.dist-info → pryvx-2.4.2.dist-info}/METADATA +1 -1
- pryvx-2.4.2.dist-info/RECORD +16 -0
- pryvx-2.4.0.dist-info/RECORD +0 -14
- {pryvx-2.4.0.dist-info → pryvx-2.4.2.dist-info}/LICENSE +0 -0
- {pryvx-2.4.0.dist-info → pryvx-2.4.2.dist-info}/WHEEL +0 -0
- {pryvx-2.4.0.dist-info → pryvx-2.4.2.dist-info}/top_level.txt +0 -0
pryvx/__init__.py
CHANGED
pryvx/fl_client.py
CHANGED
@@ -2,10 +2,11 @@ import grpc
|
|
2
2
|
from pryvx import pryvx_pb2
|
3
3
|
from pryvx import pryvx_pb2_grpc
|
4
4
|
from sklearn.linear_model import LogisticRegression
|
5
|
+
from sklearn.ensemble import RandomForestClassifier
|
5
6
|
import pickle
|
6
7
|
|
7
8
|
|
8
|
-
def
|
9
|
+
def train_logistic_classifier(features, labels):
|
9
10
|
model = LogisticRegression()
|
10
11
|
model.fit(features, labels)
|
11
12
|
|
@@ -13,6 +14,13 @@ def train(features, labels):
|
|
13
14
|
|
14
15
|
return serialized_model
|
15
16
|
|
17
|
+
def train_random_forest_classifier(features, labels):
|
18
|
+
model = RandomForestClassifier(n_estimators=100, random_state=42)
|
19
|
+
model.fit(features, labels)
|
20
|
+
serialized_model = pickle.dumps(model)
|
21
|
+
|
22
|
+
return serialized_model
|
23
|
+
|
16
24
|
|
17
25
|
def send_params(serialized_model, connection_url):
|
18
26
|
|
pryvx/fl_ensemble.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from sklearn.ensemble import VotingClassifier
|
2
|
+
|
3
|
+
# estimators = [('client_1', model_1), ('client_2', model_2), ('client_3', model_3)]
|
4
|
+
|
5
|
+
def federated_ensemble(estimators, X_test):
|
6
|
+
# Create an ensemble using soft voting
|
7
|
+
ensemble_model = VotingClassifier(
|
8
|
+
estimators=estimators,
|
9
|
+
voting='soft' # Soft voting (probability-based)
|
10
|
+
)
|
11
|
+
|
12
|
+
# Make predictions
|
13
|
+
y_pred_ensemble = ensemble_model.predict(X_test)
|
14
|
+
|
15
|
+
return y_pred_ensemble
|
16
|
+
|
pryvx/fl_server.py
CHANGED
@@ -3,21 +3,39 @@ from pryvx import pryvx_pb2
|
|
3
3
|
from pryvx import pryvx_pb2_grpc
|
4
4
|
import pickle
|
5
5
|
from concurrent import futures
|
6
|
+
import os
|
7
|
+
import datetime
|
8
|
+
|
9
|
+
# Directory to store received models
|
10
|
+
MODEL_SAVE_PATH = "received_models/"
|
11
|
+
os.makedirs(MODEL_SAVE_PATH, exist_ok=True)
|
6
12
|
|
7
|
-
# Server
|
8
13
|
class ModelServicer(pryvx_pb2_grpc.ModelServiceServicer):
|
9
14
|
def __init__(self):
|
10
|
-
self.
|
15
|
+
self.client_models = {} # Store models from clients
|
11
16
|
|
12
17
|
def SendModelParams(self, request, context):
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
try:
|
19
|
+
# Deserialize model received from client
|
20
|
+
model = pickle.loads(request.params)
|
21
|
+
|
22
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
23
|
+
|
24
|
+
# Assign a unique client ID
|
25
|
+
client_id = f"client_{len(self.client_models) + 1}"
|
26
|
+
self.client_models[client_id] = model
|
27
|
+
|
28
|
+
# Save model to disk
|
29
|
+
model_filename = os.path.join(MODEL_SAVE_PATH, f"{client_id}_model_{timestamp}.pkl")
|
30
|
+
with open(model_filename, "wb") as f:
|
31
|
+
pickle.dump(model, f)
|
32
|
+
|
33
|
+
print(f"✅ Received and saved model from {client_id}")
|
34
|
+
|
35
|
+
return pryvx_pb2.ModelResponse(message=f"Model from {client_id} received and saved.")
|
36
|
+
except Exception as e:
|
37
|
+
print(f"❌ Error processing model: {e}")
|
38
|
+
return pryvx_pb2.ModelResponse(message="Failed to process model")
|
21
39
|
|
22
40
|
|
23
41
|
def start_server():
|
pryvx/gdp.py
CHANGED
@@ -1,15 +1,27 @@
|
|
1
1
|
import numpy as np
|
2
2
|
|
3
|
-
|
3
|
+
# Laplace Mechanism for continuous data.
|
4
|
+
# Gaussian Mechanism for continuous data (commonly used in GDP).
|
5
|
+
|
6
|
+
def laplace_mechanism(query_result, sensitivity, epsilon):
|
4
7
|
"""Applies the Laplace mechanism for GDP."""
|
5
8
|
scale = sensitivity / epsilon
|
6
9
|
noise = np.random.laplace(0, scale, 1)
|
7
|
-
return
|
10
|
+
return query_result + noise[0]
|
8
11
|
|
12
|
+
def gaussian_mechanism(query_result, epsilon=0.5, delta=1e-5, sensitivity=1.0):
|
13
|
+
"""Adds Gaussian noise to a global query result."""
|
14
|
+
sigma = np.sqrt(2 * np.log(1.25 / delta)) * sensitivity / epsilon
|
15
|
+
noise = np.random.normal(0, sigma)
|
16
|
+
return query_result + noise
|
9
17
|
|
10
18
|
class GDP:
|
11
19
|
@staticmethod
|
12
20
|
def add_noise(query_result, sensitivity, epsilon):
|
13
21
|
return laplace_mechanism(query_result, sensitivity, epsilon)
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def add_gaussian_noise(query_result):
|
25
|
+
return gaussian_mechanism(query_result)
|
14
26
|
|
15
27
|
|
pryvx/ldp.py
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import random
|
3
|
+
|
4
|
+
# Laplace Mechanism (common for numeric data)
|
5
|
+
# Randomized Response (common for categorical/binary data)
|
6
|
+
|
7
|
+
def laplace_mechanism(value, epsilon, sensitivity=1.0):
|
8
|
+
"""Adds Laplace noise to a numeric value for LDP."""
|
9
|
+
scale = sensitivity / epsilon
|
10
|
+
noise = np.random.laplace(0, scale)
|
11
|
+
return value + noise
|
12
|
+
|
13
|
+
def randomized_response(value, epsilon):
|
14
|
+
"""Implements randomized response for binary data."""
|
15
|
+
p = np.exp(epsilon) / (1 + np.exp(epsilon))
|
16
|
+
return value if random.random() < p else 1 - value
|
17
|
+
|
18
|
+
class LDP:
|
19
|
+
@staticmethod
|
20
|
+
def add_numerical_noise(value, epsilon):
|
21
|
+
return laplace_mechanism(value, epsilon)
|
22
|
+
|
23
|
+
def add_categorical_noise(value, epsilon):
|
24
|
+
return randomized_response(value, epsilon)
|
25
|
+
|
26
|
+
|
pryvx/phe.py
CHANGED
@@ -26,6 +26,14 @@ class PHE:
|
|
26
26
|
private_key = (lambda_n, mu)
|
27
27
|
return public_key, private_key
|
28
28
|
|
29
|
+
@staticmethod
|
30
|
+
def encode(value, scale_factor=1000):
|
31
|
+
return int(round(value * scale_factor))
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def decode(value, scale_factor=1000):
|
35
|
+
return value / scale_factor
|
36
|
+
|
29
37
|
@staticmethod
|
30
38
|
def encrypt(public_key, plaintext, r=None):
|
31
39
|
n, g = public_key
|
@@ -56,6 +64,22 @@ class PHE:
|
|
56
64
|
n2 = n * n
|
57
65
|
return (c1 * c2) % n2
|
58
66
|
|
67
|
+
@staticmethod
|
68
|
+
def homomorphic_add_plaintext(ciphertext, plaintext, public_key):
|
69
|
+
n, g = public_key
|
70
|
+
n2 = n * n
|
71
|
+
c_plain = pow(g, plaintext, n2)
|
72
|
+
return (ciphertext * c_plain) % n2
|
73
|
+
|
74
|
+
@staticmethod
|
75
|
+
def homomorphic_sub_plaintext(ciphertext, plaintext, public_key):
|
76
|
+
n, g = public_key
|
77
|
+
n2 = n * n
|
78
|
+
|
79
|
+
# Encrypt the plaintext as its negative (-plaintext) mod n
|
80
|
+
c_plain_neg = pow(g, -plaintext % n, n2) # Modular inverse for subtraction
|
81
|
+
return (ciphertext * c_plain_neg) % n2
|
82
|
+
|
59
83
|
@staticmethod
|
60
84
|
def homomorphic_sub(c1, c2, public_key):
|
61
85
|
n, _ = public_key
|
@@ -0,0 +1,16 @@
|
|
1
|
+
pryvx/__init__.py,sha256=QvpYISJ8MH7XnbxC5iTMTycq7Svsld0NIbMozcndYjo,111
|
2
|
+
pryvx/fl_client.py,sha256=axJKdQgXDMs1FT8rgI2HzG8keyEG__U9Y4GtiWWeHO4,986
|
3
|
+
pryvx/fl_ensemble.py,sha256=cUPPx3QpnalRKtIBtoE4PCTk34E7940W7ZxGtUisDgo,475
|
4
|
+
pryvx/fl_server.py,sha256=bJ_dm-UZvmq-JUB58b_F_V5-IsmB3eC2RS0JjF5CPs8,1739
|
5
|
+
pryvx/gdp.py,sha256=ToLtWNvBKtSs-3Na1pGe_J6dWvBxXhV2raUJkNtKMg4,925
|
6
|
+
pryvx/ldp.py,sha256=WxBQLnZ_b2wNV1rlN7rtKXaQ5waUPPvnRus-4tUnCB8,810
|
7
|
+
pryvx/phe.py,sha256=HN70aUNTq0w9vRPMaNGfTnNi00gMQ1B7R7Cm6r6rheE,2974
|
8
|
+
pryvx/pryvx_pb2.py,sha256=-ce40VMW0nQcjyhAJy1BvI32wtWNm425LSIExspK_qg,1200
|
9
|
+
pryvx/pryvx_pb2_grpc.py,sha256=pRQKblTNIVEwHsMQmhC-HGJFzsLh0sdOzQ-ksFYVK-A,2437
|
10
|
+
pryvx/psi.py,sha256=V0BUJwYfiFWhKtEL27q45yxY7U-I7B4olKQhaGbgN50,1346
|
11
|
+
pryvx/smpc.py,sha256=FZcE3gZo8WUVPmcBHSgP8iOkWt3AKcBLSLCtKczy8hs,2247
|
12
|
+
pryvx-2.4.2.dist-info/LICENSE,sha256=pbIMXbaorAIVW-fDch2tvtZRkVA3mz-UnXQqeCf4LDg,1086
|
13
|
+
pryvx-2.4.2.dist-info/METADATA,sha256=axWXanMU4AAmUsGygBZlLx7BYDT6v6LNqW9pGaw02Rk,1066
|
14
|
+
pryvx-2.4.2.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
15
|
+
pryvx-2.4.2.dist-info/top_level.txt,sha256=1iHoeevu_FoFjdPg8HyMlgvHNUm1--9QEpFMaJEa4hw,6
|
16
|
+
pryvx-2.4.2.dist-info/RECORD,,
|
pryvx-2.4.0.dist-info/RECORD
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
pryvx/__init__.py,sha256=A6GcpkYgwgf_HOrneIhGs3iTsiEyvd9vYEMsmrMUqWQ,89
|
2
|
-
pryvx/fl_client.py,sha256=p8QPlv5-CjTCwfzN0QkPr7bjKJEbU3c9Aa3HhpvZNJE,677
|
3
|
-
pryvx/fl_server.py,sha256=9fUqZfA9RZOe0fM7FYnk_8qLNw6VMj-x8tzeGXZpgKA,890
|
4
|
-
pryvx/gdp.py,sha256=-HK3Xml-huOdfz2SSmBvpgca0iU9-RwCiaD6ZyMq7bY,408
|
5
|
-
pryvx/phe.py,sha256=NLYaLFCNN5aQfL0kC1YTUi962e930p2ae5dw3flJ0qo,2178
|
6
|
-
pryvx/pryvx_pb2.py,sha256=-ce40VMW0nQcjyhAJy1BvI32wtWNm425LSIExspK_qg,1200
|
7
|
-
pryvx/pryvx_pb2_grpc.py,sha256=pRQKblTNIVEwHsMQmhC-HGJFzsLh0sdOzQ-ksFYVK-A,2437
|
8
|
-
pryvx/psi.py,sha256=V0BUJwYfiFWhKtEL27q45yxY7U-I7B4olKQhaGbgN50,1346
|
9
|
-
pryvx/smpc.py,sha256=FZcE3gZo8WUVPmcBHSgP8iOkWt3AKcBLSLCtKczy8hs,2247
|
10
|
-
pryvx-2.4.0.dist-info/LICENSE,sha256=pbIMXbaorAIVW-fDch2tvtZRkVA3mz-UnXQqeCf4LDg,1086
|
11
|
-
pryvx-2.4.0.dist-info/METADATA,sha256=PhZ10Rw7ngp_mhVp2Au9YED-g_KMVKtHmUpDp4e0Zqg,1066
|
12
|
-
pryvx-2.4.0.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
13
|
-
pryvx-2.4.0.dist-info/top_level.txt,sha256=1iHoeevu_FoFjdPg8HyMlgvHNUm1--9QEpFMaJEa4hw,6
|
14
|
-
pryvx-2.4.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|