Как понять, что факторизация числа 15 не может ничего говорить о реализации квантового алгоритма Шора? Понять это несложно: один из делителей числа 15 должен быть меньше 4 (потому что 4^2 == 16), единица не рассматривается по условиям задачи, и это не 2 (потому что только нечётные подходят). Так что любой процесс поиска, каким бы аналоговым он ни был, если вообще сходится, то неизбежно попадёт в 3, что и будет верным ответом.

Заметьте, что ещё и 5 = 3 + 2, а простых чисел, меньших 15, только шесть: поэтому, учитывая, что умножение здесь коммутативно (это очень важно для квантовых алгоритмов), число 2 отбрасывается, а схема поиска расщепляется на пары, то, в самом худшем случае, вероятность, что аналоговый аппарат, состояния которого переключаются по возможным узлам дерева, промахнётся – меньше трети. (На практике, ещё раз, для промахов там просто нет места.)



Комментировать »

Недавняя записка про оптимизацию циклов и микроконтроллеры иллюстрирует весьма важный момент, связанный с пониманием алгоритмов, записи алгоритмов и реализаций этих алгоритмов. Не менее рельефный пример в этом же направлении касается базовых логических операций и понимания записи формул. Речь про вопрос из области логики, логических выражений: является ли P и ¬¬P – одним и тем же? Даже разработчики-программисты нередко говорят, что “да, является”.

Знак “¬” – это логическое отрицание, проще говоря – “не”, или “!”, как будет во многих привычных языках. То есть, казалось бы, тут, во второй части, написано, что “не-не-P”, а тогда, если P имеет значение TRUE, то “не-не-P” тоже TRUE; то же верно и для P == FALSE. Понятное двойное отрицание, поэтому, что P, что ¬¬P – разницы нет: одно и то же. Это, впрочем, не совсем так.

Использование дополнительной “закорючки” (“¬”) позволило реализовать два способа записи, которые точно отличаются. Как способ записи, как “формула”, “P” и “¬¬P” – сильно разные вещи. Посудите сами: во втором случае на два “знака-закорючки” больше; кроме того – для выполнения “сокращения” нужно ввести дополнительное правило, по которому двойное отрицание вообще схлопывается, а чтобы это правило сработало, потребуется его внести “в набор допустимых операций” и применить, то есть, продумать и оптимизировать, даже если не сравнивать именно две записи. Так что “¬¬P” приносит с собой много разного, отсутствующего в “P”.

Понятно, что тут очень многие соглашения вынесены за скобки, но, тем не менее, это хороший пример базовых свойств процесса оптимизации. Да, компилятор мог бы заменить ¬¬P на P. Почти что то же самое компилятор и проделывает, когда заменяет запись цикла, тело которого не вносит изменений в результаты работы фрагмента кода, на простое присваивание:

b = 14; 
for(a = 0; a < 8; a++){ 
 b = b + a;
} 

заменяется на эквивалентное

b = 42;

(или это не эквивалентная замена?)

Вообще, запись алгоритма, содержащая определение цикла, описывает повтор неких операторов/команд, но не определяет, как этот повтор должен реализовываться на оборудовании. В принципе, тело цикла можно прямо выписать в качестве повторений команд, это известный приём оптимизации, он так и называется - "развернуть циклы". Очень часто, будучи применённым к программе на языке высокого уровня (ЯВУ), приём даёт заметный прирост производительности. Почему? Потому что исчезли накладные вычислительные расходы. При обычной обработке представления цикла на ЯВУ - появляются необходимые машинные команды, соответствующие "записи" цикла, а процессор вынужден их исполнять. Команд может оказаться неожиданно много: например, потребуется сложная обвязка для реализации обработки переменной цикла.

Занятно, что автоматическая "обратная оптимизация", по размеру кода, когда повторяющиеся логические блоки сворачиваются в циклы, - представляет трудность. Тут, впрочем, уже недалеко и до алгоритмической неразрешимости.



Комментировать »

Letter AНемного технических деталей про протокол Диффи-Хеллмана в практике интернет-коммуникаций, почему ключи остаются в трафике, и немного про симметричные шифры.

