lschoe / mpyc

MPyC: Multiparty Computation in Python
MIT License
367 stars 76 forks source link

mpc.trunc not accurate for values of f > 3 #88

Closed AlessandroAmadoriTNO closed 4 months ago

AlessandroAmadoriTNO commented 4 months ago

Hello @lschoe

I hope you are doing well 😄

I was testing around the mpc.trunc function and I noticed that the result for "high" values of f does not return an accurate result:

This is the code I used to test it

import math
from random import randint

from mpyc.runtime import mpc

secint = mpc.SecInt(l=32)

async def main():
    test_number = randint(0, 10**9)
    async with mpc:
        sec_number = mpc.input(secint(test_number), senders=0)

        scaling_factors = range(0, int(math.log2(abs(test_number))))
        output_value = await mpc.output(sec_number)

        for f in scaling_factors:
            print("--------------------------")
            sec_truncated_number = await mpc.output(mpc.trunc(sec_number, f))
            clear_rounded_number = round(test_number / 2**f)
            print(f"{output_value=}")
            print(f"Non Rounded Output: {test_number /2**f}")
            print(f"Scaling factor f = {f}")
            print(f"{clear_rounded_number=}")
            print(f"{sec_truncated_number=}")

if __name__ == "__main__":
    mpc.run(main())

and already for f> 3 the output was a very large negative number completely off from what I expected. For example:

output_value=320134517
Non Rounded Output: 1250525.45703125
Scaling factor f = 8
clear_rounded_number=1250525
sec_truncated_number=-1080863910567668503

I played around with the source code in runtime and I noticed that in https://github.com/lschoe/mpyc/blob/master/mpyc/runtime.py#L794

the computation works correctly until the right shift is performed. Does it have to do with the fact that the element a-c+r.value is a finite field element and not an integer?

I get the same behavior for mpyc 0.9 and 0.10 and I have tested it with 1 and 3 players

Thank you!

lschoe commented 4 months ago

Hi @AlessandroAmadoriTNO, interesting question!

You are trying to use mpc.trunc() with secure integers rather than with secure fixed-point numbers. That's not working currently, as it is designed for use with secure fixed-point numbers. This has to do with the available headroom. For secure integers, the headroom is k bits, for statistical security parameter k. For secure fixed-point numbers the headroom is k+f bits, so with an extra f bits. You are experiencing overflows (wrap arounds modulo the prime of the underlying finite field) because of the smaller headroom for secure integers.

To get better results, while working with secure integers still, you can set secint as follows:

secint = mpc.SecInt(l=64)
p = secint.field.modulus  # grab the extra large prime modulus 
secint = mpc.SecInt(l=32, p=p)  # sets bit length to l and uses the given (oversized) p

Or, you can work with secure fixed-point numbers, but then of double bit length to make your test numbers fit as integers, setting secint as follows:

secint = mpc.SecFxp(l=64)

This way mpc.trunc() still returns correct results, I think.

And, also you can use the keyword parameter l to mpc.trunc() by changing your call in the above code to mpc.trunc(sec_number, f, 32) and this time setting secint = mpc.SecInt(64) to make things fit with the 0 to 10**9 range you are using in your tests.

lschoe commented 4 months ago

I have a made a small adjustment to mpc.trunc() (and also to mpc.mp_trunc()) to make it work for secure integers as well. You can now directly run your test program above to get the desired results.

To make sure all parties do the same number of iterations, change the two lines in the middle of the program to:

        output_value = await mpc.output(sec_number)
        scaling_factors = range(0, int(math.log2(abs(output_value))))
RobertWezemanTNO commented 4 months ago

Perfect! I tested it and it seems to fix our issues. Thank you for the quick fix.

Will a new release v0.11 be made?

lschoe commented 4 months ago

OK, nice.

There will be a new release maybe in a few months. For the latest version install mpyc directly from GitHub.