Changeset 8201:4e9b1bf4cf99
Legend:
- Unmodified
- Added
- Removed
-
sage/rings/polynomial/multi_polynomial_ring_generic.pyx
r6606 r8201 1 1 include '../../ext/stdsage.pxi' 2 2 3 3 4 from sage.structure.parent_gens cimport ParentWithGens 4 5 import sage.misc.latex 5 import multi_polynomial_ideal 6 import multi_polynomial_ideal 6 7 from term_order import TermOrder 7 8 from sage.rings.integer_ring import ZZ … … 9 10 import multi_polynomial_element 10 11 import polynomial_ring 12 11 13 12 14 def is_MPolynomialRing(x): … … 423 425 return unpickle_MPolynomialRing_generic_v1,(base_ring, n, names, order) 424 426 425 426 def random_element(self, degree=2, terms=5, *args, **kwds): 427 r""" 428 Return a random polynomial in this polynomial ring. 429 427 def _precomp_counts(self,n, d): 428 """ 429 Given a number of variable n and a degree d return a tuple (C,t) 430 such that C is a list of the cardinalities of the sets of 431 monomials up to degree d (including) in n variables and t is the 432 sum of these cardinalities. 433 430 434 INPUT: 431 degree -- maximum total degree of resulting polynomial 432 terms -- maximum number of terms to generate 433 434 OUTPUT: a random polynomial of total degree \code{degree} 435 and with \code{term} terms in it. 435 n -- number of variables 436 d -- degree 437 438 EXAMPLE: 439 sage: C,t = _precomp_counts(10,2) 440 sage: C[0] 441 1 442 sage: C[1] 443 10 444 sage: C[2] 445 55 446 sage: t 447 66 448 449 TESTS: 450 sage: C,t = _precomp_counts(1,2) 451 sage: C[0] 452 1 453 sage: C[1] 454 1 455 sage: C[2] 456 1 457 sage: t 458 3 459 460 """ 461 from sage.rings.arith import binomial 462 C = [1] #d = 0 463 for dbar in xrange(1, d+1): 464 C.append(binomial(n+dbar-1, dbar)) 465 t = sum(C) 466 return C, t 467 468 def _to_monomial(self,i, n, d): 469 """ 470 Given an index i, a number of variables n and a degree d return 471 the i-th monomial of degree d in n variables. 472 473 INPUT: 474 i -- index: 0 <= i < binom(n+d-1,n-1) 475 n -- number of variables 476 d -- degree 477 478 EXAMPLE: 479 sage: _to_monomial(0,10,2) 480 (0, 0, 0, 0, 0, 0, 0, 0, 0, 2) 481 sage: _to_monomial(8,10,2) 482 (0, 0, 0, 0, 0, 0, 1, 1, 0, 0) 483 sage: _to_monomial(54,10,2) 484 (2, 0, 0, 0, 0, 0, 0, 0, 0, 0) 485 486 NOTE: We do not check if the provided index/rank is within the 487 allowed range. If it is not an infinite loop will occure. 488 """ 489 from sage.combinat import choose_nk 490 comb = choose_nk.from_rank(i, n+d-1, n-1) 491 if comb == []: 492 return (d,) 493 monomial = [ comb[0] ] 494 res = [] 495 for j in range(n-2): 496 res.append(comb[j+1]-comb[j]-1) 497 monomial += res 498 monomial.append( n+d-1-comb[-1]-1 ) 499 return tuple(monomial) 500 501 def _random_monomial_upto_degree_class(self,n, degree, counts=None, total=None): 502 """ 503 Choose a random exponent tuple for $n$ variables with a random 504 degree $d$, i.e. choose the degree uniformly at random first 505 before choosing a random monomial. 506 507 INPUT: 508 n -- number of variables 509 degree -- degree of monomials 510 counts -- ignored 511 total -- ignored 512 """ 513 # bug: doesn't handle n=1 514 from sage.rings.arith import binomial 515 #Select random degree 516 d = ZZ.random_element(0,degree+1) 517 total = binomial(n+d-1, d) 518 519 #Select random monomial of degree d 520 random_index = ZZ.random_element(0, total-1) 521 #Generate the corresponding monomial 522 return self._to_monomial(random_index, n, d) 523 524 def _random_monomial_upto_degree_uniform(self,n, degree, counts=None, total=None): 525 """ 526 Choose a random exponent tuple for $n$ variables with a random 527 degree up to $d$, i.e. choose a random monomial uniformly random 528 from all monomials up to degree $d$. This discriminates against 529 smaller degrees because there are more monomials of bigger 530 degrees. 531 532 INPUT: 533 n -- number of variables 534 degree -- degree of monomials 535 counts -- ignored 536 total -- ignored 537 """ 538 if counts is not None or total is not None: 539 counts, total = self._precomp_counts(n, degree) 540 541 #Select a random one 542 random_index = ZZ.random_element(0, total-1) 543 #Figure out which degree it corresponds to 544 d = 0 545 while random_index >= counts[d]: 546 random_index -= counts[d] 547 d += 1 548 #Generate the corresponding monomial 549 return self._to_monomial(random_index, n, d) 550 551 def random_element(self, d, t, choose_degree=False,*args, **kwargs): 552 """ 553 Return a random polynomial of at most degree $d$ and at most $t$ 554 terms. 555 556 First monomials are chosen uniformly random from the set of all 557 possible monomials of degree up to $d$ (inclusive). This means 558 that it is more likely that a monomial of degree $d$ appears than 559 a monomial of degree $d-1$ because the former class is bigger. 560 561 Exactly $t$ \emph{distinct} monomials are chosen this way and each 562 one gets a random coefficient (possibly zero) from the base ring 563 assigned. 564 565 The returned polynomial is the sum of this list of terms. 566 567 INPUT: 568 d -- maximal degree (likely to be reached) 569 t -- number of terms requested 570 choose_degree -- choose degrees of monomials randomly first rather 571 than monomials uniformly random. 572 **kwargs -- passed to the random element generator of the base ring 436 573 437 574 EXAMPLES: 438 sage: [QQ['x,y'].random_element() for _ in range(5)] 439 [-1/14*x*y + 1/2*x, x*y + x - y + 1, 3*x*y + x - 1/2, 1/3*x*y - 5*x + 1/2*y + 7/6, 2*x*y + 1/2*x + 1] 440 sage: R = MPolynomialRing(ZZ, 'x,y',2 ); 441 sage: R.random_element(2) # random 442 -1*x*y + x + 15*y - 2 443 sage: R.random_element(12) # random 444 x^4*y^5 + x^3*y^5 + 6*x^2*y^2 - x^2 445 sage: R.random_element(12,3) # random 446 -3*x^4*y^2 - x^5 - x^4*y 447 sage: R.random_element(3) # random 448 2*y*z + 2*x + 2*y 449 450 sage: R.<x,y> = MPolynomialRing(RR) 451 sage: R.random_element(2) # random 452 -0.645358174399450*x*y + 0.572655401740132*x + 0.197478565033010 453 454 sage: R.random_element(41) # random 455 -4*x^6*y^4*z^4*a^6*b^3*c^6*d^5 + 1/2*x^4*y^3*z^5*a^4*c^5*d^6 - 5*x^3*z^3*a^6*b^4*c*d^5 + 10*x^2*y*z^5*a^4*b^2*c^3*d^4 - 5*x^3*y^5*z*b^2*c^5*d 456 457 AUTHOR: 458 -- didier deshommes 459 """ 460 # General strategy: 461 # generate n-tuples of numbers with each element in the tuple 462 # not greater than (degree/n) so that the degree 463 # (ie, the sum of the elements in the tuple) does not exceed 464 # their total degree 465 466 n = self.__ngens # length of the n-tuple 467 max_deg = int(degree/n) # max degree for each term 468 R = self.base_ring() 469 470 # restrict exponents to positive integers only 471 exponents = [ tuple([ZZ.random_element(0,max_deg+1) for _ in range(n)]) 472 for _ in range(terms) ] 473 coeffs = [] 474 for _ in range(terms): 475 c = R.random_element(*args,**kwds) 476 while not c: 477 c = R.random_element(*args,**kwds) 478 coeffs.append(c) # allow only nonzero coefficients 479 480 d = dict( zip(tuple(exponents), coeffs) ) 481 return self(multi_polynomial_element.MPolynomial_polydict(self, PolyDict(d))) 482 575 sage: P.<x,y,z> = PolynomialRing(QQ) 576 sage: random_element(P, 2, 5) 577 1/2*y^2 + x*z - x + 2*y + 19/2*z 578 579 sage: random_element(P, 2, 5, choose_degree=True) 580 28*x*z + y*z - z^2 - 1/2*x - 44/39 581 582 sage: random_element(P, 2, 15) 583 Traceback (most recent call last): 584 ... 585 Cannot compute polynomial with more terms than exist. 586 587 sage: random_element(P, 0, 1) 588 1 589 590 sage: random_element(P, 2, 0) 591 0 592 593 """ 594 from sage.combinat.integer_vector import IntegerVectors 595 from sage.rings.arith import binomial 596 597 k = self.base_ring() 598 n = self.ngens() 599 600 #total or d is 0. Just return 601 if 0 == total or 0 == d: 602 return tuple([0]*n) 603 604 counts, total = self._precomp_counts(n, d) 605 606 607 if t < 0: 608 raise TypeError, "Cannot compute polynomial with a negative number of terms." 609 610 elif t < total/2: 611 # we choose random monomials if t < total/2 because then we 612 # expect the algorithm to be faster than generating all 613 # monomials and picking a random index from the list. if t == 614 # total/2 we expect every second random monomial to be a 615 # double such that our runtime is doubled in the worst case. 616 M = set() 617 if not choose_degree: 618 while t: 619 m = self._random_monomial_upto_degree_uniform(n, d, counts, total) 620 if not m in M: 621 M.add(m) 622 t -= 1 623 else: 624 while t: 625 m = self._random_monomial_upto_degree_class(n, d) 626 if not m in M: 627 M.add(m) 628 t -= 1 629 elif t <= total: 630 # generate a list of all monomials and choose among them 631 if not choose_degree: 632 M = sum([list(IntegerVectors(_d,n)) for _d in xrange(d+1)],[]) 633 for mi in xrange(total - t): # we throw away those we don't need 634 M.pop( ZZ.random_element(0,len(M)-1) ) 635 M = map(tuple, M) 636 else: 637 M = [list(IntegerVectors(_d,n)) for _d in xrange(d+1)] 638 Mbar = [] 639 for mi in xrange(t): 640 d = ZZ.random_element(0,len(M)) #choose degree at random 641 m = ZZ.random_element(0,len(M[d])) # choose monomial at random 642 Mbar.append( M[d].pop(m) ) # remove and insert 643 if len(M[d]) == 0: 644 M.pop(d) # bookkeeping 645 M = map(tuple, Mbar) 646 647 else: 648 raise TypeError, "Cannot compute polynomial with more terms than exist." 649 650 C = [k.random_element(*args,**kwargs) for _ in range(len(M))] 651 652 return self(dict(zip(M,C))) 653 483 654 def new_ring(self, names=None, order=None): 484 655 """
Note: See TracChangeset
for help on using the changeset viewer.