Протокол Диффи-Хеллмана (DH) подразумевает, что каждая из сторон вычисляет свой секрет, потом на основе этого секрета вычисляет собственный открытый параметр (назовём пару этих параметров A и B), передаваемый по открытому же каналу. После того, как сторонам удалось обменяться открытыми параметрами, они могут определить уже общий симметричный секрет, на основе которого вычисляется секретный ключ (чаще – ключи) для симметричного шифра. Так вот, каждый из параметров A и B содержит полную информацию, необходимую для вычисления общего секрета (пояснение, 31/08/24: речь тут про вычисление по записи трафика DH). Это так по определению протокола, иначе сторонам не удалось бы получить одинаковый секрет. Другое дело, что извлечь эту информацию, исходя из известных алгоритмов, вычислительно трудно (но возможно). В частности, не было бы предмета для постквантовой криптографии, если бы ключи “классических” реализаций DH не восстанавливались бы полностью из записи трафика. Так что ключ есть в трафике, а не “только на клиенте”. Это всё известно специалистам, а DH в криптографии и не позиционируется как “ключ только на клиенте”, чтобы это ни значило.

Заметьте, что на практике, если конкретная реализация криптосистемы уязвима, то открытые параметры DH (A, B) могут предоставлять информацию о том, как оптимизировать поиск нужного секрета – это обычный приём. “Обратной задачей DH” называют задачу определения исходного секрета по открытому параметру (А или B). Однако вовсе не обязательно уметь решать обратную задачу быстро и универсальным образом. Можно ограничиться прямой задачей: при знании исходного пользовательского секрета решение этой задачи – быстрое по определению, это важный математический момент; специально подобранные параметры и/или дефектный датчик случайных чисел могут свести перебор к минимуму, если атакующей стороне известен другой секрет или параметр, который задаёт бэкдор (намеренно созданную уязвимость). Соответственно, оптимизированный перебор проводится до тех пор, пока подбираемое значение не совпадёт с перехваченным открытым параметром – в этом полезная роль этого параметра, так как подбирать ключ симметричного шифра, обычно, посложнее.

Ситуация с симметричными шифрами, впрочем, тоже не лишена особенностей. Дело в том, что на практике нетрудно предсказать значение открытого текста, соответствующего перехваченному в трафике зашифрованному сообщению. Это вообще самая старая из минимально содержательных атак на практические криптосистемы, известная задолго до появления интернетов, однако в интернетах обретшая новые черты: нетрудно предсказать, что содержится в начальных байтах HTTP-запроса GET, отправленного браузером через TLS-соединение к веб-серверу под известным именем. (И уж тем более нетрудно определить, для того же TLS, значение индекса в симметричной криптосистеме, работающей в режиме счётчика.) Это означает, что, формально, в трафике есть и следы ключей симметричного шифра. Другое дело, что преобразование, соответствующее зашифрованию симметричным шифром, во-первых, практически одинаково работает в обе стороны (зашифрование/расшифрование), то есть, никто и не полагался на сложность обратного преобразования как элемент обеспечения стойкости; во-вторых, с точки зрения криптоанализа симметричного шифра, секретный ключ входит в состав выполняемого преобразования. Тем не менее, для симметричных шифров тоже известны различные подходы, позволяющие устроить “вычислительный бэкдор“.



Комментировать »

Papyrus P.Oxy.Hels.2Воскресное чтение манускриптов. И кусочков папирусов. Папирус P. Oxy. Hels. 2 (справа), а точнее – та его часть, которая доступна онлайн в виде фотографии, – представляет собой узкий вертикальный кусочек, содержащий окончания самых первых строк “Илиады” (1.1-22). Фрагменты слов на папирусе вполне можно прочитать и даже сравнить с едва ли не основным источником текста “Илиады” – с манускриптом Venetus A.

Venetus A датируют десятым веком (н.э.), хоть достоверно он “выявлен в источниках” только в 15 веке. Упомянутый же папирус – относится ко второму веку (тоже н.э.). Получается, что между данными источниками около восьми сотен лет. Немало. Начертания букв сильно отличаются. Кроме того, на папирусе совсем нет знаков пунктуации, пробелов и диакритических знаков. Вообще, при ближайшем рассмотрении, отсутствие дополнительных знаков представляется очень странным – экономия места не так велика, как необходимость перечитывать один и тот же фрагмент нараспев (для греческого), чтобы понять, что же тут написано. Но, как минимум, слова с кусочка папируса неплохо сходятся с текстом Venetus A.

На манускрипте между строками записаны комментарии к тексту, но и на папирусе межстрочный интервал более или менее постоянный, это позволяет маcштабировать изображения таким образом, что фрагмент текста папируса сходится с Venetus A построчно.

Например, на скриншоте ниже фрагменты папируса и манускрипта объединены так, что можно сравнить записи построчно. Это строки с первой (не уместилась начальная Μ на Venetus A) по седьмую. Конечно, слова могли отличаться, зато на папирусе β выглядит почти как в современных шрифтах, а вот буква β манускрипта больше похожа на рукописную русскую “и” – см. пятую строку папируса, часть слова βουλή (“замысел, воля”).

