Skip to content

Commit 161072f

Browse files
authored
feat: Added integration of rationals with NadaArray and bump version number (#16)
* feat: Added integration of rationals with NadaArray * chore: bump version number
1 parent 0c5a9dc commit 161072f

File tree

8 files changed

+168
-33
lines changed

8 files changed

+168
-33
lines changed

nada_algebra/array.py

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
Integer,
1919
UnsignedInteger,
2020
)
21-
from nada_algebra.types import Rational, SecretRational
21+
from nada_algebra.types import Rational, SecretRational, RationalConfig
2222

2323
_NadaOperand = Union[
2424
"NadaArray",
@@ -380,7 +380,12 @@ def array(
380380
party: Party,
381381
prefix: str,
382382
nada_type: Union[
383-
SecretInteger, SecretUnsignedInteger, PublicInteger, PublicUnsignedInteger
383+
SecretInteger,
384+
SecretUnsignedInteger,
385+
PublicInteger,
386+
PublicUnsignedInteger,
387+
SecretRational,
388+
Rational,
384389
] = SecretInteger,
385390
) -> "NadaArray":
386391
"""
@@ -394,22 +399,33 @@ def array(
394399
395400
Returns:
396401
NadaArray: The created NadaArray.
397-
"""
402+
403+
Raises:
404+
ValueError: Raised if the nada_type is not supported.
405+
"""
406+
generator = None
407+
if nada_type in (Rational, SecretRational):
408+
generator = lambda name, party: nada_type(name=name, party=party)
409+
elif nada_type in (
410+
SecretInteger,
411+
SecretUnsignedInteger,
412+
PublicInteger,
413+
PublicUnsignedInteger,
414+
):
415+
generator = lambda name, party: nada_type(Input(name=name, party=party))
416+
else:
417+
raise ValueError(f"Unsupported nada_type: {nada_type}")
418+
398419
return NadaArray(
399-
np.array(
400-
NadaArray.create_list(
401-
dims,
402-
party,
403-
prefix,
404-
lambda name, party: nada_type(Input(name=name, party=party)),
405-
)
406-
)
420+
np.array(NadaArray.create_list(dims, party, prefix, generator))
407421
)
408422

409423
@staticmethod
410424
def random(
411425
dims: list,
412-
nada_type: Union[SecretInteger, SecretUnsignedInteger] = SecretInteger,
426+
nada_type: Union[
427+
SecretInteger, SecretUnsignedInteger, SecretRational
428+
] = SecretInteger,
413429
) -> "NadaArray":
414430
"""
415431
Create a random NadaArray with the specified dimensions.
@@ -420,14 +436,21 @@ def random(
420436
421437
Returns:
422438
NadaArray: The created random NadaArray.
439+
440+
Raises:
441+
ValueError: Raised if the nada_type is not supported.
423442
"""
424-
return NadaArray(
425-
np.array(
426-
NadaArray.create_list(
427-
dims, None, None, lambda name, party: nada_type.random()
428-
)
443+
generator = None
444+
if nada_type is SecretRational:
445+
generator = lambda name, party: SecretRational.from_parts(
446+
SecretInteger.random(), RationalConfig.LOG_SCALE
429447
)
430-
)
448+
elif nada_type in (SecretInteger, SecretUnsignedInteger):
449+
generator = lambda name, party: nada_type.random()
450+
else:
451+
raise ValueError(f"Unsupported nada_type: {nada_type}")
452+
453+
return NadaArray(np.array(NadaArray.create_list(dims, None, None, generator)))
431454

432455
def __getattr__(self, name: str) -> Any:
433456
"""Routes other attributes to the inner NumPy array.

nada_algebra/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
PublicVariableUnsignedInteger,
1212
)
1313
import numpy as np
14-
from nada_algebra.types import RationalConfig
14+
from nada_algebra.types import RationalConfig, Rational, SecretRational
1515

1616

1717
def parties(num: int, prefix: str = "Party") -> list:
@@ -36,6 +36,8 @@ def array(
3636
SecretUnsignedInteger,
3737
PublicVariableInteger,
3838
PublicVariableUnsignedInteger,
39+
Rational,
40+
SecretRational,
3941
] = SecretInteger,
4042
) -> dict:
4143
"""

nada_algebra/funcs.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
and manipulation of arrays and party objects.
44
"""
55

