def MajorDistribution(P): from sage.combinat.posets.posets import FinitePoset if not isinstance(P, FinitePoset): # An error here means we cannot interpret input as a poset P = FinitePoset(P) n = P.cardinality() relations = [] zero_vec = tuple([0 for i in range(n + 1)]) # Basic function: add k to the i-th component of v. def add_k_i(v, i, k): u = list(v) u[i] += k return tuple(u) # nonnegative relations. relations += [add_k_i(zero_vec, i, 1) for i in range(1, n + 1)] poset_edges = P.cover_relations() rel_to_vec = lambda x: add_k_i(add_k_i(zero_vec, x[0] + 1, 1), x[1] + 1, -1 ) relations += map(rel_to_vec, poset_edges) Poly = _Polyhedron(ieqs=relations) R = _PolynomialRing(_QQ, 'x', n) sm = _Zeta_smurf.from_polyhedron(Poly, R) T = _var('t') z = sm.evaluate(variables=tuple([T] * n)) N, D = z.numerator_denominator() stan_denom = reduce(lambda x, y: x * (1 - T**y), range(1, n + 1), 1) fact = stan_denom / D QT = _PolynomialRing(_QQ, T) assert fact in QT return (N * QT(fact)).expand()
def _eval_relations(relations, verbose, variable, sub): n = len(relations[0]) - 1 # In case the user wants to verify the matrix. if verbose: print("The matrix corresponding to the polyhedral cone:") print("%s" % (_Matrix(relations))) # Define the polyhedral cone and corresponding polynomial ring. P = _Polyhedron(ieqs=relations) R = _PolynomialRing(_QQ, 'x', n) # Define substitution. if sub: t = _var(variable) if n > 1: subs = {_var('x' + str(i)): t for i in range(n)} else: subs = {_var('x'): t} # Apply Zeta sm = _Zeta_smurf.from_polyhedron(P, R) Z = sm.evaluate().subs(subs).factor().simplify() else: # Apply Zeta sm = _Zeta_smurf.from_polyhedron(P, R) Z = sm.evaluate().factor().simplify() return Z
def _get_terms(f, varbs): P = _PolynomialRing(_QQ, varbs, len(varbs)) f_str = str(_expand(f)).replace(' ', '').replace('-', '+(-1)*') if f_str[0] == '+': f_str = f_str[1:] terms = f_str.split('+') induce = lambda x: P(x) return list(map(induce, terms))
def _is_fin_geo(f, varbs): terms = _get_terms(f, varbs) n = len(terms) if n == 1: return (False, 0, 0) # Determine any constant factors a = terms[-1].coefficients()[0] assert a != 0 P = _PolynomialRing(_QQ, varbs) check = lambda x: bool(_symb(f/x) in P) if a < 0: D = sorted(_ZZ(-a).divisors())[::-1] c = -filter(check, D)[0] else: D = sorted(_ZZ(a).divisors())[::-1] c = filter(check, D)[0] # Factor out the constant and check if it is already a finite geo series. g = P(c**-1 * _symb(f)) terms = _get_terms(1 - g, varbs) if len(terms) == 1: if terms[0].coefficients()[0] > 0: assert bool(f == g/c), "Something happened while determining geometric series." return (True, g, c) else: assert bool(f == (1 - terms[0]**2)/(2 - g)), "Something happened while determining geometric series." return (True, 1 - terms[0]**2, 2 - g) # Now check if it could be one. terms = _get_terms(g, varbs) tot_degs = list(map(lambda x: x.degree(), terms)) sorted_degs, sorted_terms = zip(*sorted(zip(tot_degs, terms))) t = filter(lambda x: x.degree() == sorted_degs[1], terms)[0] pattern_check = lambda x: x[0] == t**(x[1]) if all(map(pattern_check, zip(sorted_terms, range(n)))): assert bool(f == (1 - t**n)/(1 - t)), "Something happened while determining geometric series." return (True, 1 - t**n, 1 - t) return (False, 0, 0)
def _clean_denom_data(denom, data): merge = lambda X: reduce(lambda x, y: x*y[0]**y[1], X, 1) c = _decide_if_same(denom, merge(data[1])) if c != 0 : return data # In this case, we know that the data does not match the denominator P = _PolynomialRing(_QQ, denom.variables(), len(denom.variables())) f = merge(data[1])/denom if f in P: # Now we need to factor f out from both the denominator and numerator f = _symb(P(f)) fl = f.factor_list() num_fact = 1 denom_temp = data[1] for fact_dat in fl: g = fact_dat[0] for _ in range(fact_dat[1]): num, denom_temp = _find_factor(denom_temp, g, P) num_fact *= num numerator = (merge(data[0])*_symb(num_fact) / f).factor() return [numerator.factor_list(), denom_temp] else: raise AssertionError("Something unexpected happened while formatting.")
def _format(Z, denom=None, num_fact=False): # Clean up Z Z = Z.simplify() n = Z.numerator() d = Z.denominator() varbs = Z.variables() P = _PolynomialRing(_ZZ, varbs, len(varbs)) # If it's a string do not change it. if isinstance(denom, str): denom_str = denom denom = _symb(denom) else: denom_str = None # If there is a prescribed denominator, make sure we can handle it. given_denom = denom != None if given_denom: Q = denom/d if not Q in P: raise ValueError("Expected the denominator to divide the prescribed denominator.") n *= P(Q) else: denom = d # Two steps now. Get the denominator into a nice form: a product of (1 - M), # where M is monomial, and second is to format the numerator. # Step 1: denominator data = _product_of_fin_geo(denom) # If the denominator was prescribed, then we need to make sure we still # match it if given_denom: clean_data = _clean_denom_data(denom, data) else: clean_data = data denom_facts = clean_data[1] numer_facts = n.factor_list() + clean_data[0] # Step 2: numerator numer_clean = _simplify_factors(numer_facts) # Now we build strings # Some functions cat = lambda X: reduce(lambda x, y: x + y, X, "") deg_key = lambda X: P(X[0]).total_degree() merge = lambda X: reduce(lambda x, y: x*y[0]**y[1], X, 1) # Given a tuple of the form (f, e), return a formatted string for the # expression f^e. def _expo(tup): if tup[1] == 0 or tup[0] == 1: return "" else: if tup[1] == 1: return "(%s)" % (_format_poly(tup[0], varbs)) else: return "(%s)^%s" % (_format_poly(tup[0], varbs), tup[1]) # Last sanity check assert bool(merge(numer_clean) / merge(denom_facts) == Z), "Something went wrong with the formatting process at the end." # Format strings now if denom_str == None: denom_str = cat(map(_expo, sorted(denom_facts, key=deg_key))) if num_fact: numer_str = cat(map(_expo, sorted(numer_clean, key=deg_key))) else: numer_str = _format_poly(merge(numer_clean), varbs) return (numer_str, denom_str)