Manuscript and papyrus

Строки в конце кусочка папируса сходятся с Venetus A ещё лучше – см. следующий скриншот.

Manuscript and papyrus

Здесь кусочек папируса тоже положен рядом (справа). Однако, в этих строках не только Ἀχαιοὶ (“ахейцы”) присутствует на папирусе целиком (четвертая строка на скриншоте), но и если выровнять масштаб по межстрочному интервалу и сдвинуть папирус так, чтобы πάντα[ς] из второй строки фрагмента (на скриншоте) наложилась на πάντας (“всех”) Venetus A, то (ωνα) папируса совместится с (ωνα) в слове Ἀπόλλωνα (“Аполлона”) из текста Venetus A (предпоследняя строка на скриншоте), что хоть и понятно почему, но всё равно особенно занятно.

Manuscript and papyrus



Комментировать »

В современной типографике для древнегреческих текстов вопросительному знаку (“?”) графически соответствует точка с запятой (“;” – как и в новогреческом, где, формально, знак только так выглядит). В старых записях “знаков вопроса” не было. Пунктуация вообще появилась не сразу, а в старых текстах слова бывают записаны даже без пробелов.

Воскресное чтение манускриптов. Сегодня посмотрим на скриншот из манускрипта, известного как Clarke 39 (Codex Oxoniensis Clarkianus 39) – это записи сочинений Платона, на древнегреческом, конечно. Манускрипт датируют 895 годом, и там встречаются неожиданные варианты “знаков вопроса”. Фрагмент ниже (22r) относится к диалогу Платона “Критон”; уже это может показаться странным, но, в данном случае, не важно: речь пойдёт о более странных вещах – о пунктуации в средневековых манускриптах, поэтому в смысл диалога Платона не станем вдаваться.

Manuscript screenshot

Тут уже в первой строке фрагмента нетрудно найти знак, неотличимый от точки с запятой, что является большой редкостью для манускрипта девятого века н.э. Дальше – хорошо видны более экзотические варианты: двоеточие над “запятой”. На следующем скриншоте некоторые из них дополнительно отмечены зелёными стрелками.

Manuscript screenshot

В современной типографике текст между стрелками выглядит так: “τί φῄς; ταῦτα οὐχί καλῶς λέγεται;” (“Что думаешь? Это хорошо ли говорится?” – тут “точка с запятой” является вопросительным знаком).

Естественно, считается, что “запятая” тут – это вовсе не запятая, а указание на повышение тона, соответствующее вопросительной интонации, то есть, что-то вроде изогнутой стрелки. Это начертание могло послужить основой для современного знака “?”. Даже в этом манускрипте отдельный вопросительный знак используется крайне редко, далеко не во всех случаях вопросительных предложений. В древнегреческом языке вопрос можно построить при помощи специальных слов и с использованием диакритических знаков, но в неясных, – по мнению редактора, – случаях потребовалось отдельное уточнение (и оно иногда отличается от современного варианта текста). Две точки в этой записи служат для разделения высказываний участников диалога, для разделения предложений, а одна “верхняя” или “средняя” (не ясно) точка – может обозначать и окончание предложения, и паузу, так что вот эта точка тут больше и похожа на запятую, в современном понимании. Поэтому знак, указывающий на повышение тона, может быть приписан снизу как к одной точке, так и к двум.

Да, возможное отсутствие знаков препинания в исходном тексте сразу наводит на аналогию с современной перепиской, – допустим, – в мессенджерах на русском: “привет Сократ что делаешь”, “Здоров, Критон! Да вот, сижу тут, заточили меня, но есть некоторые свежие мысли” (Сократ почему-то использует знаки препинания, даже восклицательный знак, странно). Всё же, вряд ли сотрудник скриптория расшифровывал, размечая только что разработанными пунктуационными знаками, дамп базы древнего централизованного мессенждера: более распространённая точка зрения гласит, что сотрудник скриптория переписывал с папирусов и других манускриптов.

Посмотрим на увеличенный фрагмент.

Manuscript screenshot

Тут встречаются и многоуровневые “точки с запятой”, и простые “двоеточия”, так что всё сходится.

Вернёмся, впрочем, к первому фрагменту скриншота.

Manuscript screenshot

Если приглядеться, то там над первой “точкой с запятой” виден некий знак, похожий на проценты. Что он означает?

Это редакторский знак. Дело в том, что тут пропущен кусок текста, так что вопросительный знак (“;”) вообще оказался перенесён – он должен быть через восемь слов, но тоже после “δ᾽ οὔ” (как и на скриншоте), а недостающий фрагмент записан тут же, на полях (“οὐδὲ πάντων ἀλλὰ τῶν μέν, τῶν δ᾽ οὔ;”).

