Skip to content

Commit

Permalink
Two kw args added.
Browse files Browse the repository at this point in the history
Extra kw arg _SecNum and random_bit(s).
  • Loading branch information
lschoe committed Sep 20, 2018
1 parent e793495 commit 28ca4d1
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 42 deletions.
31 changes: 15 additions & 16 deletions demos/cnnmnist.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def _dim(x):

@mpc.coroutine
async def convolvetensor(x, W, b):
logging.info("- - - - - - - - conv2d - - - - - - -")
logging.info('- - - - - - - - conv2d - - - - - - -')
# 2D convolutions on m*n sized images from X with s*s sized filters from W.
# b is of dimension v
k, r, m, n = dim(x)
Expand Down Expand Up @@ -96,12 +96,12 @@ def inprod2D(X, W):
return Y

def tensormatrix_prod(x, W, b):
logging.info("- - - - - - - - fc - - - - - - -")
logging.info('- - - - - - - - fc - - - - - - -')
W, b = W.tolist(), b.tolist()
return [mpc.vector_add(mpc.matrix_prod([z.tolist()], W)[0], b) for z in x]

def maxpool(x):
logging.info("- - - - - - - - maxpool - - - - - - -")
logging.info('- - - - - - - - maxpool - - - - - - -')
# maxpooling 2 * 2 squares in images of size m * n with stride 2
k, r, m, n = dim(x)
Y = [[[[mpc.max(y[i][j], y[i][j+1], y[i+1][j], y[i+1][j+1])
Expand All @@ -110,18 +110,17 @@ def maxpool(x):
return np.array(Y)

def ReLU(x):
logging.info("- - - - - - - - ReLU - - - - - - -")
logging.info('- - - - - - - - ReLU - - - - - - -')
return np.vectorize(lambda a: (a >= 0) * a)(x)

def argmax(x):
stype = type(x[0])
a = stype(0)
a = type(x[0])(0)
m = x[0]
for i in range(1, len(x)):
b = m >= x[i]
a = b * (a - i) + i
m = b * (m - x[i]) + x[i]
return a, m
return a

def main():
global secnum
Expand All @@ -143,8 +142,8 @@ def main():

mpc.start()

logging.info("--------------- INPUT -------------")
print('SecNum type = %s, range = (%d, %d)' % (secnum.__name__, offset, offset + k))
logging.info('--------------- INPUT -------------')
print(f'Type = {secnum.__name__}, range = ({offset}, {offset + batch_size})')
# read batch_size labels and images at given offset
df = gzip.open(os.path.join('data', 'cnn', 't10k-labels-idx1-ubyte.gz'))
d = df.read()[8 + offset: 8 + offset + batch_size]
Expand All @@ -155,10 +154,10 @@ def main():
x = list(map(lambda a: a / 255, d))
x = np.array(x).reshape(batch_size, 1, 28, 28)
if batch_size == 1:
print(np.vectorize(lambda a: int(bool(a)))(x))
print(np.vectorize(lambda a: int(bool(a)))(x[0,0]))
x = scale_to_int(1 << f)(x)

logging.info("--------------- LAYER 1 -------------")
logging.info('--------------- LAYER 1 -------------')
W, b = load('conv1', f)
x = convolvetensor(x, W, b)
mpc.run(mpc.barrier())
Expand All @@ -169,7 +168,7 @@ def main():
x = ReLU(x)
mpc.run(mpc.barrier())

logging.info("--------------- LAYER 2 -------------")
logging.info('--------------- LAYER 2 -------------')
W, b = load('conv2', f, 3)
x = convolvetensor(x, W, b)
mpc.run(mpc.barrier())
Expand All @@ -180,7 +179,7 @@ def main():
x = ReLU(x)
mpc.run(mpc.barrier())

logging.info("--------------- LAYER 3 -------------")
logging.info('--------------- LAYER 3 -------------')
x = x.reshape(batch_size, 64 * 7**2)
W, b = load('fc1', f, 4)
x = tensormatrix_prod(x, W, b)
Expand All @@ -189,15 +188,15 @@ def main():
x = ReLU(x)
mpc.run(mpc.barrier())

logging.info("--------------- LAYER 4 -------------")
logging.info('--------------- LAYER 4 -------------')
W, b = load('fc2', f, 5)
x = tensormatrix_prod(x, W, b)

logging.info("--------------- OUTPUT -------------")
logging.info('--------------- OUTPUT -------------')
if secnum.__name__.startswith('SecInt'):
secnum.bit_length = 37
for i in range(batch_size):
print(labels[i], mpc.run(mpc.output(argmax(x[i])[0])))
print(labels[i], mpc.run(mpc.output(argmax(x[i]))))
print(mpc.run(mpc.output(x[i])))

mpc.shutdown()
Expand Down
2 changes: 1 addition & 1 deletion demos/lpsolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def main():
l = mpc.options.bit_length
m = len(T) - 1
n = len(T[0]) - 1
secint = mpc.SecInt(l, m + n)
secint = mpc.SecInt(l, n=m + n)
for i in range(len(T)):
for j in range(len(T[0])):
T[i][j] = secint(T[i][j])
Expand Down
2 changes: 1 addition & 1 deletion mpyc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
are provided as well (e.g., some matrix-vector operations).
"""

__version__ = '0.3.3'
__version__ = '0.3.4'
__license__ = 'Apache License 2.0'
18 changes: 12 additions & 6 deletions mpyc/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ async def _recombine(self, a, receivers, threshold):
a = tuple([a])
sftype = type(a[0]) # all elts assumed of same type
if issubclass(sftype, Share):
if sftype.field.frac_length == 0:
if sftype.field.frac_length == 0:
await returnType(sftype)
else:
await returnType((sftype, a[0].integral))
Expand Down Expand Up @@ -848,12 +848,12 @@ def randoms(self, sftype, m, max=None):
else:
return shares

def random_bit(self, sftype):
def random_bit(self, sftype, signed=False):
"""Secure random bit of the given type."""
return self.random_bits(sftype, 1)[0]
return self.random_bits(sftype, 1, signed)[0]

@mpc_coro
async def random_bits(self, sftype, m):
async def random_bits(self, sftype, m, signed=False):
"""m secure random bits of the given type."""
prss0 = False
f1 = 1
Expand All @@ -869,7 +869,8 @@ async def random_bits(self, sftype, m):

bits = [None] * m
p = field.modulus
q = (p + 1) >> 1 # q = 1/2 mod p
if not signed:
q = (p + 1) >> 1 # q = 1/2 mod p
prfs = self.parties[self.id].prfs(p)
h = m
while h > 0:
Expand All @@ -884,7 +885,12 @@ async def random_bits(self, sftype, m):
for r, r2 in zip(rs, r2s):
if r2.value != 0:
h -= 1
bits[h] = field(f1 * ((r.value * r2.sqrt(INV=True).value + 1) % p) * q)
s = r.value * r2.sqrt(INV=True).value
if not signed:
s += 1
s %= p
s *= q
bits[h] = field(f1 * s)
return bits

def add_bits(self, x, y):
Expand Down
46 changes: 28 additions & 18 deletions mpyc/sectypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,17 @@ def __init__(self, value=None):
super().__init__(field, value)
SecureFld.field = field
SecureFld.bit_length = l
name = 'SecFld' + str(SecureFld.bit_length) + '(' + str(SecureFld.field.modulus) + ')'
name = f'SecFld{SecureFld.bit_length}({SecureFld.field.modulus})'
_sectypes[(l, p)] = type(name, (SecureFld,), {'__slots__':()})
return _sectypes[(l, p)]

def _SecNum(l, f, n=2):
def _SecNum(l, f, p, n):
k = Share.runtime.options.security_parameter
field = pfield.GF(pfield.find_prime_root(l + max(f, k + 1) + 1, n=n), f)
if p is None:
p = pfield.find_prime_root(l + max(f, k + 1) + 1, n=n)
else:
assert p.bit_length() > l + k, 'Prime too small.'
field = pfield.GF(p, f)

class SecureNum(Share):
__slots__ = ()
Expand All @@ -226,18 +230,21 @@ def __init__(self, value=None):
SecureNum.bit_length = l
return SecureNum

def SecInt(l=None, n=2):
def SecInt(l=None, p=None, n=2):
"""Secure l-bit integers."""
if l is None:
l = Share.runtime.options.bit_length

if (l, 0, n) not in _sectypes:
SecureInt = _SecNum(l, 0, n)
name = 'SecInt' + str(SecureInt.bit_length)
_sectypes[(l, 0, n)] = type(name, (SecureInt,), {'__slots__':()})
return _sectypes[(l, 0, n)]
if (l, 0, p, n) not in _sectypes:
SecureInt = _SecNum(l, 0, p, n)
if p is None:
name = f'SecInt{l}'
else:
name = f'SecInt{l}({p})'
_sectypes[(l, 0, p, n)] = type(name, (SecureInt,), {'__slots__':()})
return _sectypes[(l, 0, p, n)]

def SecFxp(l=None, f=None, n=2):
def SecFxp(l=None, f=None, p=None, n=2):
"""Secure l-bit fixed-point numbers with f-bit fractional part.
NB: if dividing secure fixed-point numbers, make sure that l =~ 2f.
Expand All @@ -247,20 +254,23 @@ def SecFxp(l=None, f=None, n=2):
if f is None:
f = l // 2 # l =~ 2f enables division such that x =~ 1/(1/x)

if (l, f, n) not in _sectypes:
SecureFxp = _SecNum(l, f, n)
name = 'SecFxp' + str(SecureFxp.bit_length) + ':' + str(SecureFxp.field.frac_length)
if (l, f, p, n) not in _sectypes:
SecureFxp = _SecNum(l, f, p, n)
if p is None:
name = f'SecFxp{l}:{f}'
else:
name = f'SecFxp{l}:{f}({p})'
def init(self, value=None, integral=False):
if value is not None:
self.integral = isinstance(value, int)
if self.integral:
value <<= SecureFxp.field.frac_length
value <<= f
elif isinstance(value, float):
value = round(value * (1 << SecureFxp.field.frac_length))
value = round(value * (1 << f))
else:
self.integral = integral
else:
self.integral = integral
super(_sectypes[(l, f, n)], self).__init__(value)
_sectypes[(l, f, n)] = type(name, (SecureFxp,), {'__slots__':'integral', '__init__':init})
return _sectypes[(l, f, n)]
super(_sectypes[(l, f, p, n)], self).__init__(value)
_sectypes[(l, f, p, n)] = type(name, (SecureFxp,), {'__slots__':'integral', '__init__':init})
return _sectypes[(l, f, p, n)]
8 changes: 8 additions & 0 deletions tests/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def test_secfld(self):
a > b
with self.assertRaises(TypeError):
a >= b
b = mpc.random_bit(secfld)
self.assertIn(mpc.run(mpc.output(b)), [0,1])
b = mpc.random_bit(secfld, signed=True)
self.assertIn(mpc.run(mpc.output(b)), [-1,1])

def test_secint(self):
secint = mpc.SecInt()
Expand Down Expand Up @@ -80,6 +84,10 @@ def test_secint(self):
self.assertEqual(mpc.run(mpc.output(mpc.lsb(secint(-50)))), 0)

self.assertEqual(mpc.run(mpc.output(secint(3)**73)), 3**73)
b = mpc.random_bit(secint)
self.assertIn(mpc.run(mpc.output(b)), [0,1])
b = mpc.random_bit(secint, signed=True)
self.assertIn(mpc.run(mpc.output(b)), [-1,1])

def test_secfxp(self):
for f in [8, 16, 32, 64]:
Expand Down

0 comments on commit 28ca4d1

Please sign in to comment.