Using Functions in Nada Programs #11
-
|
Hi I'm trying to build a PyNada program that calculates the euclidean distance between 2 points. For this I have build the following script: from nada_dsl import *
def nada_main():
party1 = Party(name="Party1")
#party2 = Party(name="Party2")
# Assume points are 2-dimensional, with coordinates as secret integers
point1_x = SecretInteger(Input(name="point1_x", party=party1)) # 500
point1_y = SecretInteger(Input(name="point1_y", party=party1)) # 10
point2_x = SecretInteger(Input(name="point2_x", party=party1)) # 1
point2_y = SecretInteger(Input(name="point2_y", party=party1)) # 1
# Define operations for calculating Euclidean distance
@nada_fn
def multiply(a: SecretInteger, b: SecretInteger) -> SecretInteger:
return a * b
@nada_fn
def add(a: SecretInteger, b: SecretInteger) -> SecretInteger:
return a + b
@nada_fn
def subtract(a: SecretInteger, b: SecretInteger) -> SecretInteger:
return a - b
# Output the squared Euclidean distance (since we might not be able to calculate the square root directly)
sum_1 = add(point1_x , point1_y) # 500 + 10 = 510
sum_2 = add(point2_x , point2_y) # 1 + 1 = 2
sub_x = subtract(point1_x, point2_x) # 500 - 1 = 499
sub_y = subtract(point1_y, point2_y) # 10 - 1 = 9
simple_mul = multiply(point1_x, point1_y) # 500 * 10 = 5000
chained_sum_1 = add(sum_1, sum_2) # 510 + 2 = 512
chained_sum_2 = add(sum_2, sum_1) # 510 + 2 = 512
# Since the DSL does not support ** for power, use multiplication for squaring
diff_x_squared = multiply(sub_x, sub_x)
diff_y_squared = multiply(sub_y, sub_y)
# Sum the squared differences
#sum_of_squares = add(diff_x_squared, diff_y_squared)
out_distance_squared = Output(simple_mul, "euclidean_distance_squared", party1)
return [out_distance_squared]For debugging and understanding purposes I've modifying the output variable as follows: Until this point every result makes sense to me and works as expected. However, for the following outputs I'm not obtaining the expected outcome: 1- out_distance_squared = Output(chained_sum_1, "euclidean_distance_squared", party1): The result is 510 ( 512 is the expected result) ❌ Finally, for the following output I'm facing an error: Is there anything I'm missing about correctly performing Nada operations? If so, what is the correct method for operating on the results of previous operations? |
Beta Was this translation helpful? Give feedback.
Replies: 0 comments 5 replies
-
|
Could you provide exactly how you are running this test? Are you using the simulator or uploading and executing this with a client? This error below appears to be complaining of the secrets name. I would expect the client calling syntax to look something like: permissions = nillion.Permissions.default_for_user(client.user_id())
point1_x = nillion.SecretInteger(500)
point1_y = nillion.SecretInteger(10)
point2_x = nillion.SecretInteger(2)
point2_y = nillion.SecretInteger(2)
secrets_object = nillion.Secrets({
"point1_x": point1_x,
"point1_y": point1_y,
"point2_x": point2_x,
"point2_y": point2_y
})
store_id = await client.store_secrets(
cluster_id, None, secrets_object, permissions
) |
Beta Was this translation helpful? Give feedback.
-
|
hey @wwwehr thanks for the quick response. I'm executing this on a local test Nillion cluster by running ./bootstrap-local-environment.sh The code on my python client is the following: from pdb import set_trace as bp
import argparse
import asyncio
import py_nillion_client as nillion
import os
import sys
from dotenv import load_dotenv
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from helpers.nillion_client_helper import create_nillion_client
load_dotenv()
# Store secrets then compute on secrets
async def main():
# Get cluster_id and userkey_path from the .env file
cluster_id = os.getenv("NILLION_CLUSTER_ID")
userkey_path = os.getenv("NILLION_WRITERKEY_PATH")
# Read user key from file
userkey = nillion.UserKey.from_file(userkey_path)
# Create Nillion Client for user
client = create_nillion_client(userkey)
# Get the party id and user id
party_id = client.party_id()
user_id = client.user_id()
# Set the program id
program_id=f"{user_id}/debug_euclidean_distance"
# Set the party name to match the party name from the stored program
party_name="Party1"
# Create a secret
stored_secret = nillion.Secrets({
"point1_x": nillion.SecretInteger(500),
"point1_y": nillion.SecretInteger(10),
"point2_x": nillion.SecretInteger(1),
"point2_y": nillion.SecretInteger(1),
})
# Create secret bindings to tie a secret to a program and set the input party
secret_bindings = nillion.ProgramBindings(program_id)
secret_bindings.add_input_party(party_name, party_id)
# Store a secret
store_id = await client.store_secrets(
cluster_id, secret_bindings, stored_secret, None
)
print(f"Computing using program {program_id}")
print(f"Use secret store_id: {store_id}")
# Bind the parties in the computation to the client to set input and output parties
compute_bindings = nillion.ProgramBindings(program_id)
compute_bindings.add_input_party(party_name, party_id)
compute_bindings.add_output_party(party_name, party_id)
# Add the second secret at computation time
#computation_time_secrets = nillion.Secrets({"point1_y": nillion.SecretInteger(10)})
computation_time_secrets = nillion.Secrets({})
# Compute on the secret
compute_id = await client.compute(
cluster_id,
compute_bindings,
[store_id],
computation_time_secrets,
nillion.PublicVariables({}),
)
# Print compute result
print(f"The computation was sent to the network. compute_id: {compute_id}")
while True:
compute_event = await client.next_compute_event()
if isinstance(compute_event, nillion.ComputeFinishedEvent):
print(f"✅ Compute complete for compute_id {compute_event.uuid}")
print(f"🖥️ The result is {compute_event.result.value}")
break
asyncio.run(main()) |
Beta Was this translation helpful? Give feedback.
-
|
Hi @lumasepa thanks for the support on this. from nada_dsl import *
def nada_main():
party1 = Party(name="Party1")
#party2 = Party(name="Party2")
# Assume points are 2-dimensional, with coordinates as secret integers
point1_x = SecretInteger(Input(name="point1_x", party=party1)) # 500
point1_y = SecretInteger(Input(name="point1_y", party=party1)) # 10
point2_x = SecretInteger(Input(name="point2_x", party=party1)) # 1
point2_y = SecretInteger(Input(name="point2_y", party=party1)) # 1
# Define operations for calculating Euclidean distance
def multiply(a: SecretInteger, b: SecretInteger) -> SecretInteger:
return a * b
def add(a: SecretInteger, b: SecretInteger) -> SecretInteger:
return a + b
def subtract(a: SecretInteger, b: SecretInteger) -> SecretInteger:
return a - b
# Output the squared Euclidean distance (since we might not be able to calculate the square root directly)
sum_1 = add(point1_x , point1_y) # 500 + 10 = 510
sum_2 = add(point2_x , point2_y) # 1 + 1 = 2
# Since the DSL does not support ** for power, use multiplication for squaring
sub_x = subtract(point1_x, point2_x) # 500 - 1 = 499
sub_y = subtract(point1_y, point2_y) # 10 - 1 = 9
simple_mul = multiply(point1_x, point1_y) # 500 * 10 = 5000
chained_sum_1 = add(sum_1, sum_2) # 510 + 2 = 512
chained_sum_2 = add(sum_2, sum_1) # 510 + 2 = 512
chained_mul = multiply(sub_y,sub_y) # 9 * 9 = 81
out_distance_squared = Output(chained_mul, "euclidean_distance_squared", party1)
return [out_distance_squared]Note: The script for the Python client is still the same as mentioned above in the thread. You're right that this change is solving some of the issues since now I'm seeing the following results:
However, I'm still not able to perform the chained mul obtaining the following result.
The following script shows the steps taken for compilation, storage, and program execution On the local cluster side, I'm seeing the error: I would highly appreciate it if you could help me identify the issue and explain why changing the output variable from the chained sum to the chained multiplication is causing this problem. |
Beta Was this translation helpful? Give feedback.

Hi @emanuel-skai,
@nada_fnshould be only used to pass functions to iterators in arrays, and we are in the process of deprecating it. If you remove the@nada_fnnotations it should work, you can maintain the functions if you want, but also you can do the operations directly without functions. Here is an example. watch that the output issimple_mulnot the final output.