Manuscript screenshot

Заканчивается этот комментарий вопросительным знаком, который, как мы разобрались, неотличим от точки с запятой (настолько неотличим, что приводит к занимательным случаям в Unicode; где, впрочем, предлагается “нормализовывать” всё в один символ, в “;”).



Комментировать »

Кстати, что касается постквантовых криптосистем от NIST и “квантовых компьютеров, взламывающих современные криптосистемы”, которые, якобы, “могут появиться через десять лет” (а могут и не появиться): есть ещё ничуть не менее распространённый штамп, утверждающий, в этом контексте, что “квантовые компьютеры кардинально быстрее решают задачу факторизации”. Факторизация – это разложение данного числа на простые множители. Однако речь в данном штампе почти всегда идёт про алгоритм Шора. Технически, это разные утверждения: о скорости факторизации и – про алгоритм Шора. Что не так важно. Куда как более показательно, что никакой “квантовый компьютер” пока что вообще не решал задачу факторизации, что уж там говорить про то, чтобы решать эту задачу “кардинально быстрее”.

Без преувеличений и “раздувания хайпа” надо было бы сказать, что алгоритм Шора описывает теоретическое “квантовое преобразование”, которое позволяет снизить сложность факторизации, проводимой классическим компьютером, до полиномиальной. И тот же математический аппарат, который порождает данное “квантовое преобразование”, пока что успешно используется при интерпретации некоторых физических экспериментов. Не более того. О каком-то “кардинально быстрее” – и речи-то пока что не идёт. А вот “постквантовая стойкость” – это, в рамках термина, именно предполагаемая стойкость именно к “алгоритму Шора”.

Понятно, что стандартизованная постквантовая криптосистема может оказаться уязвимой для классического криптоанализа (и, скорее всего, так и выйдет). При этом, несмотря на прижившиеся штампы в СМИ, пока никто не продемонстрировал, как именно можно было бы реализовать алгоритм Шора с полиномиальной сложностью, что называется, в железе. Потому что те немногие эксперименты с числом 15 или чуть большим числом, на которые ссылаются много лет, в принципе не позволяют проверить ключевую часть реализации алгоритма – квантовое преобразование Фурье.

Тем не менее, пробовать построить квантовый компьютер хотя бы на 2^1024 состояний – это полезно.



Комментарии (2) »

Воскресное чтение манускриптов. В прошлом году, в заметке про кусочек папируса с фрагментом “Илиады”, я писал, что какие-то очень краткие фрагменты текстов, читаемые на папирусах, могли бы относиться к той же “Илиаде”, но их не получится так идентифицировать, поскольку недостаточно информации, а фрагменты выходят за пределы “эластичности” текста “Илиады”. Это довольно очевидное наблюдение. Тем не менее, посмотрим, можно ли на сканах манускриптов быстро найти что-то примерно подходящее, для иллюстрации. То есть, фрагменты текстов из нескольких слов, которые было бы не ясно куда отнести. Оказывается – найти можно. Для примера годится не слишком частая, но характерная фраза из записей сочинений Гомера. Скажем, кусочек из стиха, в котором упоминается Kubernetes – потому, что этот фрагмент тоже не так давно встречался на dxdt.ru, в записке про “Кибернетический след и цветовой сдвиг“. А именно: ἐνὶ οἴνοπι πόντῳ – “в тёмно-винном море”.

Ниже пара фрагментов манускриптов: Venetus A (“Илиада”, Гомер) и BnF.Grec.2771 (“Труды и дни”, Гесиод). Соответствующая фраза – выделена и там, и там.

Manuscript, screenshot

(Venetus A, 23:316)

Manuscript, screenshot

(BnF.Grec.2771, st. 622)

Первый манускрипт (Venetus A) датируется началом девятого (поправка, 2025/06/04: десятого) века, второй – десятым (всё н.э, понятно). То есть, между соответствующими манускриптами больше ста лет, согласно датировкам (поправка, 2025/06/04: меньше ста лет, конечно). При этом структура, оформление, а кроме того, что называется, “типографика” и начертания букв, очень и очень близки, что хорошо видно на скриншотах. Удачно, что интересующие нас слова в обоих случаях приходятся на конец строки. Попадись кому-то достаточно небольшой кусочек с записью только этих слов (как выделено на картинках, вместе с частями диакритических знаков и буквы из верхней строки) – различить источники по составу и по буквам текста вряд ли было бы возможно. Однако, если удалось бы прицепить какие-то ещё буквы (строки выше и ниже), то результат уже мог бы быть более избирательным.

