(self)
| 1501 | @support.cpython_only # Other implementations may choose a different algorithm |
| 1502 | @support.requires_resource('cpu') |
| 1503 | def test_sumprod_extended_precision_accuracy(self): |
| 1504 | import operator |
| 1505 | from fractions import Fraction |
| 1506 | from itertools import starmap |
| 1507 | from collections import namedtuple |
| 1508 | from math import log2, exp2, fabs |
| 1509 | from random import choices, uniform, shuffle |
| 1510 | from statistics import median |
| 1511 | |
| 1512 | DotExample = namedtuple('DotExample', ('x', 'y', 'target_sumprod', 'condition')) |
| 1513 | |
| 1514 | def DotExact(x, y): |
| 1515 | vec1 = map(Fraction, x) |
| 1516 | vec2 = map(Fraction, y) |
| 1517 | return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) |
| 1518 | |
| 1519 | def Condition(x, y): |
| 1520 | return 2.0 * DotExact(map(abs, x), map(abs, y)) / abs(DotExact(x, y)) |
| 1521 | |
| 1522 | def linspace(lo, hi, n): |
| 1523 | width = (hi - lo) / (n - 1) |
| 1524 | return [lo + width * i for i in range(n)] |
| 1525 | |
| 1526 | def GenDot(n, c): |
| 1527 | """ Algorithm 6.1 (GenDot) works as follows. The condition number (5.7) of |
| 1528 | the dot product xT y is proportional to the degree of cancellation. In |
| 1529 | order to achieve a prescribed cancellation, we generate the first half of |
| 1530 | the vectors x and y randomly within a large exponent range. This range is |
| 1531 | chosen according to the anticipated condition number. The second half of x |
| 1532 | and y is then constructed choosing xi randomly with decreasing exponent, |
| 1533 | and calculating yi such that some cancellation occurs. Finally, we permute |
| 1534 | the vectors x, y randomly and calculate the achieved condition number. |
| 1535 | """ |
| 1536 | |
| 1537 | assert n >= 6 |
| 1538 | n2 = n // 2 |
| 1539 | x = [0.0] * n |
| 1540 | y = [0.0] * n |
| 1541 | b = log2(c) |
| 1542 | |
| 1543 | # First half with exponents from 0 to |_b/2_| and random ints in between |
| 1544 | e = choices(range(int(b/2)), k=n2) |
| 1545 | e[0] = int(b / 2) + 1 |
| 1546 | e[-1] = 0.0 |
| 1547 | |
| 1548 | x[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] |
| 1549 | y[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] |
| 1550 | |
| 1551 | # Second half |
| 1552 | e = list(map(round, linspace(b/2, 0.0 , n-n2))) |
| 1553 | for i in range(n2, n): |
| 1554 | x[i] = uniform(-1.0, 1.0) * exp2(e[i - n2]) |
| 1555 | y[i] = (uniform(-1.0, 1.0) * exp2(e[i - n2]) - DotExact(x, y)) / x[i] |
| 1556 | |
| 1557 | # Shuffle |
| 1558 | pairs = list(zip(x, y)) |
| 1559 | shuffle(pairs) |
| 1560 | x, y = zip(*pairs) |
nothing calls this directly
no test coverage detected