pryvx 2.4.1__py3-none-any.whl → 2.4.3__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 +15 -6
- pryvx/fl_ensemble.py +20 -0
- pryvx/fl_server.py +30 -10
- pryvx/gdp.py +14 -2
- pryvx/ldp.py +26 -0
- {pryvx-2.4.1.dist-info → pryvx-2.4.3.dist-info}/METADATA +1 -1
- pryvx-2.4.3.dist-info/RECORD +16 -0
- pryvx-2.4.1.dist-info/RECORD +0 -14
- {pryvx-2.4.1.dist-info → pryvx-2.4.3.dist-info}/LICENSE +0 -0
- {pryvx-2.4.1.dist-info → pryvx-2.4.3.dist-info}/WHEEL +0 -0
- {pryvx-2.4.1.dist-info → pryvx-2.4.3.dist-info}/top_level.txt +0 -0
pryvx/__init__.py
CHANGED
pryvx/fl_client.py
CHANGED
@@ -2,26 +2,35 @@ 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
12
|
serialized_model = pickle.dumps(model)
|
13
|
+
return serialized_model, model
|
13
14
|
|
14
|
-
return serialized_model
|
15
15
|
|
16
|
+
def train_random_forest_classifier(features, labels):
|
17
|
+
model = RandomForestClassifier(n_estimators=100, random_state=42)
|
18
|
+
model.fit(features, labels)
|
19
|
+
serialized_model = pickle.dumps(model)
|
20
|
+
return serialized_model, model
|
16
21
|
|
17
|
-
|
22
|
+
|
23
|
+
def send_params(serialized_model, connection_url, client_id):
|
18
24
|
|
19
25
|
with grpc.insecure_channel(connection_url) as channel:
|
20
26
|
stub = pryvx_pb2_grpc.ModelServiceStub(channel)
|
21
27
|
|
28
|
+
# Attach metadata (client ID) in the request
|
29
|
+
metadata = (("client_id", client_id),)
|
30
|
+
|
22
31
|
model_params = pryvx_pb2.ModelParams(params=serialized_model)
|
23
32
|
|
24
|
-
response = stub.SendModelParams(model_params)
|
33
|
+
response = stub.SendModelParams(model_params, metadata=metadata)
|
25
34
|
|
26
|
-
return "Model Params sent to server"
|
35
|
+
return f"✅ Model Params sent to server from {client_id}"
|
27
36
|
|
pryvx/fl_ensemble.py
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
def federated_ensemble(estimators, X_test):
|
4
|
+
"""
|
5
|
+
Perform soft voting ensemble on a list of trained estimators.
|
6
|
+
|
7
|
+
:param estimators: List of trained models that support `predict_proba`
|
8
|
+
:param X_test: Test dataset (features only)
|
9
|
+
:return: Final predictions after soft voting
|
10
|
+
"""
|
11
|
+
if not estimators:
|
12
|
+
raise ValueError("No models provided for ensemble.")
|
13
|
+
|
14
|
+
# Sum all probability predictions
|
15
|
+
avg_probs = sum(model.predict_proba(X_test) for model in estimators) / len(estimators)
|
16
|
+
|
17
|
+
# Convert probabilities to final predictions (highest probability wins)
|
18
|
+
y_pred_ensemble = np.argmax(avg_probs, axis=1)
|
19
|
+
|
20
|
+
return y_pred_ensemble
|
pryvx/fl_server.py
CHANGED
@@ -3,21 +3,41 @@ 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
|
+
# Extract metadata (client ID)
|
20
|
+
client_id = dict(context.invocation_metadata()).get("client_id", "unknown_client")
|
21
|
+
|
22
|
+
# Deserialize model received from client
|
23
|
+
model = pickle.loads(request.params)
|
24
|
+
|
25
|
+
# Assign a unique client ID
|
26
|
+
client_id = f"client_{len(self.client_models) + 1}"
|
27
|
+
self.client_models[client_id] = model
|
28
|
+
|
29
|
+
# Save model to disk
|
30
|
+
model_filename = os.path.join(MODEL_SAVE_PATH, f"{client_id}_model.pkl")
|
31
|
+
with open(model_filename, "wb") as f:
|
32
|
+
|
33
|
+
pickle.dump(model, f)
|
34
|
+
|
35
|
+
print(f"✅ Received and saved model from {client_id}")
|
36
|
+
|
37
|
+
return pryvx_pb2.ModelResponse(message=f"Model from {client_id} received and saved.")
|
38
|
+
except Exception as e:
|
39
|
+
print(f"❌ Error processing model: {e}")
|
40
|
+
return pryvx_pb2.ModelResponse(message="Failed to process model")
|
21
41
|
|
22
42
|
|
23
43
|
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
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
pryvx/__init__.py,sha256=QvpYISJ8MH7XnbxC5iTMTycq7Svsld0NIbMozcndYjo,111
|
2
|
+
pryvx/fl_client.py,sha256=gQNjv99tDDjaGj0JTdAVomefqR2F3_xNLcnHJ6aGZZo,1152
|
3
|
+
pryvx/fl_ensemble.py,sha256=lvgvd-zJDSBh-RyMAjTyJZwu0eFiUNXfCU7J2Vk7g0g,715
|
4
|
+
pryvx/fl_server.py,sha256=JqYtlLtLTcKxrNB8oY-hdZmG8VajS7mIqAY3OjOUxmU,1822
|
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.3.dist-info/LICENSE,sha256=pbIMXbaorAIVW-fDch2tvtZRkVA3mz-UnXQqeCf4LDg,1086
|
13
|
+
pryvx-2.4.3.dist-info/METADATA,sha256=Ytztu3-NrHGnG2XkIBalwkZSMQiP5d7UzU0V5C8wpdQ,1066
|
14
|
+
pryvx-2.4.3.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
15
|
+
pryvx-2.4.3.dist-info/top_level.txt,sha256=1iHoeevu_FoFjdPg8HyMlgvHNUm1--9QEpFMaJEa4hw,6
|
16
|
+
pryvx-2.4.3.dist-info/RECORD,,
|
pryvx-2.4.1.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=HN70aUNTq0w9vRPMaNGfTnNi00gMQ1B7R7Cm6r6rheE,2974
|
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.1.dist-info/LICENSE,sha256=pbIMXbaorAIVW-fDch2tvtZRkVA3mz-UnXQqeCf4LDg,1086
|
11
|
-
pryvx-2.4.1.dist-info/METADATA,sha256=omTieHCpjPJe3yoCkJZ0SoX2sFj0IUMRfHIQB-8TUzI,1066
|
12
|
-
pryvx-2.4.1.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
13
|
-
pryvx-2.4.1.dist-info/top_level.txt,sha256=1iHoeevu_FoFjdPg8HyMlgvHNUm1--9QEpFMaJEa4hw,6
|
14
|
-
pryvx-2.4.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|