Я думаю, всі присутні так чи інакше стикалися з комплексними числами, якщо не в школі, то у ВУЗі так точно. Як на мене, це просто дивовижно, наскільки багата система постає з простого додавання "нового числа" \(i^2 = -1\): всі поліноми тепер мають корені (основна теорема алгебри), всі ці інтеграли по контуру, монодромії, конформні відображення і т.д. Що цікаво, схоже, що комплексні числа не лише мають широке застосування в науці та техніці, а і взагалі вплетені в структуру нашої реальності: наприклад, не вийде побудувати квантову механіку без комплексних чисел.
У дійсній квантовій механіці втрачається природний зв'язок між генераторами симетрій та спостережуваними величинами: у комплексній теорії множення на \(i\) перетворює антиермітів оператор (генератор) на ермітів (спостережувану), тоді як у дійсній теорії цієї можливості немає. Але що важливіше: вдалось знайти випадки, коли обидві теорії дають різні передбачення, наприклад рівня кореляції у багаточастинкових системах, та виміряти їх експериментально (напр. стаття "Testing real quantum theory in an optical quantum network." Phys. Rev. Lett. 128, 040402 (2022)) — комплексні числа перемогли.
Але сьогодні я хочу поговорити про їх менш відомих родичів. Давайте для початку піднімемось на один "філософський" рівень вище і задамось питанням, що цікавого можна витрясти з конструкцій типу \(a + b \cdot \text{[SOMETHING]}\), де \(a \in \mathbb{R}\), \(b \in \mathbb{R}\), а \(\text{[SOMETHING]}\) — це щось, що можна хоча б множити на себе. Якщо просто поставити замість \(\text{[SOMETHING]}\) якийсь \(x\) — прийдемо до кільця поліномів, що знаходить своє застосування в дуже багатьох областях, але програмістам як мінімум будуть близькі теорія кодування чи криптографія. Також ми вже бачили, наскільки плідною може бути система \(\text{[SOMETHING]}^2 = -1\). Виникає природне питання: а що, якщо \(\text{[SOMETHING]}^2 = -2\)? А якщо \(-10\) ?
Як виявляється (з теореми про класифікацію двовимірних алгебр над полем дійсних чисел), якщо \(\text{[SOMETHING]}^2 \in \mathbb{R}\), то маємо справу з системою, що легко зводиться до одного з трьох випадків, які фундаментально відрізняються між собою:
Як бачимо, тут є багато цікавого, але я хотів би особливо виділити механізм розрахунку значень похідних, заснований на дуальних числах. Ця схема використовується у бібліотеці Ceres та всьому, що надбудовано на ній, — структура "Jet" є прямою реалізацією дуальних чисел та їх узагальненням на багато змінних. Ceres використовує цей математичний апарат для forward-mode автодиференціювання, що дозволяє розраховувати значення якобіанів для складних задач нелінійної оптимізації за один прохід без значних втрат точності.
Також дуальні числа відіграють ключову роль у сучасному диференційовному програмуванні, зокрема у диференційовних симуляторах фізики (на кшталт Brax, MuJoCo чи Taichi). Це пов'язано з необхідністю обчислювати похідні для великої кількості виходів відносно малої кількості входів (наприклад, невелика кількість керуючих параметрів перетворюється у тисячі координат частинок): типове зворотне поширення помилки (backpropagation) вимагатиме перезапуску на кожен вихід, тоді як пряме автодиференціювання — на кожен вхід. Відповідні інструменти зараз активно розвиваються: їх можна знайти у бібліотеці JAX від Google, а також нещодавно з'явилась підтримка такого режиму і в PyTorch (torch.autograd.forward_ad).
Ну що ж, давайте розбиратися, як це працює.
Почнемо з улюблених функцій людства — поліномів; що буде, якщо у функцію \(f(z) = z^n\) підставити \(z = x + \varepsilon\), де \(\varepsilon^2 = 0\)? $$ \begin{align*} f(x + \varepsilon) &= (x + \varepsilon)^n = \sum_{k=0}^n \binom{n}{k} x^{n-k} \varepsilon^k =\\ &= \binom{n}{0} x^n + \binom{n}{1} x^{n-1} \varepsilon + \underbrace{\binom{n}{2} x^{n-2} \varepsilon^2 + \dots + \varepsilon^n}_{=0}\\ &= x^n + n x^{n-1} \varepsilon. \end{align*} $$ Уважний читач може звернути увагу на схожість виразу при \(\varepsilon\) на похідну \(f'(x)\); та чи має це практичну цінність, чи це просто курйоз для однієї конкретної функції? Не важко бачити, що для довільного полінома також буде виконуватись
$$ P(x + \varepsilon) = P(x) + P'(x) \varepsilon, $$ оскільки поліном — це зважена сума степенів \(x\). Але маючи таке твердження для поліномів, ми вже вправі очікувати чогось подібного і для рядів Тейлора, тож як мінімум усі функції в області своєї аналітичності повинні володіти властивістю \(f(x + \varepsilon) = f(x) + f'(x) \varepsilon\), а цього вже більш ніж достатньо, щоб усе це стало цікавим з практичної точки зору — ми вже отримали право писати \(\sin(x + \varepsilon) = \sin(x) + \cos(x)\varepsilon\).Насправді, написану формулу можна проштовхнути ще далі: достатньо, щоб функція була двічі диференційовна. Для любителів формул доведення під катом.
За фундаментальною теоремою математичного аналізу точний приріст гладкої функції можна записати $$ f(x + h) = f(x) + h \int_0^1 f'(x + sh) ds. $$ Якщо застосувати до цього інтеграла інтегрування частинами, ми строго виділимо лінійну частину і квадрат приросту $$ f(x + h) = f(x) + h f'(x) + h^2 \int_0^1 (1 - s) f''(x + sh) ds. $$ Розширюючи визначення нашої функції з поля \(\mathbb{R}\) на кільце дуальних чисел, ми формально підставляємо \(h = \varepsilon\) і отримуємо $$ f(x + \varepsilon) = f(x) + f'(x)\varepsilon. $$
Введена нами система може здатися схожою на нестандартний аналіз, коли дійсні числа розширюють до неархімедового поля \(R^*\), додаючи нескінченно малу \(\delta\). Проте вона ближча до синтетичної диференціальної геометрії: аналіз Робінсона працює в рамках класичної логіки першого порядку та породжує "чесне" поле, хай і неархімедове, тоді як синтетична диференціальна геометрія в рамках інтуїціоністської логіки відмовляється від закону виключеного третього та змінює саме поняття функції: усі внутрішні функції в ній автоматично є гладкими, а доведене нами твердження \(f(x + \varepsilon) = f(x) + f'(x)\varepsilon\) — стає базовою аксіомою (це одне з поширених формулювань аксіоми Кока-Ловіра).
Ні, вбивати ми нікого не будемо (принаймні поки що), просто пограємося з демо. Для початку давайте просто порівняємо три варіанти взяття похідної: аналітичний за допомогою бібліотеки math.js, звичну нам формулу $$ f'(x) = \lim_{\delta \to 0} \frac{f(x+\delta) - f(x)}{\delta} $$ та дуальні числа. Введіть якусь функцію в поле та спостерігайте за графіками, змінюйте значення \(\delta\), щоб спостерігати за зміною точності. Для великих значень параметру \(\delta\) скінченні різниці не зможуть зреагувати на високочастотні зміни функції, а для надто малих будуть проявлятися проблеми втрати точності. Дуальні ж числа пропонують нам безпараметричний підхід.
А тепер давайте нехай дуальні числа покажуть всю свою силу: зробимо те, чого не потягне аналітика. У наступному демо введіть якусь JS функцію, що повертає число. Не відмовляйте собі в конструкціях for та if.
Як щодо маленького челенджу? Програмісти ж люблять челенджі? Придумування нових чисел схоже на винайдення нових типів даних, то давайте напишемо такий тип даних. Заімплементуйте дуальні числа, а наступне демо містить кілька юніттестів для перевірки чотирьох арифметичних дій. Підказка: для ділення домножте чисельник і знаменник на спряжене до знаменника (рахується так само як для комплексних чисел: \((a+b\varepsilon)^* = a - b\varepsilon\)).
Подивіться, що ми з вами тут щойно навернули: придумали \(\varepsilon\), яке не нуль, але і трошки нуль, набудували на цьому якусь теорію, а вона ще й виявилася практично корисною... Хочеться закінчити словами Георга Кантора: Das Wesen der Mathematik liegt in ihrer Freiheit — сутність математики полягає в її свободі.