0_F и ∞_F — это не числа и не новая арифметика. Это условная запись, которая используется только в рассуждениях и только в тех местах, где обычная арифметика даёт неопределённость.
Это просто ярлык происхождения, который помогает движку понять, откуда взялся ноль или бесконечность, и корректно сократить одинаковые части выражения.
В исходной формуле этих ярлыков нет — они появляются в процессе вычислений, чтобы не потерять структуру выражения.
Это не факторизация, не дуальные числа и не конструктивный анализ. Это обычная инженерная техника: служебные теги для рассуждений и расчётов, чтобы не получить NaN и не потерять смысл выражения.
для школьного полинома факторизация решает всё, и никакие надстройки там не нужны.
Я привел этот пример исключительно чтобы показать механику тегов на пальцах.
Вот реальные задачи, ради которых это создавалось:
Представь расчет конвективного члена Навье-Стокса на динамической сетке или backprop в нейросети на тысячу слоев. Вы физически не сможете факторизовать это аналитически заранее. У Вас граф вычислений генерируется в рантайме.
У Вас есть два классических пути:
1. Считать как есть в float64.
Итог: на сингулярности прилетит переполнение и NaN.
2. Использовать тяжелую символьную алгебру (типа Mathematica)
чтобы она на каждом шаге пыталась факторизовать гигантские деревья. Итог: вычисления встанут колом, потому что это комбинаторный взрыв по времени.
Вот тут и нужен RICIS.
Он не факторизует всё уравнение глобально. Он работает скорее как ленивый JIT-компилятор для сингулярностей. Система считает численно как есть, но легковесно тащит за собой выражения происхождения (промисы). И только в тот момент, когда в рантайме происходит столкновение 0_F и ∞_F, алгоритм локально схлопывает этот конкретный узел за O(1) через детерминант. Остальной граф вычислений при этом не трогается. Но сокращаются одинаковые части числителя и знаменателя. При этом нет потерь на разрядности и округлениях, конечный тип подставляется как дженерик и нужен лишь в момент компиляции выражения в делегат.
Это не новая арифметика ради арифметики, а архитектурный паттерн для вычислительных движков (солверов), чтобы они пролетали сингулярности на полной скорости там, где численные методы падают, теряют знаки, округляются, и blow up, а символьные — виснут.
Уважаемый @mayorovp, благодарю вас за развернутый комментарий и очень показательный пример.
Вы совершенно правы в том, что дуальные числа (где ε² = 0) — это мощный и элегантный математический аппарат. В инженерной практике мы действительно часто применяем их, например, для прямого автоматического дифференцирования, чтобы избежать громоздких символьных вычислений.
Однако в вашем примере применение индексов к функциям f и g как к цельным объектам действительно приводит к неопределенности. Дело в том, что RICIS работает немного на другом уровне — на уровне графа вычислений (абстрактного синтаксического дерева), и делает это до этапа окончательной подстановки значений.
Позвольте показать, как алгоритм обрабатывает ваше выражение шаг за шагом:
Исходный граф вычислений: (x² − 25) / (x − 5)
Система анализирует структуру и факторизует числитель: (x − 5)(x + 5)
При подстановке x = 5 базовый узел (x − 5) не просто стирается, а переходит в состояние типизированного нуля: 0_(x−5)
При выполнении деления система видит полное совпадение индексов происхождения (промисов) и структурно сокращает их.
Итоговый результат: 10.
Как видите, мы получаем точный ответ без появления неопределенности в мнимой или вещественной части. При этом мы остаемся в рамках действительных чисел, просто сохраняя историю формирования узла.
Отвечая на ваш справедливый вопрос «зачем всё это было»: на простых полиномах это действительно выглядит как обычная школьная алгебра. Но ценность этого подхода раскрывается при численном моделировании сложных нелинейных систем, таких как уравнения Навье-Стокса.
Когда в конвективном члене возникают сингулярности (один тензор стремится к бесконечности, а другой к нулю), классические численные методы в машинной арифметике часто выдают переполнение (Overflow) или NaN. Дуальные числа в таких предельных случаях тоже могут не спасти от потери размерности в вещественной части.
RICIS же позволяет алгоритму на уровне структуры увидеть, что эти ноль и бесконечность порождены одним и тем же геометрическим инвариантом. Система корректно редуцирует их до конечной величины еще до того, как процессор попытается выполнить деление на ноль.
Это ни в коем случае не замена дуальным числам или классическому математическому анализу. Это скорее инженерное дополнение для сохранения связей (своеобразных foreign keys) внутри вычислительного контура, чтобы повысить устойчивость алгоритмов.
Буду искренне рад услышать ваше мнение о такой механике.
F(x)=(x^2-25)/(x-5) = (x-5)*(x+5)/(x-5) видим одинаковые части в числителе и знаменателе , сокращаем , остается (x+5) ;
при x=5 далее его можем использовать с другими сингулярностями так как происхождение известно.
То есть сокращать умножать делить вычитать. Это для удобства записывается как F(x) = (inf_{x+5} при x=5) это не неопределенность есть след. умножить на обратное ему например. получив результат =1; то есть F-> отложенное выражение которое мы упрощаем далее при редукции. Может все таки удобнее читать когда ИИ мне помогает писать?
Мысли и идея мои, все работает и сходится с классикой, но идет дальше, ИИ оформляет, я долго обучал модель работать с этой системой, по умолчанию она по классике и сопротивлялась логике. Но я переобучил ее чисто общаясь с ней в чате, бесплатном. Теперь любые ИИ соглашаются с логикой за 1 json. Там разумеется короткие токены, и модель иногда забывается. Но нет возможности писать длинные ответы. Все это есть в DOI.
0serg, вы привели sinc как пример, где пределы работают. Никто не спорит. Но вот вопрос: зачем пределы вообще нужны в этом случае?
Они нужны потому, что в декартовых координатах sin(x)/x даёт 0/0. А теперь переведите в полярные — и этой проблемы нет. Не потому что мы её спрятали, а потому что в полярной системе нет оси, на которой координата обращается в ноль при умножении. Сингулярность не «решается» — она не возникает.
Индексированный ноль делает то же самое, только алгебраически. 0_{sin(x)} и 0_x — нули с разных «осей». Их произведение корректно, как произведение двух компонент в полярных координатах. Если нули с одной оси — сокращаются. Если с разных — корректны.
Пределы — механизм исправления проблемы, которую создаёт декартова система. Индексированные нули — механизм, который проблему не создаёт. Это не замена пределов, это другой слой: там, где пределы работают (sinc) — индексы дают тот же ответ. Там, где пределы буксуют (нелинейные PDE, конвективный член Навье-Стокса) — индексы работают без линеаризации.
И да, это формализовано в Lean. Не абстрактная идея — компилируемый код с нулевыми аксиомами.
Когда ты пишешь обычный ноль — это просто «0». Откуда он взялся — забыто. Система амнезирует.
А 0_F — это тот же ноль, только с запиской: «я получился из вот этой штуки F».
F — это просто напоминалка, откуда ноль пришёл. Например:
ноль из (x−5) → 0_(x-5)
ноль из (x²−25) → 0_(x²-25)
Зачем это нужно? Потому что когда два нуля встречаются, классика орёт «неопределённость!» и падает. А если у нулей есть записки — видно, одинаковые они или разные.
Если записки одинаковые — сокращаем, получаем 1. Если разные — тогда уже настоящая неопределённость.
Мы ничего не выдумываем сверх формулы. Мы просто не выкидываем информацию, откуда ноль взялся. Классика выкидывает. Мы — нет.
не получим 1= 0 ? так как запишем F/0 как inf_F а o*F как 0_F просто для удобства, где F это промис выражения. и решим например (8/0):(4/0) => (8/0 ) * (0/4) => (это промис ноли сокращаем) => 8/4=2 пределы дают этот же ответ но приблизительно. теперь обобщим каждую часть и получим снова 0_F И inf_F
вышесказанное работает только для сингулярных случаев и создано специально для этого. Тех случаев, где классика вообще говорит Undefined. Так что ничего не ломается, это как смотреть на прямую через стеклянный цилиндр. вроде и поломалась прямая а по факту - целая.
Пока все спорят, кто у кого украл подход, никто не задаёт простой вопрос: а стена ли это вообще?
OpenAI доказала blowup в Навье–Стоксе. Но давайте посмотрим, откуда вообще берётся эта стена.
Что такое ε — очень просто Представь, что у тебя есть число, которое очень-очень маленькое, но не ноль. Настолько маленькое, что для обычной жизни это почти ноль. В математике его называют ε (эпсилон). Это не абстракция ради абстракции: в численных расчётах, в симуляциях, в нейросетях это просто очень маленькое число — скажем, 10⁻¹⁰ или 10⁻²⁰. Оно настолько близко к нулю, что если ты просто округлишь его до 0, то в обычной арифметике почти ничего не изменится.
Но в уравнениях типа Навье–Стокса почти ноль — это ловушка. Потому что дальше с этим числом делают операции, где важна не величина, а откуда оно пришло.
Классика стирает путь Классический анализ делает так: берёт предел lim(x→0) x и говорит: результат — 0. Точка. Дальше в уравнении этот ноль живёт как просто 0. Откуда он пришёл, из какой функции, с какой стороны — забыто.
Дальше этот просто 0 встречается с чем-то большим. Например, с величиной, которая ведёт себя как 1/ε — то есть очень большая, почти бесконечность. И получается 0 · ∞. А классическая арифметика говорит: это неопределённость. Уравнение ломается.
Проблема не в том, что числа плохие. Проблема в том, что мы стёрли ε, превратив его в безликий 0, и потеряли связь между ними.
Если не стирать ε А теперь давай не стирать. Пусть 0 — это не конец, а очень маленькое ε из функции F. А бесконечность — это не абстрактная ∞, а F/ε, то есть та же самая функция F, делённая на это маленькое ε.
Тогда произведение выглядит так:
0 𝐹 ⋅ ∞ 𝐹
𝜀 ⋅ 𝐹 𝜀
𝐹 0 F ⋅∞ F =ε⋅ ε F =F Никаких неопределённостей. Просто ε сокращается, и остаётся исходная функция F. Это не новая математика — это обычная алгебра: 𝜀 / 𝜀
1 ε/ε=1, и 1 ⋅ 𝐹
𝐹 1⋅F=F.
То есть неопределённость возникает не потому, что уравнение плохое, а потому, что в классической записи мы потеряли индекс происхождения: мы забыли, что этот ноль и эта бесконечность связаны одной и той же функцией F.
Аналогия из баз данных: ты делаешь JOIN двух таблиц, но у тебя нет foreign key. Ты пытаешься соединить по значению, а не по связи — и получаешь мусор. Добавь ключ — те же самые данные, тот же JOIN, но всё работает. Здесь ключ — это как раз память о том, из какой функции пришёл ноль.
Что это значит для Навье–Стокса Если не стирать происхождение нуля и бесконечности, сингулярность перестаёт быть стеной. Это не значит, что уравнения другие. Это значит, что мы не теряем информацию при переходе к пределу. Blowup в классическом смысле возникает именно из‑за этой потери: мы превратили ε в 0 и потеряли связь.
А теперь попробуйте то же самое применить к члену (v·∇)v в Навье–Стоксе. Если учесть происхождение нулей и бесконечностей, построить полную систему условий и написать целевую функцию — большая часть сингулярностей сокращается и взаимно компенсируется. Мы просто перестали терять информацию.
inductive QExpr where | var (n : String) | zeroF (e : QExpr) | infF (e : QExpr) | mul (a b : QExpr) | rect (F G : QExpr) | mu (R : QExpr) deriving DecidableEq, Repr, BEq
– A6: μ(R(F,G)) = F·G def measure : QExpr → QExpr | .mu (.rect F G) => .mul F G | e => e
theorem measure_rect (F G : QExpr) : measure (.mu (.rect F G)) = .mul F G := rfl
theorem depth_unfold (d : Nat) (q : QExpr) : (unfold d q).depth = d := by induction d generalizing q with | zero => rfl | succ d ih => simp [unfold, Fractal.depth, ih]
theorem root_unfold (d : Nat) (q : QExpr) : (unfold d q).root = q := by cases d <;> rfl
– Предикат на всех узлах дерева def Fractal.allNodes (P : QExpr → Prop) : Fractal → Prop | .leaf q => P q | .node q i z a b => P q ∧ P i ∧ P z ∧ a.allNodes P ∧ b.allNodes P
theorem allNodes_mono {P Q : QExpr → Prop} (h : ∀ e, P e → Q e) : ∀ f : Fractal, f.allNodes P → f.allNodes Q | .leaf q, hp => h q hp | .node q i z a b, ⟨h1, h2, h3, h4, h5⟩ => ⟨h q h1, h i h2, h z h3, allNodes_mono h a h4, allNodes_mono h b h5⟩
– L0: происхождение узла — только обёртки ∞_·/0_· над корнем inductive DerivedFrom (root : QExpr) : QExpr → Prop | refl : DerivedFrom root root | inf {e} : DerivedFrom root e → DerivedFrom root (.infF e) | zero {e} : DerivedFrom root e → DerivedFrom root (.zeroF e)
theorem DerivedFrom.trans {q r s : QExpr} (h1 : DerivedFrom q r) (h2 : DerivedFrom r s) : DerivedFrom q s := by induction h2 with | refl => exact h1 | inf ih => exact .inf ih | zero ih => exact .zero ih
– L0_ABSOLUTE_CONTINUITY на любой конечной глубине theorem L0_no_identity_loss (d : Nat) (q : QExpr) : (unfold d q).allNodes (DerivedFrom q) := by induction d generalizing q with | zero => simp only [unfold, Fractal.allNodes] exact .refl | succ d ih => simp only [unfold, Fractal.allNodes] refine ⟨.refl, .inf .refl, .zero .refl, ?, ?⟩ · exact allNodes_mono (fun e he => (DerivedFrom.inf .refl).trans he) (ih (.infF q)) · exact allNodesmono (fun e he => (DerivedFrom.zero .refl).trans he) _ (ih (.zeroF q))
– PART_5.geometric_invariant: для каждого узла ν на глубине m – μ(R_ν) = F_ν · G_ν (диагональная ячейка узла R(q,q)) theorem geometric_invariant_all_depths (d : Nat) (q : QExpr) : (unfold d q).allNodes (fun e => measure (.mu (.rect e e)) = .mul e e) := allNodes_mono (fun _ => rfl) (L0_no_identity_loss d q)
Для иллюстрации: 0_F — это не «ноль», а бесконечно малая ε, полученная из функции F. А inf_F — это F/ε. Тогда 0_F · inf_F = ε · (F/ε) = F. Не неопределённость, а исходная функция — потому что ε не стёрт. Классика доводит ε до 0, записывает «ноль», получает 0 · ∞ — и объявляет неопределённость. RICIS не доводит: сохраняет ε как индекс происхождения. Особенно это важно при бесконечно малом ε — именно там классика теряет максимум информации.
Уравнения Навье–Стокса описывают движение жидкости — от чайника до атмосферы Юпитера. Они записаны, понятны, и при этом одно из шести нерешённых тысячелетних проблем: никто не может доказать, что решение всегда существует и гладкое.
Конкретно: нелинейный член (v·∇)v может при определённых условиях «взрываться» — производная обращается в бесконечность за конечное время. Это называется finite-time blowup. Если решение уходит в бесконечность — оно перестаёт иметь смысл. Уравнение «ломается».
Именно этот blowup OpenAI и подтвердила. Потратила колоссальные ресурсы — и показала: да, стена существует, решение действительно расходится.
Математики всего мира приняли это как факт. Обсуждают следствия: турбулентность, регуляризацию, численные методы. Никто не спрашивает: а стена ли это?
Классическая реакция: обойти
Когда уравнение ломается в бесконечности, стандартный арсенал такой:
Регуляризация — добавить малый параметр, который «сглаживает» сингулярность. Решение существует, но оно приближённое.
Clipping — обрезать значения, превышающие порог. Решение существует, но оно искажённое.
Слабые решения — изменить определение «решения», разрешив разрывы. Решение существует, но в ослабленном смысле.
Общее у всех трёх подходов: сингулярность считается врагом, и задача — от неё избавиться. Никто не спрашивает, что произойдёт, если сингулярность — не враг, а симптом потери информации.
Куда уходит информация
Вот ключевая мысль, ради которой написана эта статья.
Когда вычисление доходит до предела — скажем, lim(x→0) x или lim(x→∞) 1/x — классический анализ делает одну вещь: стирает путь. Оба предела дают ноль, и этот ноль записывается как просто 0. Откуда он пришёл — слева, справа, из какой функции — забыто.
А потом этот ноль попадает в уравнение Навье–Стокса, и там он встречается с другим нулём. И классическая арифметика говорит: «два нуля — одинаковые, делю один на другой — неопределённость». Или: «умножаю бесконечность на ноль — неопределённость». И уравнение ломается.
Но они не одинаковые. Они пришли из разных мест. И если бы мы помнили, откуда — результат был бы другим.
Промис: ноль с историей
Назовём это промисом (от англ. promise — обещание): ноль, который помнит, как был получен.
0_F — ноль из функции F
0_G — ноль из функции G
Тогда 0_F / 0_F = 1 — не новое правило, не хак. Это классическое f/f = 1, просто f здесь равно нулю, и мы не забыли, что это за f.
А 0_F / 0_G — это действительно неопределённость, потому что F ≠ G. И это честная неопределённость, а не та, что рождается амнезией.
Аналогия из мира баз данных: представьте JOIN без foreign keys. Вы пытаетесь объединить таблицы, но связи потеряны — получается «неопределённость». Добавьте ключи — те же данные, тот же JOIN, но он работает. Не потому что данные изменились, а потому что метаданные сохранены.
Типизированный ноль — это foreign key для арифметики.
Что это меняет для Навье–Стокса
Если бесконечность, в которую уходит решение, сохранить индексированной — помнящей, откуда она пришла — сингулярность перестаёт быть стеной. Она становится узлом структуры: точкой, в которой уравнение не ломается, а меняет представление.
Это не значит, что OpenAI ошиблась. В рамках классического анализа blowup реален. Но классический анализ — это частный случай, в котором промис потерян. Сохраните промис — и ответ меняется. Не потому что уравнение другое, а потому что арифметика богаче.
OpenAI доказала, что в арифметике без ключей JOIN падает. Это правда. Но если добавить ключи — JOIN работает.
Почему этому можно верить
Три критерия:
1. Формальная верификация. Ключевые утверждения проверены в Lean 4 — языке с машинно-верифицируемыми доказательствами. Ни одного sorry, ни одной axiom — только определительные равенства и инъективность конструкторов. Доказательство не зависит от доверия к автору. Его проверила машина.
2. Воспроизводимость. Ядро реализовано на C# (.NET 8), формализация — на Lean 4. Всё опубликовано с DOI на Zenodo и figshare. Любой может скачать, собрать и запустить.
3. Хронология. Работа начата в августе 2025 года — за год до объявления OpenAI. Не «придумал альтернативу после их результата», а «результат был получен и зафиксирован до того, как они начали».
Что это значит для инженера
Вы сидите над задачей, где что-то «взрывается»: градиент в нейросети, деление на ноль в рендере, сингулярность в симуляции. Первая реакция — обойти: clipping, epsilon, регуляризация. Это работает, но теряет информацию.
Альтернатива: спросить себя, откуда пришёл ноль. Не «какое число получилось», а «какой путь оно прошло, чтобы стать нулём». Если сохранить этот путь — промис — сингулярность может оказаться не стеной, а поворотом.
Не каждая проблема решается сохранением метаданных. Но привычка задавать вопрос «что мы выбросили, когда сократили это?» — полезна. Часто мы выбрасываем ровно то, что потом не можем найти.
Окно
Мы привыкли, что сингулярность — поломка. Что деление на ноль — ошибка. Что бесконечность — тупик. Это окно, в которое мы смотрим 300 лет.
Но 300 лет назад отрицательных чисел не бывало. 150 лет назад параллельные прямые не пересекались. Каждый раз сдвиг происходил не потому, что кто-то «придумал новое», а потому, что кто-то заметил, что старое теряет информацию, и решил её сохранить.
OpenAI потратила 10 000 агентов и 88 часов, чтобы найти стену. А может, стены нет. Может, мы просто забыли, откуда пришли.
0_F и ∞_F — это не числа и не новая арифметика. Это условная запись, которая используется только в рассуждениях и только в тех местах, где обычная арифметика даёт неопределённость.
Это просто ярлык происхождения, который помогает движку понять, откуда взялся ноль или бесконечность, и корректно сократить одинаковые части выражения.
В исходной формуле этих ярлыков нет — они появляются в процессе вычислений, чтобы не потерять структуру выражения.
Это не факторизация, не дуальные числа и не конструктивный анализ. Это обычная инженерная техника: служебные теги для рассуждений и расчётов, чтобы не получить NaN и не потерять смысл выражения.
Вы абсолютно правы:
для школьного полинома факторизация решает всё, и никакие надстройки там не нужны.
Я привел этот пример исключительно чтобы показать механику тегов на пальцах.
Вот реальные задачи, ради которых это создавалось:
Представь расчет конвективного члена Навье-Стокса на динамической сетке или backprop в нейросети на тысячу слоев. Вы физически не сможете факторизовать это аналитически заранее. У Вас граф вычислений генерируется в рантайме.
У Вас есть два классических пути:
1. Считать как есть в float64.
Итог: на сингулярности прилетит переполнение и NaN.
2. Использовать тяжелую символьную алгебру (типа Mathematica)
чтобы она на каждом шаге пыталась факторизовать гигантские деревья. Итог: вычисления встанут колом, потому что это комбинаторный взрыв по времени.
Вот тут и нужен RICIS.
Он не факторизует всё уравнение глобально. Он работает скорее как ленивый JIT-компилятор для сингулярностей. Система считает численно как есть, но легковесно тащит за собой выражения происхождения (промисы). И только в тот момент, когда в рантайме происходит столкновение 0_F и ∞_F, алгоритм локально схлопывает этот конкретный узел за O(1) через детерминант. Остальной граф вычислений при этом не трогается. Но сокращаются одинаковые части числителя и знаменателя. При этом нет потерь на разрядности и округлениях, конечный тип подставляется как дженерик и нужен лишь в момент компиляции выражения в делегат.
Это не новая арифметика ради арифметики, а архитектурный паттерн для вычислительных движков (солверов), чтобы они пролетали сингулярности на полной скорости там, где численные методы падают, теряют знаки, округляются, и blow up, а символьные — виснут.
Уважаемый @mayorovp, благодарю вас за развернутый комментарий и очень показательный пример.
Вы совершенно правы в том, что дуальные числа (где ε² = 0) — это мощный и элегантный математический аппарат. В инженерной практике мы действительно часто применяем их, например, для прямого автоматического дифференцирования, чтобы избежать громоздких символьных вычислений.
Однако в вашем примере применение индексов к функциям f и g как к цельным объектам действительно приводит к неопределенности. Дело в том, что RICIS работает немного на другом уровне — на уровне графа вычислений (абстрактного синтаксического дерева), и делает это до этапа окончательной подстановки значений.
Позвольте показать, как алгоритм обрабатывает ваше выражение шаг за шагом:
Исходный граф вычислений: (x² − 25) / (x − 5)
Система анализирует структуру и факторизует числитель: (x − 5)(x + 5)
При подстановке x = 5 базовый узел (x − 5) не просто стирается, а переходит в состояние типизированного нуля: 0_(x−5)
Получаем промежуточное состояние:
Числитель: 0_(x−5) · (5 + 5) = 0_(x−5) · 10
Знаменатель: 0_(x−5)
При выполнении деления система видит полное совпадение индексов происхождения (промисов) и структурно сокращает их.
Итоговый результат: 10.
Как видите, мы получаем точный ответ без появления неопределенности в мнимой или вещественной части. При этом мы остаемся в рамках действительных чисел, просто сохраняя историю формирования узла.
Отвечая на ваш справедливый вопрос «зачем всё это было»: на простых полиномах это действительно выглядит как обычная школьная алгебра. Но ценность этого подхода раскрывается при численном моделировании сложных нелинейных систем, таких как уравнения Навье-Стокса.
Когда в конвективном члене возникают сингулярности (один тензор стремится к бесконечности, а другой к нулю), классические численные методы в машинной арифметике часто выдают переполнение (Overflow) или NaN. Дуальные числа в таких предельных случаях тоже могут не спасти от потери размерности в вещественной части.
RICIS же позволяет алгоритму на уровне структуры увидеть, что эти ноль и бесконечность порождены одним и тем же геометрическим инвариантом. Система корректно редуцирует их до конечной величины еще до того, как процессор попытается выполнить деление на ноль.
Это ни в коем случае не замена дуальным числам или классическому математическому анализу. Это скорее инженерное дополнение для сохранения связей (своеобразных foreign keys) внутри вычислительного контура, чтобы повысить устойчивость алгоритмов.
Буду искренне рад услышать ваше мнение о такой механике.
F(x)=(x^2-25)/(x-5) = (x-5)*(x+5)/(x-5) видим одинаковые части в числителе и знаменателе , сокращаем , остается (x+5) ;
при x=5 далее его можем использовать с другими сингулярностями так как происхождение известно.
То есть сокращать умножать делить вычитать. Это для удобства записывается как F(x) = (inf_{x+5} при x=5) это не неопределенность есть след. умножить на обратное ему например. получив результат =1; то есть F-> отложенное выражение которое мы упрощаем далее при редукции. Может все таки удобнее читать когда ИИ мне помогает писать?
да и f(x) = sin(x) минусующий пусть нарисует на оси x одномерно
y = f(x) это не одномерная а двумерная декартова система. и одну и ту же функцию можно рассматривать и с декартовой и с полярной точки зрения, .
Мысли и идея мои, все работает и сходится с классикой, но идет дальше, ИИ оформляет, я долго обучал модель работать с этой системой, по умолчанию она по классике и сопротивлялась логике. Но я переобучил ее чисто общаясь с ней в чате, бесплатном. Теперь любые ИИ соглашаются с логикой за 1 json. Там разумеется короткие токены, и модель иногда забывается. Но нет возможности писать длинные ответы. Все это есть в DOI.
0serg, вы привели sinc как пример, где пределы работают. Никто не спорит. Но вот вопрос: зачем пределы вообще нужны в этом случае?
Они нужны потому, что в декартовых координатах sin(x)/x даёт 0/0. А теперь переведите в полярные — и этой проблемы нет. Не потому что мы её спрятали, а потому что в полярной системе нет оси, на которой координата обращается в ноль при умножении. Сингулярность не «решается» — она не возникает.
Индексированный ноль делает то же самое, только алгебраически. 0_{sin(x)} и 0_x — нули с разных «осей». Их произведение корректно, как произведение двух компонент в полярных координатах. Если нули с одной оси — сокращаются. Если с разных — корректны.
Пределы — механизм исправления проблемы, которую создаёт декартова система. Индексированные нули — механизм, который проблему не создаёт. Это не замена пределов, это другой слой: там, где пределы работают (sinc) — индексы дают тот же ответ. Там, где пределы буксуют (нелинейные PDE, конвективный член Навье-Стокса) — индексы работают без линеаризации.
И да, это формализовано в Lean. Не абстрактная идея — компилируемый код с нулевыми аксиомами.
0serg, давайте без формул, на простом языке:
Суть подхода в том, что мы не изобретаем новую математику, а просто сохраняем информацию, которую классический анализ выбрасывает.
Разберем на примере:
В обычном анализе:
Когда у нас получается x - 5 = 0, мы записываем просто "ноль"
При этом забываем, откуда этот ноль взялся
Когда встречаются два таких нуля — получаем "неопределенность"
В нашем подходе:
Если x - 5 = 0, записываем "ноль от (x-5)"
Сохраняем информацию о происхождении
При умножении:
если два нуля от одного выражения — можно сократить
если от разных — тогда действительно неопределенность
Важный момент: это не "выдумка", а структурированный подход. Он:
Проверяется программно
Работает с реальными уравнениями (например, с Навье-Стоксом)
Не противоречит классической математике, а расширяет её
Если у вас есть конкретные возражения по математике — приведите их. Фраза "вы матан забыли" — это не аргумент, а эмоция.
P.S. Система построена так, что:
Сохраняет полезную информацию
Избегает ложных неопределенностей
Работает в рамках стандартной логики
DOI:
27 августа 2026 г. ( 1.0.0 )
Препринт
Открыть
Геометрический мост в RICIS-III: локальный детерминантный инвариант для типизированных узлов 0_F × ∞_G
https://doi.org/10.5281/zenodo.22124493
Смотри, объясняю на пальцах.
Когда ты пишешь обычный ноль — это просто «0».
Откуда он взялся — забыто. Система амнезирует.
А 0_F — это тот же ноль, только с запиской:
«я получился из вот этой штуки F».
F — это просто напоминалка, откуда ноль пришёл.
Например:
ноль из (x−5) → 0_(x-5)
ноль из (x²−25) → 0_(x²-25)
Зачем это нужно?
Потому что когда два нуля встречаются, классика орёт «неопределённость!» и падает.
А если у нулей есть записки — видно, одинаковые они или разные.
Если записки одинаковые — сокращаем, получаем 1.
Если разные — тогда уже настоящая неопределённость.
Мы ничего не выдумываем сверх формулы.
Мы просто не выкидываем информацию, откуда ноль взялся.
Классика выкидывает. Мы — нет.
Вот и всё отличие.
я автор RICIS III так что ии тут только помощник в оформлении. могу DOI приложить
вот это проверяемо на: https://live.lean-lang.org/
Lean
```
import Mathlib
namespace RICIS.NavierStokes.PromiseGeometry
inductive PromiseExpr where
| atom (name : String)
| zero | one
| add (a b : PromiseExpr) | sub (a b : PromiseExpr) | mul (a b : PromiseExpr)
| deriv (d : Fin 3) (e : PromiseExpr) | timeDeriv (e : PromiseExpr) | laplace (e : PromiseExpr)
| zeroF (index : PromiseExpr) | infF (index : PromiseExpr)
deriving DecidableEq, Repr, BEq
inductive PromiseState where
| zero (index : PromiseExpr) | infinity (index : PromiseExpr)
deriving DecidableEq, Repr, BEq
instance : Inhabited PromiseExpr := ⟨.zero⟩
def GeometricVector := PromiseExpr × PromiseExpr
def determinant (u v : GeometricVector) : PromiseExpr := .sub (.mul u.1 v.2) (.mul u.2 v.1)
def zeroVector (F : PromiseExpr) : GeometricVector := (F, .zero)
def infinityVector (G : PromiseExpr) : GeometricVector := (.zero, G)
def orthogonalBridge (F G : PromiseExpr) : PromiseExpr := determinant (zeroVector F) (infinityVector G)
def normalizeGeometric : PromiseExpr → PromiseExpr
| .sub (.mul F G) (.mul .zero .zero) => .mul F G
| e => e
def reduceOrthogonal (F G : PromiseExpr) : PromiseExpr := normalizeGeometric (orthogonalBridge F G)
theorem orthogonal_bridge_normalizes (F G : PromiseExpr) :
normalizeGeometric (orthogonalBridge F G) = .mul F G := by rfl
/-- A6: indexed 0_F × ∞_G resolves to F·G by the geometric bridge. -/
def singularProduct (z i : PromiseState) : PromiseExpr :=
match z, i with
| .zero F, .infinity G => reduceOrthogonal F G
| .infinity F, .zero G => reduceOrthogonal G F
| .zero F, .zero G => .zeroF (.mul F G)
| .infinity F, .infinity G => .infF (.mul F G)
theorem a6_zero_times_infinity (F G : PromiseExpr) :
singularProduct (.zero F) (.infinity G) = .mul F G := by rfl
def VectorField := Fin 3 → PromiseExpr
def spatialDerivative (d : Fin 3) (u : PromiseExpr) : PromiseExpr := .deriv d u
def timeDerivative (u : PromiseExpr) : PromiseExpr := .timeDeriv u
def laplacian (u : PromiseExpr) : PromiseExpr := .laplace u
/-- Convection term (v·∇)v: each u_j · ∂_j u_i as an A6 singular product. -/
def convectionComponent (u : VectorField) (i : Fin 3) : PromiseExpr :=
.add (singularProduct (.zero (u 0)) (.infinity (spatialDerivative 0 (u i))))
(.add (singularProduct (.zero (u 1)) (.infinity (spatialDerivative 1 (u i))))
(singularProduct (.zero (u 2)) (.infinity (spatialDerivative 2 (u i)))))
/-- After A6 reduction, the convection term matches the standard (v·∇)v form. -/
theorem convectionComponent_is_structural (u : VectorField) (i : Fin 3) :
convectionComponent u i =
.add (.mul (u 0) (spatialDerivative 0 (u i)))
(.add (.mul (u 1) (spatialDerivative 1 (u i)))
(.mul (u 2) (spatialDerivative 2 (u i)))) := by rfl
#check a6_zero_times_infinity
#check convectionComponent_is_structural
#print axioms a6_zero_times_infinity
end RICIS.NavierStokes.PromiseGeometry
не получим 1= 0 ? так как запишем F/0 как inf_F а o*F как 0_F просто для удобства, где F это промис выражения. и решим например (8/0):(4/0) => (8/0 ) * (0/4) => (это промис ноли сокращаем) => 8/4=2 пределы дают этот же ответ но приблизительно. теперь обобщим каждую часть и получим снова 0_F И inf_F
голословный спор. предлагаю написать сингулярность и разрешить ее по классике и по RICIS III например (x^2-25)/(x-5)
вышесказанное работает только для сингулярных случаев и создано специально для этого. Тех случаев, где классика вообще говорит Undefined. Так что ничего не ломается, это как смотреть на прямую через стеклянный цилиндр. вроде и поломалась прямая а по факту - целая.
Пока все спорят, кто у кого украл подход, никто не задаёт простой вопрос: а стена ли это вообще?
OpenAI доказала blowup в Навье–Стоксе. Но давайте посмотрим, откуда вообще берётся эта стена.
Что такое ε — очень просто Представь, что у тебя есть число, которое очень-очень маленькое, но не ноль. Настолько маленькое, что для обычной жизни это почти ноль. В математике его называют ε (эпсилон). Это не абстракция ради абстракции: в численных расчётах, в симуляциях, в нейросетях это просто очень маленькое число — скажем, 10⁻¹⁰ или 10⁻²⁰. Оно настолько близко к нулю, что если ты просто округлишь его до 0, то в обычной арифметике почти ничего не изменится.
Но в уравнениях типа Навье–Стокса почти ноль — это ловушка. Потому что дальше с этим числом делают операции, где важна не величина, а откуда оно пришло.
Классика стирает путь Классический анализ делает так: берёт предел lim(x→0) x и говорит: результат — 0. Точка. Дальше в уравнении этот ноль живёт как просто 0. Откуда он пришёл, из какой функции, с какой стороны — забыто.
Дальше этот просто 0 встречается с чем-то большим. Например, с величиной, которая ведёт себя как 1/ε — то есть очень большая, почти бесконечность. И получается 0 · ∞. А классическая арифметика говорит: это неопределённость. Уравнение ломается.
Проблема не в том, что числа плохие. Проблема в том, что мы стёрли ε, превратив его в безликий 0, и потеряли связь между ними.
Если не стирать ε А теперь давай не стирать. Пусть 0 — это не конец, а очень маленькое ε из функции F. А бесконечность — это не абстрактная ∞, а F/ε, то есть та же самая функция F, делённая на это маленькое ε.
Тогда произведение выглядит так:
0 𝐹 ⋅ ∞ 𝐹
𝜀 ⋅ 𝐹 𝜀
𝐹 0 F ⋅∞ F =ε⋅ ε F =F Никаких неопределённостей. Просто ε сокращается, и остаётся исходная функция F. Это не новая математика — это обычная алгебра: 𝜀 / 𝜀
1 ε/ε=1, и 1 ⋅ 𝐹
𝐹 1⋅F=F.
То есть неопределённость возникает не потому, что уравнение плохое, а потому, что в классической записи мы потеряли индекс происхождения: мы забыли, что этот ноль и эта бесконечность связаны одной и той же функцией F.
Аналогия из баз данных: ты делаешь JOIN двух таблиц, но у тебя нет foreign key. Ты пытаешься соединить по значению, а не по связи — и получаешь мусор. Добавь ключ — те же самые данные, тот же JOIN, но всё работает. Здесь ключ — это как раз память о том, из какой функции пришёл ноль.
Что это значит для Навье–Стокса Если не стирать происхождение нуля и бесконечности, сингулярность перестаёт быть стеной. Это не значит, что уравнения другие. Это значит, что мы не теряем информацию при переходе к пределу. Blowup в классическом смысле возникает именно из‑за этой потери: мы превратили ε в 0 и потеряли связь.
А теперь попробуйте то же самое применить к члену (v·∇)v в Навье–Стоксе. Если учесть происхождение нулей и бесконечностей, построить полную систему условий и написать целевую функцию — большая часть сингулярностей сокращается и взаимно компенсируется. Мы просто перестали терять информацию.
import Mathlib
namespace RICIS3.Fractal
inductive QExpr where | var (n : String) | zeroF (e : QExpr) | infF (e : QExpr) | mul (a b : QExpr) | rect (F G : QExpr) | mu (R : QExpr) deriving DecidableEq, Repr, BEq
– A6: μ(R(F,G)) = F·G def measure : QExpr → QExpr | .mu (.rect F G) => .mul F G | e => e
theorem measure_rect (F G : QExpr) : measure (.mu (.rect F G)) = .mul F G := rfl
– PART_5: R(Q) = {Q, T(Q), ∞_Q, 0_Q, R(∞_Q), R(0_Q)} inductive Fractal where | leaf (q : QExpr) | node (q infQ zeroQ : QExpr) (rInf rZero : Fractal) deriving Repr
def unfold : Nat → QExpr → Fractal | 0, q => .leaf q | d + 1, q => .node q (.infF q) (.zeroF q) (unfold d (.infF q)) (unfold d (.zeroF q))
def Fractal.depth : Fractal → Nat | .leaf _ => 0 | .node _ _ _ a b => max a.depth b.depth + 1
def Fractal.root : Fractal → QExpr | .leaf q => q | .node q _ _ _ _ => q
theorem depth_unfold (d : Nat) (q : QExpr) : (unfold d q).depth = d := by induction d generalizing q with | zero => rfl | succ d ih => simp [unfold, Fractal.depth, ih]
theorem root_unfold (d : Nat) (q : QExpr) : (unfold d q).root = q := by cases d <;> rfl
theorem unfold_succ (d : Nat) (q : QExpr) : unfold (d + 1) q = .node q (.infF q) (.zeroF q) (unfold d (.infF q)) (unfold d (.zeroF q)) := rfl
– Предикат на всех узлах дерева def Fractal.allNodes (P : QExpr → Prop) : Fractal → Prop | .leaf q => P q | .node q i z a b => P q ∧ P i ∧ P z ∧ a.allNodes P ∧ b.allNodes P
theorem allNodes_mono {P Q : QExpr → Prop} (h : ∀ e, P e → Q e) : ∀ f : Fractal, f.allNodes P → f.allNodes Q | .leaf q, hp => h q hp | .node q i z a b, ⟨h1, h2, h3, h4, h5⟩ => ⟨h q h1, h i h2, h z h3, allNodes_mono h a h4, allNodes_mono h b h5⟩
– L0: происхождение узла — только обёртки ∞_·/0_· над корнем inductive DerivedFrom (root : QExpr) : QExpr → Prop | refl : DerivedFrom root root | inf {e} : DerivedFrom root e → DerivedFrom root (.infF e) | zero {e} : DerivedFrom root e → DerivedFrom root (.zeroF e)
theorem DerivedFrom.trans {q r s : QExpr} (h1 : DerivedFrom q r) (h2 : DerivedFrom r s) : DerivedFrom q s := by induction h2 with | refl => exact h1 | inf ih => exact .inf ih | zero ih => exact .zero ih
– L0_ABSOLUTE_CONTINUITY на любой конечной глубине theorem L0_no_identity_loss (d : Nat) (q : QExpr) : (unfold d q).allNodes (DerivedFrom q) := by induction d generalizing q with | zero => simp only [unfold, Fractal.allNodes] exact .refl | succ d ih => simp only [unfold, Fractal.allNodes] refine ⟨.refl, .inf .refl, .zero .refl, ?, ?⟩ · exact allNodes_mono (fun e he => (DerivedFrom.inf .refl).trans he) (ih (.infF q)) · exact allNodesmono (fun e he => (DerivedFrom.zero .refl).trans he) _ (ih (.zeroF q))
– PART_5.geometric_invariant: для каждого узла ν на глубине m – μ(R_ν) = F_ν · G_ν (диагональная ячейка узла R(q,q)) theorem geometric_invariant_all_depths (d : Nat) (q : QExpr) : (unfold d q).allNodes (fun e => measure (.mu (.rect e e)) = .mul e e) := allNodes_mono (fun _ => rfl) (L0_no_identity_loss d q)
#guard (unfold 3 (.var “Q”)).depth == 3 #guard (unfold 3 (.var “Q”)).root == .var “Q”
end RICIS3.Fractal
Для иллюстрации: 0_F — это не «ноль», а бесконечно малая ε, полученная из функции F. А inf_F — это F/ε. Тогда 0_F · inf_F = ε · (F/ε) = F. Не неопределённость, а исходная функция — потому что ε не стёрт. Классика доводит ε до 0, записывает «ноль», получает 0 · ∞ — и объявляет неопределённость. RICIS не доводит: сохраняет ε как индекс происхождения. Особенно это важно при бесконечно малом ε — именно там классика теряет максимум информации.
Уравнения Навье–Стокса описывают движение жидкости — от чайника до атмосферы Юпитера. Они записаны, понятны, и при этом одно из шести нерешённых тысячелетних проблем: никто не может доказать, что решение всегда существует и гладкое.
Конкретно: нелинейный член (v·∇)v может при определённых условиях «взрываться» — производная обращается в бесконечность за конечное время. Это называется finite-time blowup. Если решение уходит в бесконечность — оно перестаёт иметь смысл. Уравнение «ломается».
Именно этот blowup OpenAI и подтвердила. Потратила колоссальные ресурсы — и показала: да, стена существует, решение действительно расходится.
Математики всего мира приняли это как факт. Обсуждают следствия: турбулентность, регуляризацию, численные методы. Никто не спрашивает: а стена ли это?
Классическая реакция: обойти
Когда уравнение ломается в бесконечности, стандартный арсенал такой:
Регуляризация — добавить малый параметр, который «сглаживает» сингулярность. Решение существует, но оно приближённое.
Clipping — обрезать значения, превышающие порог. Решение существует, но оно искажённое.
Слабые решения — изменить определение «решения», разрешив разрывы. Решение существует, но в ослабленном смысле.
Общее у всех трёх подходов: сингулярность считается врагом, и задача — от неё избавиться. Никто не спрашивает, что произойдёт, если сингулярность — не враг, а симптом потери информации.
Куда уходит информация
Вот ключевая мысль, ради которой написана эта статья.
Когда вычисление доходит до предела — скажем, lim(x→0) x или lim(x→∞) 1/x — классический анализ делает одну вещь: стирает путь. Оба предела дают ноль, и этот ноль записывается как просто 0. Откуда он пришёл — слева, справа, из какой функции — забыто.
А потом этот ноль попадает в уравнение Навье–Стокса, и там он встречается с другим нулём. И классическая арифметика говорит: «два нуля — одинаковые, делю один на другой — неопределённость». Или: «умножаю бесконечность на ноль — неопределённость». И уравнение ломается.
Но они не одинаковые. Они пришли из разных мест. И если бы мы помнили, откуда — результат был бы другим.
Промис: ноль с историей
Назовём это промисом (от англ. promise — обещание): ноль, который помнит, как был получен.
0_F — ноль из функции F
0_G — ноль из функции G
Тогда 0_F / 0_F = 1 — не новое правило, не хак. Это классическое f/f = 1, просто f здесь равно нулю, и мы не забыли, что это за f.
А 0_F / 0_G — это действительно неопределённость, потому что F ≠ G. И это честная неопределённость, а не та, что рождается амнезией.
Аналогия из мира баз данных: представьте JOIN без foreign keys. Вы пытаетесь объединить таблицы, но связи потеряны — получается «неопределённость». Добавьте ключи — те же данные, тот же JOIN, но он работает. Не потому что данные изменились, а потому что метаданные сохранены.
Типизированный ноль — это foreign key для арифметики.
Что это меняет для Навье–Стокса
Если бесконечность, в которую уходит решение, сохранить индексированной — помнящей, откуда она пришла — сингулярность перестаёт быть стеной. Она становится узлом структуры: точкой, в которой уравнение не ломается, а меняет представление.
Это не значит, что OpenAI ошиблась. В рамках классического анализа blowup реален. Но классический анализ — это частный случай, в котором промис потерян. Сохраните промис — и ответ меняется. Не потому что уравнение другое, а потому что арифметика богаче.
OpenAI доказала, что в арифметике без ключей JOIN падает. Это правда. Но если добавить ключи — JOIN работает.
Почему этому можно верить
Три критерия:
1. Формальная верификация. Ключевые утверждения проверены в Lean 4 — языке с машинно-верифицируемыми доказательствами. Ни одного sorry, ни одной axiom — только определительные равенства и инъективность конструкторов. Доказательство не зависит от доверия к автору. Его проверила машина.
2. Воспроизводимость. Ядро реализовано на C# (.NET 8), формализация — на Lean 4. Всё опубликовано с DOI на Zenodo и figshare. Любой может скачать, собрать и запустить.
3. Хронология. Работа начата в августе 2025 года — за год до объявления OpenAI. Не «придумал альтернативу после их результата», а «результат был получен и зафиксирован до того, как они начали».
Что это значит для инженера
Вы сидите над задачей, где что-то «взрывается»: градиент в нейросети, деление на ноль в рендере, сингулярность в симуляции. Первая реакция — обойти: clipping, epsilon, регуляризация. Это работает, но теряет информацию.
Альтернатива: спросить себя, откуда пришёл ноль. Не «какое число получилось», а «какой путь оно прошло, чтобы стать нулём». Если сохранить этот путь — промис — сингулярность может оказаться не стеной, а поворотом.
Не каждая проблема решается сохранением метаданных. Но привычка задавать вопрос «что мы выбросили, когда сократили это?» — полезна. Часто мы выбрасываем ровно то, что потом не можем найти.
Окно
Мы привыкли, что сингулярность — поломка. Что деление на ноль — ошибка. Что бесконечность — тупик. Это окно, в которое мы смотрим 300 лет.
Но 300 лет назад отрицательных чисел не бывало. 150 лет назад параллельные прямые не пересекались. Каждый раз сдвиг происходил не потому, что кто-то «придумал новое», а потому, что кто-то заметил, что старое теряет информацию, и решил её сохранить.
OpenAI потратила 10 000 агентов и 88 часов, чтобы найти стену. А может, стены нет. Может, мы просто забыли, откуда пришли.