Balansis · численные вычисления на Python · AGPL-3.0 / коммерческая
Контролируйте потерю точности и особые случаи в расчётах
Balansis — библиотека для разработчиков численных алгоритмов. Она помогает суммировать слагаемые при сильном взаимном уничтожении и задавать, что делать при делении на ноль: остановить расчёт, сохранить особое состояние или применить предел. Выберите нужную операцию, сравните с Python и NumPy на своих данных и проверьте цену дополнительной обработки.
Для обычной точной суммы часто достаточно math.fsum, для быстрых матриц — NumPy, для десятичных денег — Decimal. Balansis полезен, когда нужны компенсированные произведения или явная политика особых состояний. Обёртка числа сама по себе не добавляет точности.
Ниже показаны результаты прогона опубликованной версии 1.1.0 от 2026-09-08. Это данные конкретного релиза и окружения, а не результаты для всех версий библиотеки.
Задача, ради которой это существует
Скалярное произведение двух векторов длины 1000. Точный ответ известен: он вычислен в рациональной арифметике и равен -4893576300353906275/9223372036854775808 ≈ -0.530562605606728. Слагаемые при этом порядка 1016, и ответ полностью съедается взаимным уничтожением.
from fractions import Fraction
import numpy as np
from balansis.numpy_integration import compensated_dot_product
# Те же входы, что в опубликованном прогоне
rng = np.random.default_rng(20260908)
a = rng.uniform(-1.0, 1.0, 1000) * 1e8
b = rng.uniform(-1.0, 1.0, 1000) * 1e8
rest = sum(Fraction(float(x)) * Fraction(float(y))
for x, y in zip(a[:-1], b[:-1]))
b[-1] = float((Fraction(1) - rest) / Fraction(float(a[-1])))
exact = sum(Fraction(float(x)) * Fraction(float(y)) for x, y in zip(a, b))
print(float(np.dot(a, b))) # в прогоне: -32
print(float(compensated_dot_product(a, b))) # в прогоне: -0.5305626056067283
assert float(compensated_dot_product(a, b)) == float(exact)| Метод | Результат | Верных знаков | Абс. ошибка | Время (min) |
|---|---|---|---|---|
| наивный цикл float64 | -32 | 0.0 | 31.4694 | 37.81 мкс |
| numpy.dot (BLAS) | -32 | 0.0 | 31.4694 | 643.0 нс |
| math.fsum над произведениями точная сумма округлённых произведений — ошибка остаётся | -8.37496948242188 | 0.0 | 7.84441 | 51.89 мкс |
| Balansis dot2 (Ogita–Rump–Oishi) two_product ловит ошибку каждого произведения, math.fsum складывает всё точно | -0.530562605606728 | 16 (предел float64) | 4.478e-17 | 87.65 мкс |
| Balansis numpy_integration.compensated_dot_product публичный numpy-мост, внутри тот же dot2 | -0.530562605606728 | 16 (предел float64) | 4.478e-17 | 87.57 мкс |
math.fsum над произведениями не восстанавливает остаток: каждое произведение уже округлено до суммирования. Цена: 136× ко времени относительно np.dot. В Python 3.12 появился math.sumprod с повышенной точностью промежуточных произведений и суммы. В этот прогон он не включён, поэтому таблица не доказывает превосходство над современной стандартной библиотекой: math.sumprod тоже может решить показанную задачу. Контракт math.sumprod.Что такое ACT простым языком
ACT (Absolute Compensation Theory) — название модели арифметики в проекте. Она разделяет модуль и знак числа и задаёт правила для особых состояний. Численные алгоритмы и эта модель решают разные задачи: представление числа не заменяет алгоритм повышения точности.
1. Число — это величина и направление
AbsoluteValue(magnitude=..., direction=...) хранит неотрицательный float-модуль и знак отдельно. ABSOLUTE — имя канонического нуля. Представление не отличает математический ноль от нуля, возникшего из-за округления до создания объекта, и не восстанавливает уже потерянные разряды.
2. Компенсация — возвращаемое значение, а не побочный эффект
Некоторые операции возвращают пару (результат, диагностика). Например, у sequence_sum второй элемент — модуль накопленной поправки, делённый на заданный порог. Результат уже содержит поправку. Диагностика не измеряет оставшуюся ошибку и не сообщает число потерянных или восстановленных знаков.
3. Поведение при делении на ноль выбирает приложение
ExtendedRatio различает три состояния: finite, infinite (со знаком) и indeterminate. Что с этим делать, выбирает вызывающий код политикой raise / propagate / saturate — а не догадками на месте.
4. Под всем этим — классические error-free transformations
Точность даёт не «магия ACT», а известные алгоритмы: two_sum Кнута, two_product Деккера, dot2 Огиты–Румпа–Оиши, суммирование по Ноймайеру. В измеренной версии Balansis эти методы доступны вместе с типами и политиками. Доказательства Lean относятся к математическим моделям; они не доказывают корректность исполнения каждого Python-вызова или C-ядра.
Где выигрывает, а где нет
Ниже — результаты Balansis 1.1.0 на выбранных входах. Они помогают выбрать, что проверять на своей задаче; результаты другого релиза могут отличаться.
| Задача | Итог в этом прогоне | Измеренный факт |
|---|---|---|
| Плохо обусловленное скалярное произведение | Balansis | оба метода Balansis дают ближайший float64 к эталону на этих входах; сравнение с math.sumprod не проведено |
| Деление на ноль как типизированное состояние | Balansis | три состояния и три политики против ZeroDivisionError и тихого nan |
| Сумма ряда со взаимным уничтожением | ничья по точности | math.fsum, встроенный sum() и Balansis дают точный ответ; наивный цикл — 0. Balansis при этом 25× медленнее math.fsum |
| Softmax на переполняющих логитах | ничья со стандартным приёмом | сдвиг на максимум в numpy даёт тот же ответ до последнего бита; выигрыш только против наивной реализации, которая возвращает NaN |
| Бухгалтерский ноль на суммах вроде 12345.67 | Decimal, не Balansis | Ledger приводит Decimal к float на входе и повторяет ошибку float; структурный ноль появляется только там, где и float даёт ноль |
| SVD | numpy | бэкенд numpy_gesdd — это буквально numpy; бэкенд act_jacobi в прогоне оказался и медленнее, и чуть менее точен |
| Сигнал о взаимном уничтожении | это не победа | compensated_add(1e16, −1e16) возвращает 2 при точном ответе 0 — величину в один ULP операнда: это флаг риска, а не восстановленное значение |
| Скорость арифметики на объектах | float | compensated_add медленнее сложения float в 87×; примитив two_sum — только в 5.9× (обе цифры за вычетом накладных питоновского вызова) |
| Алгебра модели | Lean4 | 73 теорем проверены ядром Lean 2026-09-08; вне стандартных аксиом Mathlib не используется ничего |
math.fsum, для произведений — math.sumprod. Ближайший float64 к точному ответу получен в показанном сценарии на Balansis 1.1.0 с PyPI и Python 3.12.3; это не гарантия корректного округления для любых входов. В этой версии при underflow промежуточных произведений или их остатков компенсация может теряться. Результат следует проверять для своих данных и версии Python.Куда дальше
Примеры
Восемь задач с выполненными вычислениями: агрегация, скалярное произведение, softmax, бухгалтерский ноль, деление на ноль, SVD.
Бенчмарки
Точность против наивной арифметики и цена этой точности во времени. Включая случаи, где Balansis проигрывает.
Доказательства
73 теорем Lean4: что именно доказано, на какие аксиомы опирается и что доказательства не покрывают.
Установка
pip install balansis, минимальные зависимости, CLI и первые вызовы API.
Честность
Как повторить каждое число на этой странице и где документация самой библиотеки расходится с прогоном.
Время в таблицах — минимум из серии повторов на разделяемом хосте (Intel Xeon (серверный), 8 vCPU); наименьшее время наименее зашумлено, отношения между методами тоже зависят от окружения. Самый быстрый и самый медленный замер сценария скалярного произведения: 643.0 нс и 87.65 мкс.