示例#1
0
def mat_to_float(m, rnum):
    nrow = len(m)
    values = [r(rnum, i) for i in range(rnum)]
    return sum([
        sum([m[i][j] * values[j] for j in range(rnum)]) * r(nrow, i)
        for i in range(nrow)
    ])
def expr_to_float(expr):
    if type(expr) == int:
        return float(expr)
    if type(expr) == tuple:
        return expr_to_float(expr[0]) / expr_to_float(expr[1])
    if is_sum(expr):
        return sum([expr_to_float(part) for part in expr[1:]])
    if is_radical(expr):
        degree = expr[1]
        which = expr[2]
        radicand = expr[3]
        mult = expr[4]
        # make sure rounding errors don't affect the choice of nth root
        radicand_float = expr_to_float(radicand)
        if radicand_float.real < 0 and float_equal(radicand_float,
                                                   radicand_float.real):
            radicand_float = radicand_float.real
        return mult * r(degree, which) * radicand_float**(1 / degree)
示例#3
0
def roots_to_radicals(rnum):
    if rnum in r_x:
        return r_x[rnum]

    # prime factors of n - 1
    # to match the expressions I found in 2013, factors are placed in increasing order except for one 2 at the end.
    # the last factor needs to be 2 for the real and imaginary parts of the final answer to be separable
    # other than that, any permutation of factors leads to an equally valid, distinct expression
    facts = factorize(rnum // 2) + [2]
    # multiplicative cycle of some primitive root, mod n
    power_cycle = make_power_cycle(rnum)
    # n_terms: number of a-sums on each iteration (see below)
    n_terms = 1
    # all_a: list of all lists of a-sums (see below for what an a-sum is) from each loop iteration
    # all_a_x: expressions for all a-sums
    all_a = []
    all_a_x = []

    for i, c_size in enumerate(facts):
        # calculates:
        # - p_1 sums of (n - 1)/p_1 roots
        # - p_1*p_2 sums of (n - 1)/(p_1*p_2) roots
        # - p_1*p_2*p_3 sums of (n - 1)/(p_1*p_2*p_3) roots
        # etc, where a, b, c... are prime factors of n - 1
        # let p_k = c_size be the prime factor of n - 1 considered on the kth iteration
        n_terms *= c_size

        # a: list of sums of (n - 1)/(p_1*...*p_k) roots (hereafter called a-sums) stored as lists
        #   these sums can be grouped into (n - 1)/(p_1*...*p_{k - 1}) sets of p_k
        # a_x: expressions for a-sums in a
        # s: for each set of p_k a-sums a_0 to a_{p_k - 1}, a list of p_k sums (stored as lists or matrices where appropriate), where the i-th sum s_i is equal to a_j*r(p_k, i*j), for j from 0 to p_k - 1
        #   s is simply a list of all p_k sums for all (n - 1)/(p_1*...*p_{k - 1}) sets of a-sums
        # s_x: expressions for s-sums in s
        # ss: contains each s-sum, except for the first value in a p_k-size set, raised to the power p_k, expressed as lists
        # ss_coeff: coefficients in expression, in terms of 1 and a-sum values from p_{k - 1} level, for each value in ss
        # ss_x: expressions for values in ss
        a = []
        a_x = []
        s = []
        ss = []
        ss_coeff = []
        s_x = []
        ss_x = []

        a_values = partition(rnum, power_cycle, n_terms)

        cyc = make_cycle(c_size)

        # iterate over each set of p_k terms
        for h in multi_range(tuple(facts[:i])):
            # construct a-sums
            part_ind = 0
            mult = 1
            for j in range(i):
                part_ind += mult * h[j]
                mult *= facts[j]
            for j in range(c_size):
                a.append(a_values[j * (n_terms // c_size) + part_ind])

            # construct s-sums and ss values
            offset = 0
            mult = c_size
            for j in range(i - 1, -1, -1):
                offset += mult * h[j]
                mult *= facts[j]
            s.append(sumlist(a[offset:offset + c_size], rnum))
            ss.append(None)
            for j in range(1, c_size):
                if c_size == 2:
                    s.append(
                        plus(a[offset], itimes(-1, a[offset + 1], rnum), rnum))
                    ss.append(power(s[offset + 1], c_size, rnum))
                else:
                    s.append(
                        msumlist([
                            xtimes(cyc[j * k % c_size], a[offset + k], rnum)
                            for k in range(c_size)
                        ], rnum))
                    ss.append(mpower(s[offset + j], c_size, rnum))

            # construct terms with which to describe each ss expression as well as the 0th s-sum in each set.
            # these terms are 1 and the a-sums from the previous iteration, whose radical expressions are already found
            if i > 0:
                terms = [1] + all_a[i - 1]
            else:
                terms = [1]
            # build s0_coeff and ss_coeff
            s0_coeff = deconvert(s[offset], terms, rnum)
            ss_coeff.append(None)
            if c_size == 2:
                ss_coeff.append(deconvert(ss[offset + 1], terms, rnum))
            else:
                for j in range(1, c_size):
                    ss_coeff.append(mdeconvert(ss[offset + j], terms, rnum))

            # retrieve the radical expression for each term used to construct s0_coeff and ss_coeff
            if i > 0:
                terms_x = [1] + all_a_x[i - 1]
            else:
                terms_x = [1]
            # build s0_x and ss_x
            # s0_x is the 0th s_x expression in each set but not the 0th member of s_x because s_x concatenates expression lists from every set on the p_k level
            s0_x = coeff_to_expr(s0_coeff, terms_x)
            ss_x.append(None)
            if c_size == 2:
                ss_x.append(coeff_to_expr(ss_coeff[offset + 1], terms_x))
            else:
                for j in range(1, c_size):
                    ss_x.append(
                        mcoeff_to_expr(ss_coeff[offset + j], terms_x,
                                       roots_to_radicals(c_size)))

            s_x.append(s0_x)

            # build the other s_x expressions (the p_k-th roots of the ss_x expressions)
            for j in range(1, c_size):
                si_xs = [['r', c_size, k, ss_x[offset + j], 1]
                         for k in range(c_size)]
                set = False
                for k in range(c_size):
                    if c_size == 2:
                        s_j_float = to_float(s[offset + j], rnum)
                    else:
                        s_j_float = mat_to_float(s[offset + j], rnum)
                    if float_equal(s_j_float, expr_to_float(si_xs[k])):
                        s_x.append(si_xs[k])
                        set = True
                        break
                if not set:
                    raise RuntimeError("Unable to resolve expression")

            # build the a_x expressions by adding the s_x expressions together and multiplying by the appropriate p_k-th roots of unity
            for j in range(c_size):
                a_x.append(
                    etimes(esumlist(s_x[offset:offset + c_size]), (1, c_size)))
                for k in range(1, c_size):
                    s_x[offset + k] = rtimes(-k, s_x[offset + k])

        all_a.append(a)
        all_a_x.append(a_x)

    # on last iteration, a-sums are single roots of unity. bit_reverse() reorders these roots
    # x: list of expressions for all n-th roots of unity in order
    x = [1] + [None] * (rnum - 1)
    for i in range(len(a_x)):
        xind = power_cycle[bit_reverse(i, facts)]
        x[xind] = a_x[i]
        assert float_equal(expr_to_float(a_x[i]), r(rnum, xind))
    r_x[rnum] = x
    return x
示例#4
0
def to_float(l, rnum):
    values = [r(rnum, i) for i in range(rnum)]
    return sum([l[i] * values[i] for i in range(rnum)])