Гесиод – из гомеровского периода, так что, наверное, ничего удивительного. Тем более, что приведённый фрагмент текста “про море” находится очень близко к описанию в “Трудах и днях” путешествия на “состязание поэтов”, которое нередко считают упоминанием состязания между самим Гесиодом и самим Гомером.



Комментировать »

В продолжение записки про ИИ Google и “серебряную медаль” Международной математической олимпиады. Там исходная задача, которую потом “решает” ИИ, прежде переводится людьми на входной язык системы машинных доказательств – то есть, на некоторый формальный язык, описывающий в определённых логических формулах целевое состояние, соответствующее задаче. Это не программа, как иногда пишут, а запись, грубо говоря, теоремы, соответствующей задаче, в формулах, которые возможно (при некоторых ограничениях) доказать в данной системе. Да, доказательство выполняется при помощи компьютерных вычислений, тем не менее, формальная запись задачи-теоремы не является программой, реализующей некий алгоритм – иначе не было бы смысла в поиске записей доказательств. И вот в “популярном изложении” необходимость такого перевода обосновывают тем, что нужно “переписать на языке, понятном системе”. Это, конечно, искажение. Но с ним связаны два важных момента.

Момент первый, – достаточно очевидный, – в том, что этот “суперпродвинутый” искусственный интеллект даже не способен прочитать условие задачи и, предположим, перевести его на формальный язык самостоятельно, хоть это и могло бы быть типичным вариантом запроса к ChatGPT. Так как для корректного перевода нужно хорошо понимать не только задачу, но и принципы построения формальных языков, выдача подобного ИИ-перевода для входа “системы ИИ” просто не подходит. Это понятно.

Есть и второй момент, который “замыливают” посильнее: перевод на формальный язык необходим для того, чтобы предлагаемые системой решения могли быть проверены на соответствие этому описанию машинным способом. То есть, так как предполагается, что будут подбираться тексты “доказательств” на формальном языке, необходим автоматический, машинный “проверятор” для этих доказательств – иначе, если решения потребуется согласовывать с человеком, то и работать это всё будет слишком медленно, поскольку образуется непреодолимый затык с проверкой решений. Так что необходима запись на формальном языке и проверяющая компьютерная система, а такая система в принципе не может проверять записи на естественном языке. Так что, понятно, это не решение олимпиадной задачи в том смысле, который тут принято присваивать слову “решение”.

Что же касается применения систем машинного (автоматического) доказательства в области теоретической (чистой) математики, то тут мнения, как говорится, сильно расходятся. Прежде всего потому, что теоретическая математика это не только не наука, но и вовсе не является такой уж “точной и строгой” областью, как, почему-то, нередко предполагают. Однако системы машинного доказательства очень полезны в некоторых прикладных математических направлениях. Например, для формальной проверки корректности (соответствия задаче) программного кода. Автоматические системы на этом направлении уже используются. Наверное, и автоматический генератор доказательств с перебором тут мог бы тоже пригодиться, как инструмент “фаззинга”. Но нужно учитывать, что во всех случаях речь, всё же, идет о компьютерных вычислениях, а не о математических операциях, поэтому соответствующие методы имеют существенные ограничения, как фундаментальные, так и вполне “локальные”, обусловленные ошибками на разных уровнях реализации.



Комментировать »

Нередко можно прочитать, что “сверхразумный ИИ” сгенерирует такие “решения и идеи”, которые человек понять не сможет, а ИИ – не сможет объяснить человеку (ну, настолько вот сложные и непонятные результаты). Это очень популярная характеристика, которую часто обозначают как одно из ключевых свойств “сверхразумного” ИИ (хотя таким свойством должна бы быть как раз способность объяснить сложное, но сейчас не об этом).

С другой стороны, в популярной литературе по теме ничуть не реже утверждается, что вот на пути к уровню “сверхразумности” – ИИ должен быстро переходить из статуса просто “сильного ИИ”, в статус “сверхразумного”, используя для этого поэтапные самоулучшения: то есть, вот такой ИИ доработал собственную модель, обновился, перешёл на более мощную версию, и тут же опять улучшил показатели, снова обновился, и так далее. Так утверждается.

Между тем, подобный “рекурсивный ИИ” мог бы работать в обратную сторону, с целью объяснения тех самых “сложных идей” из первого абзаца: вот “сверхразумный” ИИ объяснил свои идеи своей предыдущей итерации – интеллекту не менее искусственному, но который попроще, пусть и близок по “уровню понимания”, тот – предыдущей итерации, и, – опять же, – так далее, вплоть до уровня, близкого уже тем самым человекам.



