Balansis · численные вычисления на Python · AGPL-3.0 / коммерческая

Контролируйте потерю точности и особые случаи в расчётах

Balansis — библиотека для разработчиков численных алгоритмов. Она помогает суммировать слагаемые при сильном взаимном уничтожении и задавать, что делать при делении на ноль: остановить расчёт, сохранить особое состояние или применить предел. Выберите нужную операцию, сравните с Python и NumPy на своих данных и проверьте цену дополнительной обработки.

Для обычной точной суммы часто достаточно math.fsum, для быстрых матриц — NumPy, для десятичных денег — Decimal. Balansis полезен, когда нужны компенсированные произведения или явная политика особых состояний. Обёртка числа сама по себе не добавляет точности.

Измерено 2026-09-08balansis 1.1.0 с PyPIpython 3.12.3numpy 1.26.4Lean4: 73/73 теорем проверено 2026-09-08

Ниже показаны результаты прогона опубликованной версии 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)
Скалярное произведение двух векторов длины 1000: точный ответ -0.530562605606728 при числе обусловленности ≈ 4.62e+18. Эталон вычислен в рациональной арифметике (fractions.Fraction), а не в float повышенной точности. Измерено 2026-09-08
МетодРезультатВерных знаковАбс. ошибкаВремя (min)
наивный цикл float64-320.031.469437.81 мкс
numpy.dot (BLAS)-320.031.4694643.0 нс
math.fsum над произведениями
точная сумма округлённых произведений — ошибка остаётся
-8.374969482421880.07.8444151.89 мкс
Balansis dot2 (Ogita–Rump–Oishi)
two_product ловит ошибку каждого произведения, math.fsum складывает всё точно
-0.53056260560672816 (предел float64)4.478e-1787.65 мкс
Balansis numpy_integration.compensated_dot_product
публичный numpy-мост, внутри тот же dot2
-0.53056260560672816 (предел float64)4.478e-1787.57 мкс
Результат этого прогона. NumPy даёт -32 вместо -0.530562606, а 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 на выбранных входах. Они помогают выбрать, что проверять на своей задаче; результаты другого релиза могут отличаться.

Восемь прогнанных сценариев и отдельно — аудит Lean4. Подробности в разделах «Примеры» и «Бенчмарки». Измерено 2026-09-08
ЗадачаИтог в этом прогонеИзмеренный факт
Плохо обусловленное скалярное произведениеBalansisоба метода Balansis дают ближайший float64 к эталону на этих входах; сравнение с math.sumprod не проведено
Деление на ноль как типизированное состояниеBalansisтри состояния и три политики против ZeroDivisionError и тихого nan
Сумма ряда со взаимным уничтожениемничья по точностиmath.fsum, встроенный sum() и Balansis дают точный ответ; наивный цикл — 0. Balansis при этом 25× медленнее math.fsum
Softmax на переполняющих логитахничья со стандартным приёмомсдвиг на максимум в numpy даёт тот же ответ до последнего бита; выигрыш только против наивной реализации, которая возвращает NaN
Бухгалтерский ноль на суммах вроде 12345.67Decimal, не BalansisLedger приводит Decimal к float на входе и повторяет ошибку float; структурный ноль появляется только там, где и float даёт ноль
SVDnumpyбэкенд numpy_gesdd — это буквально numpy; бэкенд act_jacobi в прогоне оказался и медленнее, и чуть менее точен
Сигнал о взаимном уничтоженииэто не победаcompensated_add(1e16, −1e16) возвращает 2 при точном ответе 0 — величину в один ULP операнда: это флаг риска, а не восстановленное значение
Скорость арифметики на объектахfloatcompensated_add медленнее сложения float в 87×; примитив two_sum — только в 5.9× (обе цифры за вычетом накладных питоновского вызова)
Алгебра моделиLean473 теорем проверены ядром Lean 2026-09-08; вне стандартных аксиом Mathlib не используется ничего
Выбирайте Balansis, если ваша задача требует компенсированного скалярного произведения или явной политики особых состояний. Для обычных сумм сначала проверьте math.fsum, для произведений — math.sumprod. Ближайший float64 к точному ответу получен в показанном сценарии на Balansis 1.1.0 с PyPI и Python 3.12.3; это не гарантия корректного округления для любых входов. В этой версии при underflow промежуточных произведений или их остатков компенсация может теряться. Результат следует проверять для своих данных и версии Python.

Куда дальше

Время в таблицах — минимум из серии повторов на разделяемом хосте (Intel Xeon (серверный), 8 vCPU); наименьшее время наименее зашумлено, отношения между методами тоже зависят от окружения. Самый быстрый и самый медленный замер сценария скалярного произведения: 643.0 нс и 87.65 мкс.