def doit(logfile): lines = [l for l in CM.iread(logfile) if "Info:***" in l] rs = OrderedDict() for l in lines: name, nlocs, ninvs, ninps, ntime, nrand = l.split(',') name = name.split()[1].strip() nlocs = float(nlocs.split()[0].strip()) ninvs = float(ninvs.split()[1].strip()) ninps = float(ninps.split()[1].strip()) ntime = float(ntime.split()[1].strip()) print name, nlocs, ninvs, ninps, ntime if name not in rs: rs[name] = {'nlocs': [], 'ninvs': [], 'ninps': [], 'ntime': []} rs[name]['nlocs'].append(nlocs) rs[name]['ninvs'].append(ninvs) rs[name]['ninps'].append(ninps) rs[name]['ntime'].append(ntime) nruns = max(len(rs[name]['nlocs']) for name in rs) stats = {} for name in rs: stats[name] = {} for key in rs[name]: contents = rs[name][key] if len(contents) < nruns: maxv = max(contents) maxv = maxv * 100 contents.extend([maxv] * (nruns - len(contents))) medianc = median(contents) meanc = mean(contents) lenc = len(contents) stats[name][key] = (medianc, meanc, lenc) print('{} {} median {} mean {} len {}'.format( name, key, medianc, meanc, lenc)) for name in sorted(stats): invsd = stats[name]["ninvs"] timesd = stats[name]["ntime"] print name, invsd[0], timesd[0] return stats
def calibrate(self): r""" Run some test computations to estimate the optimal search size. Let `p` be the search size. We should simply choose `p` such that the average expected time is minimal. The algorithm succeeds when it chooses an information set with at least `k - p` correct positions, where `k` is the dimension of the code and `p` the search size. The expected number of trials we need before this occurs is: .. MATH:: \binom{n}{k}/(\rho \sum_{i=0}^p \binom{n-\tau}{k-i} \binom{\tau}{i}) Here `\rho` is the fraction of `k` subsets of indices which are information sets. If `T` is the average time for steps 1 and 2 (including selecting `I` until an information set is found), while `P(i)` is the time for the body of the ``for``-loop in step 3 for `m` of weight `i`, then each information set trial takes roughly time `T + \sum_{i=0}^{p} P(i) \binom{k}{i} (q-1)^i`, where `\GF{q}` is the base field. The values `T` and `P` are here estimated by running a few test computations similar to those done by the decoding algorithm. We don't explicitly estimate `\rho`. OUTPUT: Does not output anything but sets private fields used by :meth:`sage.coding.information_set_decoder.InformationSetAlgorithm.parameters()` and :meth:`sage.coding.information_set_decoder.InformationSetAlgorithm.time_estimate()``. EXAMPLES:: sage: from sage.coding.information_set_decoder import LeeBrickellISDAlgorithm sage: C = codes.GolayCode(GF(2)) sage: A = LeeBrickellISDAlgorithm(C, (0,3)); A ISD Algorithm (Lee-Brickell) for [24, 12, 8] Extended Golay code over GF(2) decoding up to 3 errors sage: A.calibrate() sage: A.parameters() #random {'search_size': 1} sage: A.time_estimate() #random 0.0008162108571427874 If we specify the parameter at construction time, calibrate does not override this choice:: sage: A = LeeBrickellISDAlgorithm(C, (0,3), search_size=2); A ISD Algorithm (Lee-Brickell) for [24, 12, 8] Extended Golay code over GF(2) decoding up to 3 errors sage: A.parameters() {'search_size': 2} sage: A.calibrate() sage: A.parameters() {'search_size': 2} sage: A.time_estimate() #random 0.0008162108571427874 """ from sage.all import sample, mean, random_vector, random_matrix, randint import time C = self.code() G = C.generator_matrix() n, k = C.length(), C.dimension() tau = self.decoding_interval()[1] F = C.base_ring() q = F.cardinality() Fstar = F.list()[1:] def time_information_set_steps(): before = time.clock() while True: I = sample(range(n), k) Gi = G.matrix_from_columns(I) try: Gi_inv = Gi.inverse() except ZeroDivisionError: continue return time.clock() - before def time_search_loop(p): y = random_vector(F, n) g = random_matrix(F, p, n).rows() scalars = [[Fstar[randint(0, q - 2)] for i in range(p)] for s in range(100)] before = time.clock() for m in scalars: e = y - sum(m[i] * g[i] for i in range(p)) errs = e.hamming_weight() return (time.clock() - before) / 100. T = mean([time_information_set_steps() for s in range(5)]) P = [time_search_loop(p) for p in range(tau + 1)] def compute_estimate(p): iters = 1.* binomial(n, k)/ \ sum( binomial(n-tau, k-i)*binomial(tau,i) for i in range(p+1) ) estimate = iters*(T + \ sum(P[pi] * (q-1)**pi * binomial(k, pi) for pi in range(p+1) )) return estimate if self._parameters_specified: self._time_estimate = compute_estimate( self._parameters['search_size']) else: self._calibrate_select( [compute_estimate(p) for p in range(tau + 1)])
def log_mean(X): return log(mean([abs(x) for x in X]), 2)
def estimate(nlen=256, m=85, klen=254, skip=None): """ Estimate the cost of solving HNP for an ECDSA with biased nonces instance. :param nlen: :param m: :param klen: :param compute: :returns: :rtype: EXAMPLES:: sage: estimate(256, m=85, klen=254) sage: estimate(160, m=85, klen=158) """ from usvp import solvers if skip is None: skip = [] ecdsa = ECDSA(nbits=nlen) klen_list = make_klen_list(klen, m) gh = ECDSASolver.ghf(m, ecdsa.n, klen_list, prec=nlen // 2) vol = ECDSASolver.volf(m, ecdsa.n, klen_list, prec=nlen // 2) target_norm = ECDSASolver.evf(m, max(klen_list), prec=nlen // 2) print( ("% {t:s} {h:s}, nlen: {nlen:3d}, m: {m:2d}, klen: {klen:.3f}").format( t=str(datetime.datetime.now()), h=socket.gethostname(), nlen=nlen, m=m, klen=float(mean(klen_list)) ) ) print(" E[|b[0]|]: 2^{v:.2f}".format(v=float(RR(log(gh, 2))))) print(" E[|v|]: 2^{v:.2f}".format(v=float(RR(log(target_norm, 2))))) print(" E[v]/E[b[0]]: %.4f" % float(target_norm / gh)) print("") for solver in solvers: if solver in skip: continue cost, params = solvers[solver].estimate((2 * log(vol), m + 1), target_norm ** 2) if cost is None: print(" {solver:20s} not applicable".format(solver=solver)) continue else: print( " {solver:20s} cost: 2^{c:.1f} cycles ≈ {t:12.4f}h, aux data: {params}".format( solver=solver, c=float(log(cost, 2)), t=cost / (2.0 * 10.0 ** 9 * 3600.0), params=params ) )
def benchmark( nlen=256, klen=128, m=2, e=0.0, tasks=8, algorithm=None, flavor="plain", d=None, jobs=1, parallelism=1, seed=None, solver_params=None, ): """ :param nlen: number of bits in the ECDSA key :param klen: number of known bits of the key :param m: number of available samples :param e: fraction of errors :param tasks: number of experiments to run :param algorithm: algorithm to use, see ``usvp.solvers`` :param flavor: higher-level strategy to use, see ``usvp.flavors`` :param d: lattice dimension (default: `m+1`) :param jobs: number of experiments to run in parallel :param parallelism: parallelism to use per experiment :param seed: randomness seed """ from usvp_prec_hack import usvp_pred_cut_n_sieve_solve from usvp import solvers if nlen > 384: logging.warning("% hotpatching with slower but more numerically stable `usvp_pred_cut_n_sieve_solve`.") solvers["sieve_pred"] = usvp_pred_cut_n_sieve_solve klen_list = make_klen_list(klen, m) tag = ZZ.random_element(x=0, y=2 ** 64) # we tag all outputs for easier matching if seed is None: seed = ZZ.random_element(x=0, y=2 ** 64) logging.warning( ( "% {t:s} {h:s} 0x{tag:016x} :: nlen: {nlen:3d}, m: {m:2d}, klen: {klen:.3f}, e: {e:.2f}, " "alg: {alg:s}, seed: 0x{seed:016x}, params: {params}" ).format( t=str(datetime.datetime.now()), h=socket.gethostname(), nlen=nlen, e=e, m=m, klen=float(mean(klen_list)), alg=str(algorithm), seed=seed, tag=tag, params=solver_params, ) ) pool = Pool(jobs) J = [ ComputeKernelParams( i=i, nlen=nlen, m=m, e=e, klen_list=klen_list, seed=seed + i, algorithm=algorithm, flavor=flavor, d=d, threads=parallelism, params=solver_params, tag=tag, ) for i in range(tasks) ] if jobs > 1: r = list(pool.imap_unordered(compute_kernel, J)) else: r = list(map(compute_kernel, J)) ecdsa = ECDSA(nbits=nlen) expected_target = ECDSASolver.evf(m, max(klen_list), prec=nlen // 2) expected_b0 = ECDSASolver.ghf(m, ecdsa.n, klen_list, prec=nlen // 2) successes = 0 B0 = [] eB0 = [] work = [] cputime = [] walltime = [] for key, res, targetvector_norm in r: successes += int(res.success) B0.append(float(targetvector_norm / res.b0)) eB0.append(float(expected_target / res.b0)) work.append(int(res.ntests)) cputime.append(float(res.cputime)) walltime.append(float(res.walltime)) logging.warning( ( "% {tm:s} {h:s} 0x{tag:016x} :: sr: {sr:3.0f}%, v/b[0]: {b0ratio:.3f}, " "E|v|/|b[0]|: {eb0r:.3f}, E|v|/E|b[0]|: {eveb:.3f}, work: {wk:d}, " "t: {t:.1f}s, w: {w:.1f}s" ).format( tm=str(datetime.datetime.now()), h=socket.gethostname(), sr=100 * float(successes / tasks), b0ratio=median(B0), eb0r=median(eB0), eveb=float(expected_target / expected_b0), wk=int(median(work)), t=median(cputime), w=median(walltime), tag=tag, ) ) return r
def calibrate(self): r""" Run some test computations to estimate the optimal search size. Let `p` be the search size. We should simply choose `p` such that the average expected time is minimal. The algorithm succeeds when it chooses an information set with at least `k - p` correct positions, where `k` is the dimension of the code and `p` the search size. The expected number of trials we need before this occurs is: .. MATH:: \binom{n}{k}/(\rho \sum_{i=0}^p \binom{n-\tau}{k-i} \binom{\tau}{i}) Here `\rho` is the fraction of `k` subsets of indices which are information sets. If `T` is the average time for steps 1 and 2 (including selecting `I` until an information set is found), while `P(i)` is the time for the body of the ``for``-loop in step 3 for `m` of weight `i`, then each information set trial takes roughly time `T + \sum_{i=0}^{p} P(i) \binom{k}{i} (q-1)^i`, where `\GF{q}` is the base field. The values `T` and `P` are here estimated by running a few test computations similar to those done by the decoding algorithm. We don't explicitly estimate `\rho`. OUTPUT: Does not output anything but sets private fields used by :meth:`sage.coding.information_set_decoder.InformationSetAlgorithm.parameters()` and :meth:`sage.coding.information_set_decoder.InformationSetAlgorithm.time_estimate()``. EXAMPLES:: sage: from sage.coding.information_set_decoder import LeeBrickellISDAlgorithm sage: C = codes.GolayCode(GF(2)) sage: A = LeeBrickellISDAlgorithm(C, (0,3)); A ISD Algorithm (Lee-Brickell) for [24, 12, 8] Extended Golay code over GF(2) decoding up to 3 errors sage: A.calibrate() sage: A.parameters() #random {'search_size': 1} sage: A.time_estimate() #random 0.0008162108571427874 If we specify the parameter at construction time, calibrate does not override this choice:: sage: A = LeeBrickellISDAlgorithm(C, (0,3), search_size=2); A ISD Algorithm (Lee-Brickell) for [24, 12, 8] Extended Golay code over GF(2) decoding up to 3 errors sage: A.parameters() {'search_size': 2} sage: A.calibrate() sage: A.parameters() {'search_size': 2} sage: A.time_estimate() #random 0.0008162108571427874 """ from sage.all import sample, mean, random_vector, random_matrix, randint import time C = self.code() G = C.generator_matrix() n, k = C.length(), C.dimension() tau = self.decoding_interval()[1] F = C.base_ring() q = F.cardinality() Fstar = F.list()[1:] def time_information_set_steps(): before = time.clock() while True: I = sample(range(n), k) Gi = G.matrix_from_columns(I) try: Gi_inv = Gi.inverse() except ZeroDivisionError: continue return time.clock() - before def time_search_loop(p): y = random_vector(F, n) g = random_matrix(F, p, n).rows() scalars = [ [ Fstar[randint(0,q-2)] for i in range(p) ] for s in range(100) ] before = time.clock() for m in scalars: e = y - sum(m[i]*g[i] for i in range(p)) errs = e.hamming_weight() return (time.clock() - before)/100. T = mean([ time_information_set_steps() for s in range(5) ]) P = [ time_search_loop(p) for p in range(tau+1) ] def compute_estimate(p): iters = 1.* binomial(n, k)/ \ sum( binomial(n-tau, k-i)*binomial(tau,i) for i in range(p+1) ) estimate = iters*(T + \ sum(P[pi] * (q-1)**pi * binomial(k, pi) for pi in range(p+1) )) return estimate if self._parameters_specified: self._time_estimate = compute_estimate(self._parameters['search_size']) else: self._calibrate_select([ compute_estimate(p) for p in range(tau+1) ])