Комментарии (1) »

Посмотрим на совсем небольшой фрагмент кода.

while(1){
	GP0 = 0;
	GP0 = 1;
}

Этот код предназначен для микроконтроллера PIC12F675 (один из очень популярных и очень простых микроконтроллеров) и, можно считать, написан на C (но здесь это не важно). Некоторые пояснения для тех, кто с микроконтроллерами дела не имел: микроконтроллеры предназначены для управления прочей электроникой и электротехникой, а GP0 здесь – это такой “синтаксический сахар”, а именно – способ задать логическое значение на выводе микроконтроллера при помощи специальной, зарезервированной переменной. PIC12F675 имеет несколько подходящих выводов, все они пронумерованы: GP0..GP5 (GP – это от General Purpose). Строчкам кода, приведённым выше, ещё предшествует несколько строк, настраивающих начальное состояние микроконтроллера и задающих режим работы, это здесь тоже не важно, поскольку речь пойдёт о другом.

Итак, GP0 = 0 – “записывает” в нулевой выход логический ноль, GP0 = 1 – “записывает” логическую единицу. Так что это не совсем “запись переменной”. Вариант while(1) – не менее типовой для мира микроконтроллеров способ задать бесконечный цикл. У микроконтроллера нет развесистой операционной системы. В алгоритмическом смысле, микроконтроллеры, обычно, либо спят, либо работают в бесконечном цикле while(1), иногда – то есть, очень редко, – отвлекаясь на обработку прерываний (в данном случае, считаем, что никаких прерываний и прочих неожиданностей не происходит).

Получается, что приведённый фрагмент последовательно выполняет запись ноля и единицы в логический вывод. Какой сигнал образуется на этом выводе? Пусть логические уровни задаются напряжением 0 вольт – для ноля и +5 вольт – для единицы (на практике, понятно, используется отсечение по порогу, но, опять же, это детали из области микроэлектроники). Нетрудно предположить, что некоторые предложения языка высокого уровня C аппаратурой выполняются пошагово, а значит, на выводе с номером ноль должен появиться ноль, а потом единица, а потом опять ноль, опять единица и так далее. Продолжительность импульса – ключевой момент, о нём как раз и пойдёт речь.

Обычно, даже не знакомые с аппаратурой и языками ассемблера разработчики быстро догадываются, что, пусть в исходном коде ничего про это не написано, но какая-то продолжительность переключения всё же будет наблюдаться. Если считать, что на переключение, описанное в коде, затрачивается одинаковое время, то, подключив осциллограф для вывода развёртки по времени, сможем наблюдать набор “прямоугольных” импульсов одинаковой продолжительности (“ширины”), которые разделены “ямами” той же продолжительности (соответствует состоянию “ноль”). Такой “прямоугольный” сигнал называют “меандром”.

Однако, если скомпилировать код, прошить программу в микроконтроллер, запустить его и подключить осциллограф к нужному выводу, то картина получится несколько иная, меандр не очень-то сбалансирован. Вот результат на фото ниже.
Signal graph

(Здесь я использовал компилятор Microchip MPLAB XC8 версии 2.46, в бесплатном варианте.) Единицы получились в четыре раза длиннее нолей (здесь положительное направление, как обычно, вверх). Как так вышло? Вообще, “чисто структурное” понимание исходного кода подразумевает, что while(1) лишь задаёт бесконечный цикл – это доступно компилятору, а как там работают остальные детали, какой алгоритм задуман – компилятор учитывать не может. То есть, текст на ЯВУ задаёт лишь схему итераций. Это логично, но иногда нужно знать ассемблер и то, как работает аппаратура. В данном случае, происходит следующее.

Микроконтроллер работает со скоростью миллион программных тактов в секунду. Компилятор превращает цикл с двумя присваиваниями в некоторый набор кодов команд микроконтроллера. Реализация цикла должна использовать команду безусловного перехода, которая тоже потребляет такты. При этом начало цикла содержит дополнительную команду, задающую способ адресации регистров. В итоге, получается следующий набор машинных команд, которые, в результате прошивки, выполняет микроконтроллер (псевдокод, соответствующий ассемблеру; здесь loop – это метка, по которой происходит передача управления каждый раз в конце тела цикла):

loop:
 STATUS 5      // выбор режима адресации, один такт;
 SET_BIT_GP0 0 // установка бита вывода GP0 в ноль, один такт;
 SET_BIT_GP0 1 // установка бита вывода GP0 в единицу, один такт;
 GOTO loop     // переход к началу цикла, два такта;

