Random fixed-point number generation in binary circuits.

This commit is contained in:
Marcel Keller
2025-05-06 12:40:09 +10:00
parent 6f3c719f75
commit 98251fc101
2 changed files with 54 additions and 44 deletions

View File

@@ -841,6 +841,16 @@ class sbitvec(_vec, _bit, _binary):
return self.from_vec(x.zero_if_not(condition) for x in self.v)
def __str__(self):
return 'sbitvec(%d)' % n
@classmethod
def get_random_int(cls, n_bits):
assert instructions_base.get_global_vector_size() == 1
return cls.from_vec(
[sbit.get_random_bit() for i in range(n_bits)] + \
[0] * (n - n_bits))
@staticmethod
def get_random_bit():
assert instructions_base.get_global_vector_size() == 1
return sbit.get_random_bit()
sbitvecn.basic_type = sbitvecn
sbitvecn.reg_type = 'sb'
return sbitvecn

View File

@@ -4853,6 +4853,50 @@ class _fix(_single):
assert self.f == other.f
self.v.update(other.v)
@vectorized_classmethod
def get_random(cls, lower, upper, symmetric=True, public_randomness=False):
""" Uniform secret random number around centre of bounds.
Actual range can be smaller but never larger.
:param lower: float
:param upper: float
:param symmetric: symmetric distribution at higher cost
:param public_randomness: use public randomness (avoids preprocessing)
:param size: vector size (int, default 1)
"""
if public_randomness:
get_random_int = regint.get_random
get_random_bit = lambda: regint.get_random(1)
else:
get_random_int = cls.int_type.get_random_int
get_random_bit = cls.int_type.get_random_bit
f = cls.f
k = cls.k
log_range = int(math.log(upper - lower, 2))
n_bits = log_range + cls.f
gen_range = (2 ** (n_bits) - 1) / 2 ** cls.f
diff = upper - lower
factor = diff / gen_range
real = lambda x: cfix.int_rep(x, f, k) * 2 ** -f
real_range = real(real(factor) * gen_range)
average = lower + 0.5 * (upper - lower)
lower = average - 0.5 * real_range
upper = average + 0.5 * real_range
r = cls._new(get_random_int(n_bits)) * factor + lower
if symmetric:
lowest = math.floor(lower * 2 ** cls.f) / 2 ** cls.f
highest = math.ceil(upper * 2 ** cls.f) / 2 ** cls.f
if program.verbose:
print('randomness range [%f,%f], '
'fringes half the probability' % \
(lowest, highest))
return get_random_bit().if_else(r, -r + 2 * average)
else:
if program.verbose:
print('randomness range [%f,%f], %d bits' % \
(real(lower), real(lower) + real_range, n_bits))
return r
class sfix(_fix):
""" Secret fixed-point number represented as secret integer, by
multiplying with ``2^f`` and then rounding. See :py:class:`sint`
@@ -4904,50 +4948,6 @@ class sfix(_fix):
def get_raw_input_from(cls, player):
return cls._new(cls.int_type.get_raw_input_from(player))
@vectorized_classmethod
def get_random(cls, lower, upper, symmetric=True, public_randomness=False):
""" Uniform secret random number around centre of bounds.
Actual range can be smaller but never larger.
:param lower: float
:param upper: float
:param symmetric: symmetric distribution at higher cost
:param public_randomness: use public randomness (avoids preprocessing)
:param size: vector size (int, default 1)
"""
if public_randomness:
get_random_int = regint.get_random
get_random_bit = lambda: regint.get_random(1)
else:
get_random_int = cls.int_type.get_random_int
get_random_bit = cls.int_type.get_random_bit
f = cls.f
k = cls.k
log_range = int(math.log(upper - lower, 2))
n_bits = log_range + cls.f
gen_range = (2 ** (n_bits) - 1) / 2 ** cls.f
diff = upper - lower
factor = diff / gen_range
real = lambda x: cfix.int_rep(x, f, k) * 2 ** -f
real_range = real(real(factor) * gen_range)
average = lower + 0.5 * (upper - lower)
lower = average - 0.5 * real_range
upper = average + 0.5 * real_range
r = cls._new(get_random_int(n_bits)) * factor + lower
if symmetric:
lowest = math.floor(lower * 2 ** cls.f) / 2 ** cls.f
highest = math.ceil(upper * 2 ** cls.f) / 2 ** cls.f
if program.verbose:
print('randomness range [%f,%f], '
'fringes half the probability' % \
(lowest, highest))
return get_random_bit().if_else(r, -r + 2 * average)
else:
if program.verbose:
print('randomness range [%f,%f], %d bits' % \
(real(lower), real(lower) + real_range, n_bits))
return r
@classmethod
def direct_matrix_mul(cls, A, B, n, m, l, reduce=True, indices=None):
# pre-multiplication must be identity