6-
from typing import Any, Iterable
6+
from typing import Any, Iterable, Union
77
from nada_dsl import (
88
Party,
99
SecretInteger,
@@ -13,8 +13,14 @@
1313
Integer,
1414
UnsignedInteger,
1515
)
16+
1617
import numpy as np
1718
from nada_algebra.array import NadaArray
19+
from nada_algebra.types import Rational, SecretRational
20+
21+
22+
_NadaCleartextType = Union[Integer, UnsignedInteger, Rational]
23+
""" A Nada Cleartext Type is: `Integer`, `UnsignedInteger`, or `Rational` """
1824

1925

2026
def parties(num: int, prefix: str = "Party") -> list:
@@ -31,7 +37,7 @@ def parties(num: int, prefix: str = "Party") -> list:
3137
return [Party(name=f"{prefix}{i}") for i in range(num)]
3238

3339

34-
def __from_numpy(arr: np.ndarray, nada_type: Integer | UnsignedInteger) -> list:
40+
def __from_numpy(arr: np.ndarray, nada_type: _NadaCleartextType) -> list:
3541
"""
3642
Recursively convert a n-dimensional NumPy array to a nested list of NadaInteger objects.
3743
@@ -43,11 +49,13 @@ def __from_numpy(arr: np.ndarray, nada_type: Integer | UnsignedInteger) -> list:
4349
list: A nested list of NadaInteger objects.
4450
"""
4551
if len(arr.shape) == 1:
52+
if isinstance(nada_type, Rational):
53+
return [nada_type(elem) for elem in arr]
4654
return [nada_type(int(elem)) for elem in arr]
4755
return [__from_numpy(arr[i], nada_type) for i in range(arr.shape[0])]
4856

4957