В данном микроконтроллере GOTO (безусловный переход) занимает два программных такта, а остальные упомянутые команды – по одному. Это означает, что в цикле, между переключениями статуса вывода, будет четыре такта. Здесь перед последней командой (GOTO) вывод установлен в единицу (SET_BIT_GP0 1). Посчитаем, начиная с команды, устанавливающей единицу, такты: 1 (SET_BIT_GP0) + 2 (GOTO) + 1 (STATUS) == 4 такта. После чего вывод устанавливается в ноль на один такт. Интерпретация полностью соответствует картинке с осциллографа. Ниже, для сравнения, соответствующий фрагмент кода уже на ассемблере PIC. Точно в такой код преобразует исходную конструкцию с while(1) компилятор. Для задания состояния логического вывода микроконтроллер использует один бит в специальном регистре, поэтому тут выведены команды, устанавливающие (bsf) и сбрасывающие (bcf) биты.

mainloop: // while(...)
	bcf status, 5 // выбор режима адресации;
	bcf (5), 0    // GP0 = 0 (clear bit);
	bsf (5), 0    // GP0 = 1 (set bit);
	goto mainloop // while(1);

Получается, вывод компилятора тут не соответствовал ожиданиям при элементарном подходе. Но, конечно, догадаться о том, что реализация цикла тоже требует некоторых команд – не так трудно. Вообще же, текст программы ничего не говорит об организации цикла: while(1){…} только описывает его границы, это чисто структурный элемент, если воспринимать реализацию на высоком уровне – на языке высокого уровня.

Небольшое “лирическое отступление”. Автоматическая оптимизация циклов компиляторами является хрестоматийным примером неверной интерпретации того, как “программы работают”: многие разработчики на языках высокого уровня, – даже опытные, – пытаясь измерять производительность реализации той или иной функции, начинают с того, что вызывают функцию повторно в цикле, фиксируя время начала и время окончания, – лишь для того, чтобы по результатам измерений обнаружить, что функция “исполняется” практически мгновенно, поскольку оптимизирующий компилятор просто выкинул её вызов из результирующего кода, так как выяснилось, что вывод функции в программе ни на что не влияет (а что касается переменной счётчика цикла, то тут всё заменяется на единственное присваивание – в переменную записывается окончательное значение, которое можно определить на этапе компиляции). Интересно, кстати, что конкретно в C есть и достаточно много неопределённостей на уровне языка, связанных с обработкой присваиваний значений переменным, но наш пример – это не тот случай, поскольку здесь диалект C использует не-совсем-переменные для вывода электрических значений на контакты.

Вернёмся к рисованию меандра программой для микроконтроллера. Хотелось бы получить “ровный” меандр. На языке C для PIC в нашем конкретном случае это можно сделать так.

while(1){
	GPIO = GPIO^0x01;
}

Здесь присваивание значений конкретным выводам заменено на XOR для всего регистра. GPIO – это псевдоним, обозначающий тот самый специальный регистр (массив битов), биты которого соответствуют конкретным логическим выводам. В нашем случае – переключается нулевой вывод (GP0), поэтому изменяется младший бит (бит с индексом нуль, этому соответствует значение маски – 0x01, единица). Логика, стоящая за данным решением, следующая: нужно исключить из тела цикла двойственность состояний. А именно – XOR переключает бит: если там был ноль, то будет единица, если была единица, то станет ноль; тут больше нет записи двух разных значений. Такой вариант кода гораздо лучше соответствует задаче, если бы задачей было получение “ровного, сбалансированного” меандра (заметьте, впрочем, что изначальная задача этой записки – другая: определить, что выводится микроконтроллером и, главное, почему).

Меандр “сбалансировался”, а результат, демонстрируемый осциллографом, поменялся, что и видно на фото экрана ниже.
Signal graph

Однако длительность каждого импульса теперь равна шести тактам. Почему? Потому что компилятор дописал новых команд, опасаясь за целостность общей логики кода: значения в регистрах микроконтроллера могут поменяться вне зависимости от работы АЛУ (арифметико-логического устройства). И это только нам известно, что подобные изменения не предусмотрены ни схемой включения, ни настройками микроконтроллера, а вот компилятор про это догадаться не может, поскольку компилятор не видит сам алгоритм.

