|
5 | 5 | AUTHORS:
|
6 | 6 |
|
7 | 7 | - Kevin Stueve (2010-01-17): in ``is_prime(n)``, delegated calculation to ``n.is_prime()``
|
| 8 | +- Lorenz Panny (2022): :class:`ProductTree`, :func:`prod_with_derivative` |
8 | 9 | """
|
9 | 10 |
|
10 | 11 | # ****************************************************************************
|
@@ -6285,3 +6286,195 @@ def dedekind_psi(N):
|
6285 | 6286 | """
|
6286 | 6287 | N = Integer(N)
|
6287 | 6288 | return Integer(N * prod(1 + 1 / p for p in N.prime_divisors()))
|
| 6289 | + |
| 6290 | + |
| 6291 | +class ProductTree: |
| 6292 | + r""" |
| 6293 | + A simple product tree. |
| 6294 | +
|
| 6295 | + INPUT: |
| 6296 | +
|
| 6297 | + - ``leaves`` -- a sequence of elements in a common ring |
| 6298 | +
|
| 6299 | + EXAMPLES:: |
| 6300 | +
|
| 6301 | + sage: from sage.arith.misc import ProductTree |
| 6302 | + sage: R.<x> = GF(101)[] |
| 6303 | + sage: vs = [x - i for i in range(1,10)] |
| 6304 | + sage: tree = ProductTree(vs) |
| 6305 | + sage: tree.root() |
| 6306 | + x^9 + 56*x^8 + 62*x^7 + 44*x^6 + 47*x^5 + 42*x^4 + 15*x^3 + 11*x^2 + 12*x + 13 |
| 6307 | + sage: tree.remainders(x^7 + x + 1) |
| 6308 | + [3, 30, 70, 27, 58, 72, 98, 98, 23] |
| 6309 | + sage: tree.remainders(x^100) |
| 6310 | + [1, 1, 1, 1, 1, 1, 1, 1, 1] |
| 6311 | +
|
| 6312 | + :: |
| 6313 | +
|
| 6314 | + sage: vs = prime_range(100) |
| 6315 | + sage: tree = ProductTree(vs) |
| 6316 | + sage: tree.root().factor() |
| 6317 | + 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67 * 71 * 73 * 79 * 83 * 89 * 97 |
| 6318 | + sage: tree.remainders(3599) |
| 6319 | + [1, 2, 4, 1, 2, 11, 12, 8, 11, 3, 3, 10, 32, 30, 27, 48, 0, 0, 48, 49, 22, 44, 30, 39, 10] |
| 6320 | +
|
| 6321 | + We can access the individual layers of the tree:: |
| 6322 | +
|
| 6323 | + sage: tree.layers |
| 6324 | + [(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97), |
| 6325 | + (6, 35, 143, 323, 667, 1147, 1763, 2491, 3599, 4757, 5767, 7387, 97), |
| 6326 | + (210, 46189, 765049, 4391633, 17120443, 42600829, 97), |
| 6327 | + (9699690, 3359814435017, 729345064647247, 97), |
| 6328 | + (32589158477190044730, 70746471270782959), |
| 6329 | + (2305567963945518424753102147331756070,)] |
| 6330 | +
|
| 6331 | + .. NOTE:: |
| 6332 | +
|
| 6333 | + Use this class if you need the :meth:`remainders` method. |
| 6334 | + To compute just the product, :func:`prod` is likely faster. |
| 6335 | + """ |
| 6336 | + def __init__(self, leaves): |
| 6337 | + r""" |
| 6338 | + Initialize a product tree having the given ring elements |
| 6339 | + as its leaves. |
| 6340 | +
|
| 6341 | + EXAMPLES:: |
| 6342 | +
|
| 6343 | + sage: from sage.arith.misc import ProductTree |
| 6344 | + sage: vs = prime_range(100) |
| 6345 | + sage: tree = ProductTree(vs) |
| 6346 | + """ |
| 6347 | + V = tuple(leaves) |
| 6348 | + self.layers = [V] |
| 6349 | + while len(V) > 1: |
| 6350 | + V = tuple(prod(V[i:i+2]) for i in range(0,len(V),2)) |
| 6351 | + self.layers.append(V) |
| 6352 | + |
| 6353 | + def __len__(self): |
| 6354 | + r""" |
| 6355 | + Return the number of leaves of this product tree. |
| 6356 | +
|
| 6357 | + EXAMPLES:: |
| 6358 | +
|
| 6359 | + sage: from sage.arith.misc import ProductTree |
| 6360 | + sage: R.<x> = GF(101)[] |
| 6361 | + sage: vs = [x - i for i in range(1,10)] |
| 6362 | + sage: tree = ProductTree(vs) |
| 6363 | + sage: len(tree) |
| 6364 | + 9 |
| 6365 | + sage: len(tree) == len(vs) |
| 6366 | + True |
| 6367 | + sage: len(tree.remainders(x^2)) |
| 6368 | + 9 |
| 6369 | + """ |
| 6370 | + return len(self.layers[0]) |
| 6371 | + |
| 6372 | + def root(self): |
| 6373 | + r""" |
| 6374 | + Return the value at the root of this product tree (i.e., the product of all leaves). |
| 6375 | +
|
| 6376 | + EXAMPLES:: |
| 6377 | +
|
| 6378 | + sage: from sage.arith.misc import ProductTree |
| 6379 | + sage: R.<x> = GF(101)[] |
| 6380 | + sage: vs = [x - i for i in range(1,10)] |
| 6381 | + sage: tree = ProductTree(vs) |
| 6382 | + sage: tree.root() |
| 6383 | + x^9 + 56*x^8 + 62*x^7 + 44*x^6 + 47*x^5 + 42*x^4 + 15*x^3 + 11*x^2 + 12*x + 13 |
| 6384 | + sage: tree.root() == prod(vs) |
| 6385 | + True |
| 6386 | + """ |
| 6387 | + assert len(self.layers[-1]) == 1 |
| 6388 | + return self.layers[-1][0] |
| 6389 | + |
| 6390 | + def remainders(self, x): |
| 6391 | + r""" |
| 6392 | + Given a value `x`, return a list of all remainders of `x` |
| 6393 | + modulo the leaves of this product tree. |
| 6394 | +
|
| 6395 | + The base ring must support the ``%`` operator for this |
| 6396 | + method to work. |
| 6397 | +
|
| 6398 | + INPUT: |
| 6399 | +
|
| 6400 | + - ``x`` -- an element of the base ring of this product tree |
| 6401 | +
|
| 6402 | + EXAMPLES:: |
| 6403 | +
|
| 6404 | + sage: from sage.arith.misc import ProductTree |
| 6405 | + sage: vs = prime_range(100) |
| 6406 | + sage: tree = ProductTree(vs) |
| 6407 | + sage: n = 1085749272377676749812331719267 |
| 6408 | + sage: tree.remainders(n) |
| 6409 | + [1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13] |
| 6410 | + sage: [n % v for v in vs] |
| 6411 | + [1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13] |
| 6412 | + """ |
| 6413 | + X = [x] |
| 6414 | + for V in reversed(self.layers): |
| 6415 | + X = [X[i//2] % V[i] for i in range(len(V))] |
| 6416 | + return X |
| 6417 | + |
| 6418 | + |
| 6419 | +def prod_with_derivative(pairs): |
| 6420 | + r""" |
| 6421 | + Given a list of pairs `(f, \partial f)` of ring elements, return |
| 6422 | + the pair `(\prod f, \partial \prod f)`, assuming `\partial` is an |
| 6423 | + operator obeying the standard product rule. |
| 6424 | +
|
| 6425 | + This function is entirely algebraic, hence still works when the |
| 6426 | + elements `f` and `\partial f` are all passed through some ring |
| 6427 | + homomorphism first. (See the polynomial-evaluation example below.) |
| 6428 | +
|
| 6429 | + INPUT: |
| 6430 | +
|
| 6431 | + - ``pairs`` -- a sequence of tuples `(f, \partial f)` of elements |
| 6432 | + of a common ring |
| 6433 | +
|
| 6434 | + ALGORITHM: |
| 6435 | +
|
| 6436 | + This function wraps the given pairs in a thin helper class that |
| 6437 | + automatically applies the product rule whenever multiplication |
| 6438 | + is invoked, then calls :func:`prod` on the wrapped pairs. |
| 6439 | +
|
| 6440 | + EXAMPLES:: |
| 6441 | +
|
| 6442 | + sage: from sage.arith.misc import prod_with_derivative |
| 6443 | + sage: R.<x> = ZZ[] |
| 6444 | + sage: fs = [x^2 + 2*x + 3, 4*x + 5, 6*x^7 + 8*x + 9] |
| 6445 | + sage: prod(fs) |
| 6446 | + 24*x^10 + 78*x^9 + 132*x^8 + 90*x^7 + 32*x^4 + 140*x^3 + 293*x^2 + 318*x + 135 |
| 6447 | + sage: prod(fs).derivative() |
| 6448 | + 240*x^9 + 702*x^8 + 1056*x^7 + 630*x^6 + 128*x^3 + 420*x^2 + 586*x + 318 |
| 6449 | + sage: F, dF = prod_with_derivative((f, f.derivative()) for f in fs) |
| 6450 | + sage: F |
| 6451 | + 24*x^10 + 78*x^9 + 132*x^8 + 90*x^7 + 32*x^4 + 140*x^3 + 293*x^2 + 318*x + 135 |
| 6452 | + sage: dF |
| 6453 | + 240*x^9 + 702*x^8 + 1056*x^7 + 630*x^6 + 128*x^3 + 420*x^2 + 586*x + 318 |
| 6454 | +
|
| 6455 | + The main reason for this function to exist is that it allows us to |
| 6456 | + *evaluate* the derivative of a product of polynomials at a point |
| 6457 | + `\alpha` without ever fully expanding the product *as a polynomial*:: |
| 6458 | +
|
| 6459 | + sage: alpha = 42 |
| 6460 | + sage: F(alpha) |
| 6461 | + 442943981574522759 |
| 6462 | + sage: dF(alpha) |
| 6463 | + 104645261461514994 |
| 6464 | + sage: us = [f(alpha) for f in fs] |
| 6465 | + sage: vs = [f.derivative()(alpha) for f in fs] |
| 6466 | + sage: prod_with_derivative(zip(us, vs)) |
| 6467 | + (442943981574522759, 104645261461514994) |
| 6468 | + """ |
| 6469 | + class _aux: |
| 6470 | + def __init__(self, f, df): |
| 6471 | + self.f, self.df = f, df |
| 6472 | + |
| 6473 | + def __mul__(self, other): |
| 6474 | + return _aux(self.f * other.f, self.df * other.f + self.f * other.df) |
| 6475 | + |
| 6476 | + def __iter__(self): |
| 6477 | + yield self.f |
| 6478 | + yield self.df |
| 6479 | + |
| 6480 | + return tuple(prod(_aux(*tup) for tup in pairs)) |
0 commit comments