50-
def from_list(lst: list, nada_type: Integer | UnsignedInteger = Integer) -> NadaArray:
58+
def from_list(lst: list, nada_type: _NadaCleartextType = Integer) -> NadaArray:
5159
"""
5260
Create a cleartext NadaArray from a list of integers.
5361
@@ -63,9 +71,7 @@ def from_list(lst: list, nada_type: Integer | UnsignedInteger = Integer) -> Nada
6371
return NadaArray(np.array(__from_numpy(lst, nada_type)))
6472

6573

66-
def ones(
67-
dims: Iterable[int], nada_type: Integer | UnsignedInteger = Integer
68-
) -> NadaArray:
74+
def ones(dims: Iterable[int], nada_type: _NadaCleartextType = Integer) -> NadaArray:
6975
"""
7076
Create a cleartext NadaArray filled with ones.
7177
@@ -80,7 +86,7 @@ def ones(
8086

8187

8288
def ones_like(
83-
a: np.ndarray | NadaArray, nada_type: Integer | UnsignedInteger = Integer
89+
a: np.ndarray | NadaArray, nada_type: _NadaCleartextType = Integer
8490
) -> NadaArray:
8591
"""
8692
Create a cleartext NadaArray filled with one with the same shape and type as a given array.
@@ -97,9 +103,7 @@ def ones_like(
97103
return from_list(np.ones_like(a), nada_type)
98104

99105

100-
def zeros(
101-
dims: Iterable[int], nada_type: Integer | UnsignedInteger = Integer
102-
) -> NadaArray:
106+
def zeros(dims: Iterable[int], nada_type: _NadaCleartextType = Integer) -> NadaArray:
103107
"""
104108
Create a cleartext NadaArray filled with zeros.
105109
@@ -114,7 +118,7 @@ def zeros(
114118

115119

116120
def zeros_like(
117-
a: np.ndarray | NadaArray, nada_type: Integer | UnsignedInteger = Integer
121+
a: np.ndarray | NadaArray, nada_type: _NadaCleartextType = Integer
118122
) -> NadaArray:
119123
"""
120124
Create a cleartext NadaArray filled with zeros with the same shape and type as a given array.
@@ -168,7 +172,11 @@ def array(
168172
party: Party,
169173
prefix: str,
170174
nada_type: (
171-
SecretInteger | SecretUnsignedInteger | PublicInteger | PublicUnsignedInteger
175+
SecretInteger
176+
| SecretUnsignedInteger
177+
| PublicInteger
178+
| PublicUnsignedInteger
179+
| SecretRational
172180
) = SecretInteger,
173181
) -> NadaArray:
174182
"""
@@ -188,7 +196,7 @@ def array(
188196

189197
def random(
190198
dims: Iterable[int],
191-
nada_type: SecretInteger | SecretUnsignedInteger = SecretInteger,
199+
nada_type: SecretInteger | SecretUnsignedInteger | SecretRational = SecretInteger,
192200
) -> NadaArray:
193201
"""
194202
Create a random NadaArray with the specified dimensions.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "nada-algebra"
3-
version = "0.2.0"
3+
version = "0.2.1"
44
description = ""
55
authors = ["José Cabrero-Holgueras <jose.cabrero@nillion.com>"]
66
readme = "README.md"

tests/nada-tests/nada-project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,8 @@ prime_size = 128
108108

109109
[[programs]]
110110
path = "src/chained_rational_operations.py"
111+
prime_size = 128
112+
113+
[[programs]]
114+
path = "src/rational_array.py"
111115
prime_size = 128
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from nada_dsl import *
2+
import nada_algebra as na
3+
4+
5+
def nada_main():
6+
parties = na.parties(2)
7+
8+
a = na.array([3], parties[0], "A", nada_type=na.SecretRational)
9+
b = na.array([3], parties[0], "B", nada_type=na.SecretRational)
10+
c = na.ones([3], na.Rational)
11+
12+
out_0 = a + b
13+
out_1 = a - b
14+
out_2 = a * b
15+
out_3 = a / b
16+
17+
out_4 = a + c
18+
out_5 = a - c
19+
out_6 = a * c
20+
out_7 = a / c
21+
22+
return (
23+
out_0.output(parties[1], "out_0")
24+
+ out_1.output(parties[1], "out_1")
25+
+ out_2.output(parties[1], "out_2")
26+
+ out_3.output(parties[1], "out_3")
27+
+ out_4.output(parties[1], "out_4")
28+
+ out_5.output(parties[1], "out_5")
29+
+ out_6.output(parties[1], "out_6")
30+
+ out_7.output(parties[1], "out_7")
31+
)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
program: rational_array
3+
inputs:
4+
secrets:
5+
A_0:
6+
SecretInteger: "0" # 0
7+
A_1:
8+
SecretInteger: "65536" # 1
9+
A_2:
10+
SecretInteger: "131072" # 2
11+
B_0:
12+
SecretInteger: "65536" # 1
13+
B_1:
14+
SecretInteger: "131072" # 2
15+
B_2:
16+
SecretInteger: "196608" # 3
17+
public_variables: {}
18+
expected_outputs:
19+
out_0_0:
20+
SecretInteger: "65536"
21+
out_0_1:
22+
SecretInteger: "196608"
23+
out_0_2:
24+
SecretInteger: "327680"
25+
out_1_0:
26+
SecretInteger: "-65536"
27+
out_1_1:
28+
SecretInteger: "-65536"
29+
out_1_2:
30+
SecretInteger: "-65536"
31+
out_2_0:
32+
SecretInteger: "0"
33+
out_2_1:
34+
SecretInteger: "131072"
35+
out_2_2:
36+
SecretInteger: "393216"
37+
out_3_0:
38+
SecretInteger: "0"
39+
out_3_1:
40+
SecretInteger: "32768"
41+
out_3_2:
42+
SecretInteger: "43690"
43+
out_4_0:
44+
SecretInteger: "65536"
45+
out_4_1:
46+
SecretInteger: "131072"
47+
out_4_2:
48+
SecretInteger: "196608"
49+
out_5_0:
50+
SecretInteger: "-65536"
51+
out_5_1:
52+
SecretInteger: "0"
53+
out_5_2:
54+
SecretInteger: "65536"
55+
out_6_0:
56+
SecretInteger: "0"
57+
out_6_1:
58+
SecretInteger: "65536"
59+
out_6_2:
60+
SecretInteger: "131072"
61+
out_7_0:
62+
SecretInteger: "0"
63+
out_7_1:
64+
SecretInteger: "65536"
65+
out_7_2:
66+
SecretInteger: "131072"

tests/test_all.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"secret_rational_arithmetic",
2929
"secret_rational_comparison",
3030
"chained_rational_operations",
31+
"rational_array",
3132
# Not supported yet
3233
# "unsigned_matrix_inverse",
3334
# "private_inverse"

0 commit comments

Comments
 (0)