Если PIC-ассемблер взять, что называется, в руки, то данный цикл можно оптимизировать. Во-первых, команду выбора схемы адресации можно вынести за пределы цикла, так как выбор зафиксирован. Это сэкономит один такт. Во-вторых, компилятор здесь записывает предложение “GPIO = GPIO^0x01” в три команды: загружает значение специального регистра GPIO в рабочий регистр (в PIC-микроконтроллерах он называется W), выполняет XOR и записывает значение в GPIO. Но нужный XOR можно сделать в одну команду, загрузив заранее, до начала цикла, маску (единицу) в рабочий регистр W и выполняя XOR непосредственно с GPIO. Это позволит сэкономить ещё два такта. А вот два такта, нужных на goto – так сэкономить уже не выйдет (можно, наверное, придумать особенно экзотические методы, но это на общий смысл не повлияет). Естественно, всё это возможно только потому, что задача сводится к преобразованию цикла записи состояний в значение одного логического вывода микроконтроллера, но в итоге – можно добиться трёх тактов на цикл. Соответствующий фрагмент ассемблерного кода приведён ниже.

        bcf     status, 5
        movlw   0x01
mainloop:
        xorwf   (5), 1
        goto    mainloop

Теперь импульсы стали короче в два раза, что и подтверждается очередной фотографией экрана осциллографа.
Signal graph

Конечно, реальная ситуация может быть сложнее, а компилятор может “догадаться” о какой-то лучшей оптимизации. Если рассматривать современные мощные микропроцессоры, то там, например, вообще сложно судить о реальном количестве тактов, затрачиваемых той или иной машинной командой, даже на уровне ассемблера. Ассемблер сейчас не всегда нужен, даже если нужно программировать микроконтроллеры, но знакомство с ассемблерами и принципами работы той аппаратуры, на которой программа будет исполняться, часто помогает осознать причины несоответствия ожиданий тексту программы на языке высокого уровня.



Комментировать »

Очередное “ИИ решает математические задачи”, в этот раз – от Google и про олимпиадные задачи, но, хотя бы, есть уточнения про ограничения. В исходном сообщении сказано, что “AI achieves silver-medal standard solving International Mathematical Olympiad problems” (“ИИ достиг уровня серебряной медали, решая задачи Международной математической олимпиады”).

Если прочитать сообщение внимательно, то оказывается, что, на первом шаге, задачи были вручную переформулированы в виде предложений на формальном языке, который “подходит для системы”. Буквально: “First, the problems were manually translated into formal mathematical language for our systems to understand” – “Прежде всего, задачи были вручную переведены на формальный математический язык, чтобы наши системы их поняли”. То есть, данный ИИ, – который, как теперь напишут в СМИ (не Google), “демонстрирует уровень серебряного медалиста Международной математической олимпиады”, – даже не может прочитать задачи в том виде, как они представлены для людей (я, собственно, давно привожу этот момент в качестве примера).

Под “формальным математическим языком”, судя по всему, имеется в виду язык системы компьютерных доказательств Lean. То есть, речь, конечно, не про то, что задачу просто “записали строго”: исходные формулировки и так достаточно строгие. Заметьте, что корректный перевод исходного текста задачи на тот или иной формальный язык требует достаточно высокой квалификации и, обычно, примерного понимания сути самой задачи. Рассматриваемой системе ИИ этот шаг, очевидно, недоступен.

На втором шаге ИИ-нейросеть генерировала тексты, представляющие собой запись на формальном языке “доказательства” для поставленной задачи, а то, что получилось, автоматически проверяла при помощи системы компьютерных доказательств Lean, после чего “вносила исправления”. Это называется оптимизированный перебор. Или, так как происходит поиск с дополнительными условиями по всему пространству возможных текстов “программ-доказательств” для Lean, можно сказать, что это “форсированный поиск” по тем “веткам”, которые, предположительно, ведут к корректному “доказательству”. Под “корректным” тут подразумевается такой текст программы, который будет принят системой доказательств Lean – то есть, она не выдаст сообщений об ошибках. Это, конечно, никакой не поиск решения олимпиадной задачи в математическом смысле – это поиск текста, парного к тексту формулировки задачи, если в качестве связующей системы используется Lean.

Да, метод перебора является одним из корректных методов математического доказательства, но здесь имеется в виду перебор некоторых вариантов и наборов данных, а не подбор текстов с записью решения. Нельзя сказать, что успешная оптимизация перебора для поиска формальных записей “доказательств” каких-то простых по формулировке задач не является полезным достижением. Не понятно, почему это преподносится как математическая деятельность ИИ и как шаг на пути к “универсальному искусственному интеллекту” (AGI). При этом, как написано, на подбор текста для некоторых из решённых задач система ИИ тратила до трёх дней (!). Другими словами: сверхмощный специализированный компьютер три дня подбирал даже не решение, а текст программы, который представляет собой запись “доказательства” на некотором формальном языке.



Комментарии (1) »