def eval_levicivita(*args): """Evaluate Levi-Civita symbol.""" from sympy import factorial n = len(args) return prod( prod(args[j] - args[i] for j in xrange(i + 1, n)) / factorial(i) for i in xrange(n))
def eval_levicivita(*args): """Evaluate Levi-Civita symbol.""" from sympy.functions.combinatorial.factorials import factorial n = len(args) return prod( prod(args[j] - args[i] for j in range(i + 1, n)) / factorial(i) for i in range(n))
def denester(nested): """ Denests a list of expressions that contain nested square roots. This method should not be called directly - use 'sqrtdenest' instead. This algorithm is based on <http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf>. It is assumed that all of the elements of 'nested' share the same bottom-level radicand. (This is stated in the paper, on page 177, in the paragraph immediately preceding the algorithm.) When evaluating all of the arguments in parallel, the bottom-level radicand only needs to be denested once. This means that calling denester with x arguments results in a recursive invocation with x+1 arguments; hence denester has polynomial complexity. However, if the arguments were evaluated separately, each call would result in two recursive invocations, and the algorithm would have exponential complexity. This is discussed in the paper in the middle paragraph of page 179. """ if all((n**2).is_Number for n in nested): #If none of the arguments are nested for f in subsets(len(nested)): #Test subset 'f' of nested p = prod(nested[i]**2 for i in range(len(f)) if f[i]).expand() if 1 in f and f.count(1) > 1 and f[-1]: p = -p if sqrt(p).is_Number: return sqrt(p), f #If we got a perfect square, return its square root. return nested[-1], [0]*len(nested) #Otherwise, return the radicand from the previous invocation. else: a, b, r, R = Wild('a'), Wild('b'), Wild('r'), None values = [expr.match(sqrt(a + b * sqrt(r))) for expr in nested] if any(v is None for v in values): # this pattern is not recognized return nested[-1], [0]*len(nested) #Otherwise, return the radicand from the previous invocation. for v in values: if r in v: #Since if b=0, r is not defined if R is not None: assert R == v[r] #All the 'r's should be the same. else: R = v[r] if R is None: return nested[-1], [0]*len(nested) #return the radicand from the previous invocation. d, f = denester([sqrt((v[a]**2).expand()-(R*v[b]**2).expand()) for v in values] + [sqrt(R)]) if all(fi == 0 for fi in f): v = values[-1] return sqrt(v[a] + v[b]*d), f else: v = prod(nested[i]**2 for i in range(len(nested)) if f[i]).expand().match(a+b*sqrt(r)) if 1 in f and f.index(1) < len(nested) - 1 and f[len(nested)-1]: v[a] = -1 * v[a] v[b] = -1 * v[b] if not f[len(nested)]: #Solution denests with square roots vad = (v[a] + d).expand() if not vad: return nested[-1], [0]*len(nested) #Otherwise, return the radicand from the previous invocation. return (sqrt(vad/2) + sign(v[b])*sqrt((v[b]**2*R/(2*vad)).expand())).expand(), f else: #Solution requires a fourth root FR, s = (R.expand()**Rational(1,4)), sqrt((v[b]*R).expand()+d) return (s/(sqrt(2)*FR) + v[a]*FR/(sqrt(2)*s)).expand(), f
def _terms(e): # return the number of terms of this expression # when multiplied out -- assuming no joining of terms if e.is_Add: return sum([_terms(ai) for ai in e.args]) if e.is_Mul: return prod([_terms(mi) for mi in e.args]) return 1
def test_decomp_7(): # Try working through an AlgebraicField T = Poly(x**3 + x**2 - 2 * x + 8) K = QQ.alg_field_from_poly(T) p = 2 P = K.primes_above(p) ZK = K.maximal_order() assert len(P) == 3 assert all(Pi.e == Pi.f == 1 for Pi in P) assert prod(Pi**Pi.e for Pi in P) == p * ZK
def test_decomp_6(): # Another case where 2 divides the index. This is Dedekind's example of # an essential discriminant divisor. (See Cohen, Excercise 6.10.) T = Poly(x**3 + x**2 - 2 * x + 8) rad = {} ZK, dK = round_two(T, radicals=rad) p = 2 P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) assert len(P) == 3 assert all(Pi.e == Pi.f == 1 for Pi in P) assert prod(Pi**Pi.e for Pi in P) == p * ZK
def _nP(n, k=None, replacement=False): from sympy.functions.combinatorial.factorials import factorial from sympy.core.mul import prod if k == 0: return 1 if type(n) is int: # n different items # assert n >= 0 if k is None: return sum(_nP(n, i, replacement) for i in range(n + 1)) elif replacement: return n**k elif k > n: return 0 elif k == n: return factorial(k) elif k == 1: return n else: # assert k >= 0 return _product(n - k + 1, n) elif isinstance(n, _MultisetHistogram): if k is None: return sum(_nP(n, i, replacement) for i in range(n[_N] + 1)) elif replacement: return n[_ITEMS]**k elif k == n[_N]: return factorial(k) / prod([factorial(i) for i in n[_M] if i > 1]) elif k > n[_N]: return 0 elif k == 1: return n[_ITEMS] else: # assert k >= 0 tot = 0 n = list(n) for i in range(len(n[_M])): if not n[i]: continue n[_N] -= 1 if n[i] == 1: n[i] = 0 n[_ITEMS] -= 1 tot += _nP(_MultisetHistogram(n), k - 1) n[_ITEMS] += 1 n[i] = 1 else: n[i] -= 1 tot += _nP(_MultisetHistogram(n), k - 1) n[i] += 1 n[_N] += 1 return tot
def _nP(n, k=None, replacement=False): from sympy.functions.combinatorial.factorials import factorial from sympy.core.mul import prod if k == 0: return 1 if isinstance(n, SYMPY_INTS): # n different items # assert n >= 0 if k is None: return sum(_nP(n, i, replacement) for i in range(n + 1)) elif replacement: return n**k elif k > n: return 0 elif k == n: return factorial(k) elif k == 1: return n else: # assert k >= 0 return _product(n - k + 1, n) elif isinstance(n, _MultisetHistogram): if k is None: return sum(_nP(n, i, replacement) for i in range(n[_N] + 1)) elif replacement: return n[_ITEMS]**k elif k == n[_N]: return factorial(k)/prod([factorial(i) for i in n[_M] if i > 1]) elif k > n[_N]: return 0 elif k == 1: return n[_ITEMS] else: # assert k >= 0 tot = 0 n = list(n) for i in range(len(n[_M])): if not n[i]: continue n[_N] -= 1 if n[i] == 1: n[i] = 0 n[_ITEMS] -= 1 tot += _nP(_MultisetHistogram(n), k - 1) n[_ITEMS] += 1 n[i] = 1 else: n[i] -= 1 tot += _nP(_MultisetHistogram(n), k - 1) n[i] += 1 n[_N] += 1 return tot
def get_upper_degree(self): SymPyDeprecationWarning(feature="get_upper_degree", useinstead="get_max_degrees", issue=17763, deprecated_since_version="1.5").warn() list_of_products = [ self.variables[i]**self._max_degrees[i] for i in range(self.n) ] product = prod(list_of_products) product = Poly(product).monoms() return monomial_deg(*product)
def get_upper_degree(self): sympy_deprecation_warning( """ The get_upper_degree() method of DixonResultant is deprecated. Use get_max_degrees() instead. """, deprecated_since_version="1.5", active_deprecations_target="deprecated-dixonresultant-properties" ) list_of_products = [self.variables[i] ** self._max_degrees[i] for i in range(self.n)] product = prod(list_of_products) product = Poly(product).monoms() return monomial_deg(*product)
def test_decomp_8(): # This time we consider various cubics, and try factoring all primes # dividing the index. cases = ( x**3 + 3 * x**2 - 4 * x + 4, x**3 + 3 * x**2 + 3 * x - 3, x**3 + 5 * x**2 - x + 3, x**3 + 5 * x**2 - 5 * x - 5, x**3 + 3 * x**2 + 5, x**3 + 6 * x**2 + 3 * x - 1, x**3 + 6 * x**2 + 4, x**3 + 7 * x**2 + 7 * x - 7, x**3 + 7 * x**2 - x + 5, x**3 + 7 * x**2 - 5 * x + 5, x**3 + 4 * x**2 - 3 * x + 7, x**3 + 8 * x**2 + 5 * x - 1, x**3 + 8 * x**2 - 2 * x + 6, x**3 + 6 * x**2 - 3 * x + 8, x**3 + 9 * x**2 + 6 * x - 8, x**3 + 15 * x**2 - 9 * x + 13, ) def display(T, p, radical, P, I, J): """Useful for inspection, when running test manually.""" print('=' * 20) print(T, p, radical) for Pi in P: print(f' ({Pi!r})') print("I: ", I) print("J: ", J) print(f'Equal: {I == J}') inspect = False for g in cases: T = Poly(g) rad = {} ZK, dK = round_two(T, radicals=rad) dT = T.discriminant() f_squared = dT // dK F = factorint(f_squared) for p in F: radical = rad.get(p) P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=radical) I = prod(Pi**Pi.e for Pi in P) J = p * ZK if inspect: display(T, p, radical, P, I, J) assert I == J
def test_boson_states(): a = BosonOp("a") # Fock states n = 3 assert (BosonFockBra(0) * BosonFockKet(1)).doit() == 0 assert (BosonFockBra(1) * BosonFockKet(1)).doit() == 1 assert qapply(BosonFockBra(n) * Dagger(a)**n * BosonFockKet(0)) \ == sqrt(prod(range(1, n+1))) # Coherent states alpha1, alpha2 = 1.2, 4.3 assert (BosonCoherentBra(alpha1) * BosonCoherentKet(alpha1)).doit() == 1 assert (BosonCoherentBra(alpha2) * BosonCoherentKet(alpha2)).doit() == 1 assert abs((BosonCoherentBra(alpha1) * BosonCoherentKet(alpha2)).doit() - exp((alpha1 - alpha2)**2 * Rational(-1, 2))) < 1e-12 assert qapply(a * BosonCoherentKet(alpha1)) == \ alpha1 * BosonCoherentKet(alpha1)
def _sample_numpy(cls, dist, size, seed): """Sample from NumPy.""" numpy_rv_map = {} sample_shape = {} dist_list = numpy_rv_map.keys() if dist.__class__.__name__ not in dist_list: return None import numpy if seed is None or isinstance(seed, int): rand_state = numpy.random.default_rng(seed=seed) else: rand_state = seed samp = numpy_rv_map[dist.__class__.__name__](dist, prod(size), rand_state) return samp.reshape(size + sample_shape[dist.__class__.__name__](dist))
def gf_crt(U, M, K=None): """ Chinese Remainder Theorem. Given a set of integer residues ``u_0,...,u_n`` and a set of co-prime integer moduli ``m_0,...,m_n``, returns an integer ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``. As an example consider a set of residues ``U = [49, 76, 65]`` and a set of moduli ``M = [99, 97, 95]``. Then we have:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt >>> from sympy.ntheory.modular import solve_congruence >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ) 639985 This is the correct result because:: >>> [639985 % m for m in [99, 97, 95]] [49, 76, 65] Note: this is a low-level routine with no error checking. See Also ======== sympy.ntheory.modular.crt : a higher level crt routine sympy.ntheory.modular.solve_congruence """ p = prod(M, start=K.one) v = K.zero for u, m in zip(U, M): e = p // m s, _, _ = K.gcdex(e, m) v += e * (u * s % m) return v % p
def gf_crt(U, M, K=None): """ Chinese Remainder Theorem. Given a set of integer residues ``u_0,...,u_n`` and a set of co-prime integer moduli ``m_0,...,m_n``, returns an integer ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``. As an example consider a set of residues ``U = [49, 76, 65]`` and a set of moduli ``M = [99, 97, 95]``. Then we have:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt >>> from sympy.ntheory.modular import solve_congruence >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ) 639985 This is the correct result because:: >>> [639985 % m for m in [99, 97, 95]] [49, 76, 65] Note: this is a low-level routine with no error checking. See Also ======== sympy.ntheory.modular.crt : a higher level crt routine sympy.ntheory.modular.solve_congruence """ p = prod(M, start=K.one) v = K.zero for u, m in zip(U, M): e = p // m s, _, _ = K.gcdex(e, m) v += e*(u*s % m) return v % p
def gf_crt1(M, K): """ First part of the Chinese Remainder Theorem. Examples ======== >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt1 >>> gf_crt1([99, 97, 95], ZZ) (912285, [9215, 9405, 9603], [62, 24, 12]) """ E, S = [], [] p = prod(M, start=K.one) for m in M: E.append(p // m) S.append(K.gcdex(E[-1], m)[0] % m) return p, E, S
def _sample_pymc3(cls, dist, size, seed): """Sample from PyMC3.""" import pymc3 pymc3_rv_map = { 'MatrixNormalDistribution': lambda dist: pymc3.MatrixNormal( 'X', mu=matrix2numpy(dist.location_matrix, float), rowcov=matrix2numpy(dist.scale_matrix_1, float), colcov=matrix2numpy(dist.scale_matrix_2, float), shape=dist.location_matrix.shape), 'WishartDistribution': lambda dist: pymc3.WishartBartlett( 'X', nu=int(dist.n), S=matrix2numpy(dist.scale_matrix, float)) } sample_shape = { 'WishartDistribution': lambda dist: dist.scale_matrix.shape, 'MatrixNormalDistribution': lambda dist: dist.location_matrix.shape } dist_list = pymc3_rv_map.keys() if dist.__class__.__name__ not in dist_list: return None import logging logging.getLogger("pymc3").setLevel(logging.ERROR) with pymc3.Model(): pymc3_rv_map[dist.__class__.__name__](dist) samps = pymc3.sample(draws=prod(size), chains=1, progressbar=False, random_seed=seed, return_inferencedata=False, compute_convergence_checks=False)['X'] return samps.reshape(size + sample_shape[dist.__class__.__name__](dist))
def test_decomp_5(): # Here is our first test of the "hard case" of prime decomposition. # We work in a quadratic extension Q(sqrt(d)) where d is 1 mod 4, and # we consider the factorization of the rational prime 2, which divides # the index. # Theory says the form of p's factorization depends on the residue of # d mod 8, so we consider both cases, d = 1 mod 8 and d = 5 mod 8. for d in [-7, -3]: T = Poly(x**2 - d) rad = {} ZK, dK = round_two(T, radicals=rad) p = 2 P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) if d % 8 == 1: assert len(P) == 2 assert all(P[i].e == 1 and P[i].f == 1 for i in range(2)) assert prod(Pi**Pi.e for Pi in P) == p * ZK else: assert d % 8 == 5 assert len(P) == 1 assert P[0].e == 1 assert P[0].f == 2 assert P[0].as_submodule() == p * ZK
def _sample_scipy(cls, dist, size, seed): """Sample from SciPy.""" from scipy import stats as scipy_stats import numpy scipy_rv_map = { 'WishartDistribution': lambda dist, size, rand_state: scipy_stats.wishart.rvs( df=int(dist.n), scale=matrix2numpy(dist.scale_matrix, float), size=size), 'MatrixNormalDistribution': lambda dist, size, rand_state: scipy_stats.matrix_normal.rvs( mean=matrix2numpy(dist.location_matrix, float), rowcov=matrix2numpy(dist.scale_matrix_1, float), colcov=matrix2numpy(dist.scale_matrix_2, float), size=size, random_state=rand_state) } sample_shape = { 'WishartDistribution': lambda dist: dist.scale_matrix.shape, 'MatrixNormalDistribution': lambda dist: dist.location_matrix.shape } dist_list = scipy_rv_map.keys() if dist.__class__.__name__ not in dist_list: return None if seed is None or isinstance(seed, int): rand_state = numpy.random.default_rng(seed=seed) else: rand_state = seed samp = scipy_rv_map[dist.__class__.__name__](dist, prod(size), rand_state) return samp.reshape(size + sample_shape[dist.__class__.__name__](dist))
def crt(m, v, symmetric=False, check=True): r"""Chinese Remainder Theorem. The moduli in m are assumed to be pairwise coprime. The output is then an integer f, such that f = v_i mod m_i for each pair out of v and m. If ``symmetric`` is False a positive integer will be returned, else \|f\| will be less than or equal to the LCM of the moduli, and thus f may be negative. If the moduli are not co-prime the correct result will be returned if/when the test of the result is found to be incorrect. This result will be None if there is no solution. The keyword ``check`` can be set to False if it is known that the moduli are coprime. As an example consider a set of residues ``U = [49, 76, 65]`` and a set of moduli ``M = [99, 97, 95]``. Then we have:: >>> from sympy.ntheory.modular import crt, solve_congruence >>> crt([99, 97, 95], [49, 76, 65]) (639985, 912285) This is the correct result because:: >>> [639985 % m for m in [99, 97, 95]] [49, 76, 65] If the moduli are not co-prime, you may receive an incorrect result if you use ``check=False``: >>> crt([12, 6, 17], [3, 4, 2], check=False) (954, 1224) >>> [954 % m for m in [12, 6, 17]] [6, 0, 2] >>> crt([12, 6, 17], [3, 4, 2]) is None True >>> crt([3, 6], [2, 5]) (5, 6) Note: the order of gf_crt's arguments is reversed relative to crt, and that solve_congruence takes residue, modulus pairs. Programmer's note: rather than checking that all pairs of moduli share no GCD (an O(n**2) test) and rather than factoring all moduli and seeing that there is no factor in common, a check that the result gives the indicated residuals is performed -- an O(n) operation. See Also ======== solve_congruence sympy.polys.galoistools.gf_crt : low level crt routine used by this routine """ if check: m = list(map(as_int, m)) v = list(map(as_int, v)) result = gf_crt(v, m, ZZ) mm = prod(m) if check: if not all(v % m == result % m for v, m in zip(v, m)): result = solve_congruence(*list(zip(v, m)), check=False, symmetric=symmetric) if result is None: return result result, mm = result if symmetric: return symmetric_residue(result, mm), mm return result, mm
def crt(m, v, symmetric=False, check=True): r"""Chinese Remainder Theorem. The moduli in m are assumed to be pairwise coprime. The output is then an integer f, such that f = v_i mod m_i for each pair out of v and m. If ``symmetric`` is False a positive integer will be returned, else \|f\| will be less than or equal to the LCM of the moduli, and thus f may be negative. If the moduli are not co-prime the correct result will be returned if/when the test of the result is found to be incorrect. This result will be None if there is no solution. The keyword ``check`` can be set to False if it is known that the moduli are coprime. As an example consider a set of residues ``U = [49, 76, 65]`` and a set of moduli ``M = [99, 97, 95]``. Then we have:: >>> from sympy.ntheory.modular import crt, solve_congruence >>> crt([99, 97, 95], [49, 76, 65]) (639985, 912285) This is the correct result because:: >>> [639985 % m for m in [99, 97, 95]] [49, 76, 65] If the moduli are not co-prime, you may receive an incorrect result if you use ``check=False``: >>> crt([12, 6, 17], [3, 4, 2], check=False) (954, 1224) >>> [954 % m for m in [12, 6, 17]] [6, 0, 2] >>> crt([12, 6, 17], [3, 4, 2]) is None True >>> crt([3, 6], [2, 5]) (5, 6) Note: the order of gf_crt's arguments is reversed relative to crt, and that solve_congruence takes residue, modulus pairs. Programmer's note: rather than checking that all pairs of moduli share no GCD (an O(n**2) test) and rather than factoring all moduli and seeing that there is no factor in common, a check that the result gives the indicated residuals is performed -- an O(n) operation. See Also ======== solve_congruence sympy.polys.galoistools.gf_crt : low level crt routine used by this routine """ if check: m = map(as_int, m) v = map(as_int, v) result = gf_crt(v, m, ZZ) mm = prod(m) if check: if not all(v % m == result % m for v, m in zip(v, m)): result = solve_congruence(*zip(v, m), check=False, symmetric=symmetric) if result is None: return result result, mm = result if symmetric: return symmetric_residue(result, mm), mm return result, mm
def nC(n, k=None, replacement=False): """Return the number of combinations of ``n`` items taken ``k`` at a time. Possible values for ``n``:: integer - set of length ``n`` sequence - converted to a multiset internally multiset - {element: multiplicity} If ``k`` is None then the total of all combinations of length 0 through the number of items represented in ``n`` will be returned. If ``replacement`` is True then a given item can appear more than once in the ``k`` items. (For example, for 'ab' sets of 2 would include 'aa', 'ab', and 'bb'.) The multiplicity of elements in ``n`` is ignored when ``replacement`` is True but the total number of elements is considered since no element can appear more times than the number of elements in ``n``. Examples ======== >>> from sympy.functions.combinatorial.numbers import nC >>> from sympy.utilities.iterables import multiset_combinations >>> nC(3, 2) 3 >>> nC('abc', 2) 3 >>> nC('aab', 2) 2 When ``replacement`` is True, each item can have multiplicity equal to the length represented by ``n``: >>> nC('aabc', replacement=True) 35 >>> [len(list(multiset_combinations('aaaabbbbcccc', i))) for i in range(5)] [1, 3, 6, 10, 15] >>> sum(_) 35 If there are ``k`` items with multiplicities ``m_1, m_2, ..., m_k`` then the total of all combinations of length 0 hrough ``k`` is the product, ``(m_1 + 1)*(m_2 + 1)*...*(m_k + 1)``. When the multiplicity of each item is 1 (i.e., k unique items) then there are 2**k combinations. For example, if there are 4 unique items, the total number of combinations is 16: >>> sum(nC(4, i) for i in range(5)) 16 References ========== .. [1] http://en.wikipedia.org/wiki/Combination .. [2] http://tinyurl.com/cep849r See Also ======== sympy.utilities.iterables.multiset_combinations """ from sympy.functions.combinatorial.factorials import binomial from sympy.core.mul import prod if isinstance(n, SYMPY_INTS): if k is None: if not replacement: return 2**n return sum(nC(n, i, replacement) for i in range(n + 1)) if k < 0: raise ValueError("k cannot be negative") if replacement: return binomial(n + k - 1, k) return binomial(n, k) if isinstance(n, _MultisetHistogram): N = n[_N] if k is None: if not replacement: return prod(m + 1 for m in n[_M]) return sum(nC(n, i, replacement) for i in range(N + 1)) elif replacement: return nC(n[_ITEMS], k, replacement) # assert k >= 0 elif k in (1, N - 1): return n[_ITEMS] elif k in (0, N): return 1 return _AOP_product(tuple(n[_M]))[k] else: return nC(_multiset_histogram(n), k, replacement)
def nC(n, k=None, replacement=False): """Return the number of combinations of ``n`` items taken ``k`` at a time. Possible values for ``n``:: integer - set of length ``n`` sequence - converted to a multiset internally multiset - {element: multiplicity} If ``k`` is None then the total of all combinations of length 0 through the number of items represented in ``n`` will be returned. If ``replacement`` is True then a given item can appear more than once in the ``k`` items. (For example, for 'ab' sets of 2 would include 'aa', 'ab', and 'bb'.) The multiplicity of elements in ``n`` is ignored when ``replacement`` is True but the total number of elements is considered since no element can appear more times than the number of elements in ``n``. Examples ======== >>> from sympy.functions.combinatorial.numbers import nC >>> from sympy.utilities.iterables import multiset_combinations >>> nC(3, 2) 3 >>> nC('abc', 2) 3 >>> nC('aab', 2) 2 When ``replacement`` is True, each item can have multiplicity equal to the length represented by ``n``: >>> nC('aabc', replacement=True) 35 >>> [len(list(multiset_combinations('aaaabbbbcccc', i))) for i in range(5)] [1, 3, 6, 10, 15] >>> sum(_) 35 If there are ``k`` items with multiplicities ``m_1, m_2, ..., m_k`` then the total of all combinations of length 0 hrough ``k`` is the product, ``(m_1 + 1)*(m_2 + 1)*...*(m_k + 1)``. When the multiplicity of each item is 1 (i.e., k unique items) then there are 2**k combinations. For example, if there are 4 unique items, the total number of combinations is 16: >>> sum(nC(4, i) for i in range(5)) 16 References ========== * http://en.wikipedia.org/wiki/Combination * http://tinyurl.com/cep849r See Also ======== sympy.utilities.iterables.multiset_combinations """ from sympy.functions.combinatorial.factorials import binomial from sympy.core.mul import prod if type(n) is int: if k is None: if not replacement: return 2**n return sum(nC(n, i, replacement) for i in range(n + 1)) assert k >= 0 if replacement: return binomial(n + k - 1, k) return binomial(n, k) if isinstance(n, _MultisetHistogram): N = n[_N] if k is None: if not replacement: return prod(m + 1 for m in n[_M]) return sum(nC(n, i, replacement) for i in range(N + 1)) elif replacement: return nC(n[_ITEMS], k, replacement) # assert k >= 0 elif k in (1, N - 1): return n[_ITEMS] elif k in (0, N): return 1 return _AOP_product(tuple(n[_M]))[k] else: return nC(_multiset_histogram(n), k, replacement)
def denester(nested): """ Denests a list of expressions that contain nested square roots. This method should not be called directly - use 'sqrtdenest' instead. This algorithm is based on <http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf>. It is assumed that all of the elements of 'nested' share the same bottom-level radicand. (This is stated in the paper, on page 177, in the paragraph immediately preceding the algorithm.) When evaluating all of the arguments in parallel, the bottom-level radicand only needs to be denested once. This means that calling denester with x arguments results in a recursive invocation with x+1 arguments; hence denester has polynomial complexity. However, if the arguments were evaluated separately, each call would result in two recursive invocations, and the algorithm would have exponential complexity. This is discussed in the paper in the middle paragraph of page 179. """ if all((n**2).is_Number for n in nested): #If none of the arguments are nested for f in subsets(len(nested)): #Test subset 'f' of nested p = prod(nested[i]**2 for i in range(len(f)) if f[i]).expand() if 1 in f and f.count(1) > 1 and f[-1]: p = -p if sqrt(p).is_Number: return sqrt( p), f #If we got a perfect square, return its square root. return nested[-1], [0] * len( nested ) #Otherwise, return the radicand from the previous invocation. else: a, b, r, R = Wild('a'), Wild('b'), Wild('r'), None values = [expr.match(sqrt(a + b * sqrt(r))) for expr in nested] if any(v is None for v in values): # this pattern is not recognized return nested[-1], [0] * len( nested ) #Otherwise, return the radicand from the previous invocation. for v in values: if r in v: #Since if b=0, r is not defined if R is not None: assert R == v[r] #All the 'r's should be the same. else: R = v[r] if R is None: return nested[-1], [0] * len( nested) #return the radicand from the previous invocation. d, f = denester([ sqrt((v[a]**2).expand() - (R * v[b]**2).expand()) for v in values ] + [sqrt(R)]) if all(fi == 0 for fi in f): v = values[-1] return sqrt(v[a] + v[b] * d), f else: v = prod(nested[i]**2 for i in range(len(nested)) if f[i]).expand().match(a + b * sqrt(r)) if 1 in f and f.index(1) < len(nested) - 1 and f[len(nested) - 1]: v[a] = -1 * v[a] v[b] = -1 * v[b] if not f[len(nested)]: #Solution denests with square roots vad = (v[a] + d).expand() if not vad: return nested[-1], [0] * len( nested ) #Otherwise, return the radicand from the previous invocation. return (sqrt(vad / 2) + sign(v[b]) * sqrt( (v[b]**2 * R / (2 * vad)).expand())).expand(), f else: #Solution requires a fourth root FR, s = (R.expand()**Rational( 1, 4)), sqrt((v[b] * R).expand() + d) return (s / (sqrt(2) * FR) + v[a] * FR / (sqrt(2) * s)).expand(), f