Дефекты и исправления

В каждом разделе: суть дефекта, воспроизведение, причина, исправление, почему оно верно, и проверка. Ссылки на код — по master 81bcf35. Для дефектов второго прохода мы прямо говорим, может ли дефект дать неверный результат без всякого сообщения и достижим ли он при построении 2-групп с параметрами по умолчанию (PqDescendants, PqPCover, PqStandardPresentation для 2-группы, без дополнительных опций).

Первый проход

1. Автоморфизмы передаются в pq в чужом базисе

добавлен 17.10.2026 · данные: coords.g, gen_codes.g, check_codes.g, ds_children.g · тест в PR: tst/aut_basis.tst

Суть. PqDescendants(G) для pc-группы $G$ возвращает неверных потомков или роняет pq, если pcgs группы $G$ не совпадает с pc-презентацией, которую pq строит для $G$ сам. Группы из библиотеки SmallGroups не затронуты (их pcgs совпадает с презентацией pq), их копии — затронуты.

G := PcGroupCode(3850294291458756985713877851, 128);;   # копия SmallGroup(128, 540)
Length(PqDescendants(G : StepSize := 1));             # 7; у SmallGroup(128, 540) их 10
H := PcGroupCode(20922463594250774814923642549699, 128);; # копия SmallGroup(128, 773)
PqDescendants(H : StepSize := 1);                     # pq падает ("iostream dead")

Причина. PQ_PC_PRESENTATION передаёт pq презентацию группы $G$ на $g_1, \dots, g_n$ = PcgsPCentralSeriesPGroup(G), и pq строит по ней свою pcp: порождающие $q_1 = g_1, \dots, q_d = g_d$, а дальше коммутаторы и степени по своему выбору, которые не обязаны совпадать с $g_{d+1}, \dots, g_n$. PQ_AUT_INPUT записывает образ $g_j$ при каждом автоморфизме показателями относительно $g_1, \dots, g_n$, а pq читает их как показатели относительно $q_1, \dots, q_n$. Там, где базисы различаются, pq работает с отображениями, которые не являются автоморфизмами $G$ (часто вообще не автоморфизмы): их действие на $p$-мультипликаторе, а значит и орбиты на допустимых подгруппах, неверны.

Исправление (#96). Перед отправкой автоморфизмов самой группы PQ_PG_SUPPLY_AUTS запрашивает у pq его текущую pcp и образы $g_1,\dots,g_n$ в ней (PQ_PCP_BASIS), и PQ_AUT_INPUT отправляет для каждого автоморфизма $\alpha$ образы $\varepsilon(\alpha(\varepsilon^{-1}(q_j)))$ в координатах pq, где $\varepsilon$ — изоморфизм группы $G$ на группу pq. Если pcp pq совпадает с pcgs группы $G$, вход не меняется.

Почему это верно. $\varepsilon$ — изоморфизм (pq вычисляет $p$-фактор класса $c$ группы $G$, то есть саму $G$), поэтому $\varepsilon\alpha\varepsilon^{-1}$ пробегает группу автоморфизмов группы pq, когда $\alpha$ пробегает группу автоморфизмов $G$, а образы $q_1,\dots,q_d$ его определяют. Если текущая pcp — $p$-накрытие, её первые $n$ порождающих — это порождающие фактора, а остальные порождают мультипликатор, так что фактор по ним — группа pq.

Проверка. По одной изоморфной копии (pcgs из случайно сопряжённого перестановочного представления) каждой группы $p$-класса $\ge 3$ порядков 64 и 128 и каждой 25-й такой группы порядка 256 до номера 13 550 (2123 копий, список на странице «Данные») в сравнении с самой группой SmallGroups:

сборкакопийневерные потомкиpq упал
master21231211
с исправлением212300

Неверные ответы начинаются с порядка 64 (SmallGroup(64, 128): 6 потомков вместо 12). Кроме того, копии всех групп $p$-класса $\ge 3$ порядков 32–128, полученные тремя другими путями (специальный pcgs, перестановочная группа, собственная презентация pq для fp-группы): 1983 сравнений в каждом из четырёх прогонов, 0 расхождений с исправлением, в том числе под AddressSanitizer. Лишний обмен с pq не стоит ничего заметного (группы SmallGroups порядка 128, каждая 10-я: 28,6 с до, 29,3 с после).

2. Счётчики орбит в char

добавлен 17.10.2026 · тест в PR: tst/orbit_counters.tst

Суть. При 127 и более автоморфизмах алгоритм орбит разрешимой группы возвращает неверные орбиты и стабилизаторы, а значит, неверных потомков (и неверные группы автоморфизмов у них).

Length(PqDescendants(AbelianGroup([2^3, 2^5, 2^7, 2^9, 2^11]) : StepSize := 1));
# 16384; у типов [2..2^5] и [2^2..2^10] их 26, и у этой группы с исправлением тоже

Причина. Для каждой допустимой подгруппы $k$ soluble_orbits.c хранит $c[k]$ — число автоморфизмов, при которых $k$ замыкает свою орбиту (до $m + 1$ при $m$ автоморфизмах), а find_permutation.c хранит $d[k]$ — номер автоморфизма, который первым дошёл до $k$; по ним stabiliser.c строит слова Шрайера. Код стандартной презентации хранит номера автоморфизмов в векторе Шрайера (insoluble_orbits.c) и в word_map (standard.c). Все эти массивы были типа char, который знаковый на x86 и на macOS arm64 и переполняется на 127. Для группы выше AutPGrp даёт 130 разрешимых автоморфизмов.

Исправление (#99): эти массивы теперь int.

Почему это верно. Меняется только тип; всякое хранимое значение помещается в int (не больше $m + 1$ или номер автоморфизма). Независимо от кода: добавление тождественных автоморфизмов не меняет порождённую ими группу, а значит, не должно менять потомков. На master меняет, как только автоморфизмов становится 127 (в примере из tst/bugfix.tst 9 потомков превращаются в 125); с исправлением не меняет. Время для группы выше падает с 44 с до 3 с, потому что орбиты больше не дробятся.

Кого это затрагивает. Разрешимые автоморфизмы, передаваемые в pq, образуют pcgs, поэтому их не больше $\log_2 |\mathrm{Aut}(G)| < d \cdot n$ для $|G| = 2^n$ ранга $d$. При $|G| \le 2^{10}$ это меньше 100, плюс несколько неразрешимых: меньше 127. Поэтому вызовы с шагом 1 для групп порядка не больше $2^{10}$ не затронуты.

3. EpimorphismStandardPresentation для pc-групп не изоморфизм

добавлен 17.10.2026 · тест в PR: tst/sp_epimorphism.tst

Суть. Для pc-группы, являющейся $p$-группой, у которой порождающие не совпадают с pcgs, возвращаемое отображение не биективно. Сама стандартная презентация верна.

G := AbelianGroup(IsPcGroup, [4, 2]);; e := EpimorphismStandardPresentation(G);;
# образы MappingGeneratorsImages(e)[1] не задают биекцию на группу стандартной презентации

До исправления отображение не биективно для AbelianGroup(IsPcGroup,[4,2]), [2,4], Group(G.1,G.2) в SmallGroup(64,100), C4 x Q8 как прямого произведения и силовской 2-подгруппы группы $S_8$; после исправления биективно.

Причина. pq получает презентацию на datarec.pcgs, и последние $k$ элементов его отображения — образы именно этих элементов, а anusp.gi сопоставлял их с GeneratorsOfGroup(G) и строил отображение через GroupHomomorphismByImagesNC.

Исправление (#100): сопоставлять с datarec.pcgs, как уже делает PQ_GROUP_FROM_PCP.

4. Группа, нарушающая заданный закон экспоненты, роняет pq

добавлен 17.10.2026 · тест в PR: tst/descendants_law.tst

PqDescendants(SmallGroup(16, 5) : Exponent := 4);   # C8 x C2: pq падает; с исправлением: [ ]

Причина. pq сначала вычисляет $p$-фактор группы $G$ её класса с учётом закона; если $G$ закон нарушает, этот фактор меньше $G$ (здесь 3 порождающих вместо 4), но автоморфизмы всё равно отправляются с $|{\rm pcgs}(G)|$ показателями каждый, и pq принимает лишние числа за ответы на свои следующие вопросы.

Исправление (#97). Каждый потомок группы $G$ имеет $G$ своим фактором, значит, тоже нарушает закон: ответ — пустой список, и он возвращается до вызова pq, как для группы, у которой нет непосредственных потомков. $G$ удовлетворяет $x^e = 1$ тогда и только тогда, когда $\exp(G) \mid e$, и метабелева тогда и только тогда, когда её ступень разрешимости не больше 2.

5. PqStandardPresentation зависает при Relators другого ранга

добавлен 17.10.2026 · тест в PR: tst/sp_relators.tst

PqStandardPresentation(FreeGroup("a", "b") : Prime := 2, Relators := ["a^8", "b*a^-2"]);
# зависает; с исправлением: стандартная презентация C8

Причина. Ранг фактора класса 1, нужный для автоморфизмов $GL(\mathrm{rank}, p)$, берётся у группы, а pq работает с соотношениями из опции. Здесь он отправляет матрицы $2 \times 2$ для группы ранга 1; pq принимает лишние элементы за следующие ответы и ждёт ввода, а GAP ждёт pq.

Исправление (#98): брать ранг у группы, которую задают соотношения (PqGAPRelators).

6. Интерактивный PqDescendants возвращает группы прежних команд

добавлен 17.10.2026 · тест в PR: tst/stale_descendants.tst

procId := PqStart(FreeGroup(2) : Prime := 3);; Pq(procId : ClassBound := 1);;
PqSetPQuotientToGroup(procId); PqComputePCover(procId); PqPGSupplyAutomorphisms(procId);
PqPGConstructDescendants(procId : StepSize := 1);    # 3 группы порядка 27
List(PqDescendants(procId : StepSize := 2), Size);   # [ 27, 27, 27, 81, 81, 81 ]; исправлено: [ 81, 81, 81 ]

Причина. pq дописывает в GAP_library всё время жизни процесса; файл очищается только в PqStart. Исправление (#101): группы этого вызова — последние ndescendants записей; их и оставляем.

7. SavePqList пишет файлы, которые PqList не может прочитать

добавлен 17.10.2026 · тест в PR: tst/savepqlist.tst

Для группы с группой автоморфизмов записывался вызов шестиаргументной функции ANUPQSetAutomorphismGroup с четырьмя аргументами. Исправление (#102): записывать данные об автоморфизмах, которые приписал PqDescendants, тем вызовом, который пишет сам pq; прочитанные обратно группы имеют те же данные и тех же потомков.

8. Опция RelativeOrders

добавлен 17.10.2026 · тест в PR: tst/relative_orders.tst

PQ_MANUAL_AUT_INPUT перебирал for i in RelativeOrders и отправлял RelativeOrders[i]: [3, 2] давал ошибку, [2, 3, 3] отправлял 3, 3, 3. Исправление (#103): перебирать позиции.

9. IsIsomorphicPGroup для двух тривиальных групп

добавлен 17.10.2026 · тест в PR: tst/isomorphic_trivial.tst

Выдавал ошибку <G> must be a p-group (наименьший корень из 1 не простое число). Исправление (#104): возвращать true.

Второй проход: неверный результат без сообщения

Каждому из десяти дефектов нужно что-то, чего построение 2-групп с параметрами по умолчанию не делает; что именно — сказано в последней строке раздела.

10. PcgsAutomorphisms с последовательностью, не являющейся PAG

добавлен 18.10.2026 · данные: pcgs_automorphisms.g · тест в PR: tst/pcgs_automorphisms.tst

Суть. С опцией PcgsAutomorphisms pq считает переданные автоморфизмы PAG-последовательностью (каждая порождённая подгруппа нормальна простого индекса в следующей), и никто этого не проверяет. Если это не так, алгоритм орбит разрешимой группы возвращает неверные орбиты и стабилизаторы.

G := ElementaryAbelianGroup(4);;
Length(PqDescendants(G : ClassBound := 3));                      # 136
Length(PqDescendants(G : ClassBound := 3, PcgsAutomorphisms));   # 161: 25 изоморфных повторов; исправлено: 136

Для ElementaryAbelianGroup(8), у которой группа автоморфизмов $GL(3,2)$ неразрешима, pq сообщает о 116 потомках порядка $2^5$ (групп ранга 3 и 2-класса 2 всего 15) и затем падает; руководство обещает сообщение об ошибке.

Причина. trace_action (src/soluble_orbits.c:96–131) прослеживает только образ последнего элемента орбиты, а это верно, лишь когда $\langle g_1, \dots, g_{i-1} \rangle$ нормальна простого индекса в $\langle g_1, \dots, g_i \rangle$. PQ_AUT_INPUT отправляет agAutos, а затем glAutos из AutPGrp; glAutos лишь порождают фактор и не образуют pcgs (для $C_2^2$ это две инволюции, порождающие $S_3$). По руководству значение по умолчанию — IsSolvable(Aut(G)), в коде — false. Коммит 71e4ac2 (#92) исправил на этом пути чтение за границей массива, но не орбиты.

Исправление (#110). Если PcgsAutomorphisms задана в момент передачи автоморфизмов и образ glAutos в $GL(d,p)$ разрешим, они заменяются прообразами pcgs этого образа с их относительными порядками; иначе вызов завершается ошибкой, как и для заданных вручную матриц, не прошедших проверку PAG на факторе Фраттини. В руководстве теперь указано настоящее значение по умолчанию. Сам pcgs разрешимой группы — PAG-последовательность и порождает ту же группу, поэтому орбиты те же.

Без сообщения? Да. Построение 2-групп по умолчанию: не затронуто; по умолчанию опция равна false, и наши прогоны её не задают. Для всех нециклических 2-групп порядка не больше 32 с разрешимой группой автоморфизмов потомки с шагом 1 с опцией и без неё совпали; ошибка проявляется со второго шага (ClassBound не меньше класса + 2) и при автоморфизмах, заданных вручную.

11. Закон экспоненты при автоморфизмах, заданных вручную

добавлен 18.10.2026 · данные: exponent_reduced_list.g · тест в PR: tst/exponent_reduced_list.tst

Суть. После PqSupplyAutomorphisms PqNextClass накладывает закон экспоненты, проверяя сокращённый список слов и замыкая найденное относительно автоморфизмов. Это верно, только если автоморфизмы индуцируют всю $GL(d,p)$ на относительно свободной группе, как сказано в комментарии в src/extra_relations.c:39–42; в руководстве этого нет. Иначе часть закона не накладывается, и группа получается слишком большой.

id := PqStart(FreeGroup(4) : Prime := 2, Exponent := 2);;
PqPcPresentation(id : ClassBound := 1);;
PqSupplyAutomorphisms(id, [ IdentityMat(4) ]);;
PqNextClass(id);   # порядок 2^13; группа экспоненты 2 имеет порядок 2^4, как и без автоморфизмов

Настоящие автоморфизмы, не индуцирующие $GL(d,p)$, дают то же: $\langle a, b \mid a^2 \rangle$ с экспонентой 4 и $b \mapsto ab$ даёт $2^{13}$ вместо $2^6$; $p = 3$, экспонента 3, $d = 2$ и $\mathrm{diag}(2,1)$ дают $3^8$ вместо $3^3$.

Причина. При заданных автоморфизмах next_class.c:81–86 использует REDUCED_LIST: проверочные слова веса не больше $2c/3$, а из порождающих веса 1 — только первый (extra_relations.c:216, 239–241).

Исправление (#112, вливать после #106). Сокращённый список остаётся, только если соотношений нет, метабелева закона нет и матрицы порождают $GL(d,p)$ (проверяется перебором, пока $|GL(d,p)| \le 2^{24}$); иначе проверяются все слова всех весов, а результат, как и прежде, замыкается относительно автоморфизмов. Замыкание относительно настоящих автоморфизмов добавляет только следствия закона, поэтому получается группа закона. 19 случаев с автоморфизмами теперь дают тот же порядок, что и без них; пример B4-4-a-i по-прежнему даёт $2^{422}$ за то же время.

Без сообщения? Да; порядок всегда завышен. Построение 2-групп по умолчанию: не затронуто; PqDescendants, PqPCover и PqStandardPresentation проверяют все слова и эти автоморфизмы не используют.

12. Очередь закона экспоненты не увеличивается в классе 2

добавлен 18.10.2026 · данные: exponent_queue.g · тест в PR: tst/exponent_queue.tst

Суть. next_class выделяет очередь избыточностей, найденных законом экспоненты, до вычисления хвостов, на 4 элемента, и увеличивает её только для классов, отличных от 2 (src/next_class.c:37, 44–50). В классе 2 extra_relations и close_relations пишут за её конец. Через GAP исход зависит от раскладки кучи: верный ответ, неверный или падение.

procId := PqStart(FreeGroup(7) : Prime := 2, Exponent := 2);;
PqPcPresentation(procId : ClassBound := 1);;
PqSupplyAutomorphisms(procId, auts);;   # auts: 7-цикл и трансвекция, порождают GL(7,2)
PqNextClass(procId);                    # верно: 2^7, класс 1

На master, по 10 прогонов для каждого $d$: при $d = 7$ шесть раз $2^{11}$ и одно падение; при $d = 8$, 9, 10 неверный порядок ($2^{26}$, $2^{34}$, $2^{43}$) 6, 7 и 7 раз. Под ASan каждый раз heap-buffer-overflow в close_relations.c:128.

Исправление (#106): увеличивать очередь на каждом классе, включая класс 2. Новый тест на master упал в 14 прогонах из 15, с исправлением проходит; ASan молчит.

Без сообщения? Иногда; в остальных случаях pq падает. Построение 2-групп по умолчанию: не затронуто; эта ветка требует автоморфизмов $p$-фактора, а их задают только PqSupplyAutomorphisms и PqExtendAutomorphisms.

13. glOrder неверен при $p^d > 10000$

добавлен 18.10.2026 · данные: gl_order.g · тест в PR: tst/gl_order.tst

Суть. PqStabiliserOfAllowableSubgroup сообщает AutPGrp порядок группы автоморфизмов $A$ по модулю её разрешимой части $S$. При $p^d > 10000$ ($d$ — ранг) lib/anustab.gi:121–123 берёт его как ANUPQsize / p^ANUPQagsize, будто все относительные порядки разрешимой части равны $p$, и не задаёт glOper. Относительные порядки не все равны $p$ (для $p = 101$: 5, 5, 2, 2), поэтому glOrder неверен и часто даже не целый. AutPGrp прекращает перебор орбиты на glOrder$/q$ и принимает glOrder за её длину; стабилизатор выходит слишком маленьким, потомки получают слишком маленькие группы автоморфизмов, и на следующем шаге появляются изоморфные повторы.

SetInfoLevel(InfoANUPQ, 2);
PqDescendants(ElementaryAbelianGroup(101^2) : OrderBound := 4, ClassBound := 2);
# #I  Order of GL subgroup is 1020000/1030301      верно: |GL(2,101)|/100 = 1030200

Проверка. 6 потомков порядка $101^4$ верны (эти орбиты pq считает сам); прогон до класса 3 не завершился. На том же коде с порогом, опущенным до 0, чтобы этой веткой шло $p = 5$, $C_5^2$ до порядка $5^4$ даёт 18 групп порядка 625 вместо 9 (изоморфные повторы), а $C_3^2$ до $3^6$ — 139 групп вместо 132; с исправлением 9 и 132, как в SmallGroups.

Исправление (#117): glOrder равен $|P|/|S|$ для матричных групп всех и разрешимых автоморфизмов на факторе Фраттини — тот же индекс, который другая ветка считает начиная с PR #85.

Без сообщения? Да. Построение 2-групп по умолчанию: не затронуто; при $p = 2$ эта ветка требует ранга не меньше 14, а у наших групп ранг не больше 11.

14. PqEpimorphism для pc-группы, не являющейся $p$-группой

добавлен 18.10.2026 · данные: epimorphism_non_pgroup.g · тест в PR: tst/epimorphism_non_pgroup.tst

Суть. Для pc-группы $G$, не являющейся $p$-группой, PqEpimorphism, EpimorphismPqStandardPresentation и EpimorphismStandardPresentation сопоставляют образы порождающих pq с GeneratorsOfGroup(G), хотя pq получил презентацию на Pcgs(G). Если они различаются, отображение не гомоморфизм, или не сюръективно, или вызов завершается ошибкой.

P := Image(IsomorphismPcGroup(DirectProduct(SymmetricGroup(3), CyclicGroup(4))));;
B := Group(Reversed(GeneratorsOfGroup(P)));;     # та же группа, порождающие в обратном порядке
PqEpimorphism(B : Prime := 2, ClassBound := 3);   # [f4,f3,f2,f1] -> [f1,f3,f2,1]: не гомоморфизм
C := Group(P.1*P.3, P.2*P.4);;                   # подгруппа порядка 12
PqEpimorphism(C : Prime := 2, ClassBound := 3);   # Error, <gens> and <imgs> must be lists of same length

Причина. lib/anupqi.gi:270–273 передаёт pq группу FpGroupPcGroup(G), порождающие которой соответствуют Pcgs(G), и не заполняет datarec.pcgs; тогда PQ_GROUP_FROM_PCP и anusp.gi:222 берут GeneratorsOfGroup. Исправление дефекта 3 касалось только ветки $p$-групп.

Исправление (#111, вливать после #100): сохранять прообразы порождающих fp-группы и сопоставлять образы с ними. Прежнее замечание о том, что проверка на подгруппе $S_4 \times C_3$ не завершилась, объясняется не зависанием: стандартная презентация возвращается как fp-группа, в которой перечислены только нетривиальные pc-соотношения; как fp-группа она бесконечна, и Size работает бесконечно.

Без сообщения? Да, в первом случае. Построение 2-групп по умолчанию: не затронуто; $p$-группы и fp-группы идут другими ветками.

15. Шаг больше ядерного ранга и ещё две ошибки учёта

добавлен 18.10.2026 · данные: step_size.g · тест в PR: tst/step_size_dialogue.tst
tst/group_name.tst
tst/sp_tmpdir.tst

Суть. Три ошибки учёта в интерфейсе GAP; первая может дать неверных потомков.

(a) Шаг больше ядерного ранга. Под GAP pq снова спрашивает шаг, если он больше ядерного ранга (src/defaults_pga.c:283), но GAP уже отправил следующие ответы, и они читаются как шаг, пока один из них не будет принят. Собственная защита GAP (lib/anupqi.gi:2859) срабатывает, только если ядерный ранг уже известен.

G := SmallGroup(4, 2);;                                   # ядерный ранг 3
PqDescendants(G : StepSize := 4, PcgsAutomorphisms);     # 3 группы порядка 8; верно: [ ]
PqDescendants(G : StepSize := 4);                        # [ ]: верно, но случайно

(b) ANUPQDirectoryTemporary меняет временный каталог, но не ANUPQData.SPimages, и следующий PqStandardPresentation читает пустой файл и завершается ошибкой (lib/anupq.gi:37). (c) Имя группы с _ (опция GroupName или Name(G)) приводит к ошибке в PqDescendants: класс извлекается из имени файла my_G_class2 разбиением по _ (lib/anupqi.gi:2975); с пробелом интерактивный PqPGSetDescendantToPcp убивает pq.

Исправление (#119, #120, #121): под GAP pq принимает шаг и возвращает пустой список потомков, как уже делает при вводе из файла; ANUPQDirectoryTemporary обновляет SPimages; класс берётся из цифр в конце имени файла, а пробельные символы в GroupName заменяются на _. С ним три воспроизведения дают [ ], группу и 6 потомков.

Без сообщения? (a) да, с PcgsAutomorphisms (или RankInitialSegmentSubgroups); (b) и (c) всегда кончаются ошибкой. Построение 2-групп по умолчанию: не затронуто; с параметрами по умолчанию (a) возвращает верный пустой список. Наша сверка с SmallGroups перебирала все допустимые шаги, в том числе больше ядерного ранга, без PcgsAutomorphisms; её результат остаётся в силе.

16. Простые $p \ge 2^{15}$

добавлен 18.10.2026 · данные: prime_bound.g · тест в PR: tst/prime_bound.tst

Суть. Показатели упакованы в 15 бит числа int (include/storage_fixed.h), а верхней границы для $p$ нет, поэтому при $p \ge 2^{15}$ показатель до $p - 1$ заходит в знаковый бит.

F := FreeGroup("a", "b", "c");; a := F.1;; b := F.2;; c := F.3;;
G := F / [a^2*b^3*c^5, Comm(a,b)^7*Comm(b,c)^11*c^2];;
PqEpimorphism(G : Prime := 32771, ClassBound := 1);
# a, b, c -> [1], [31914], [7068]; верно: [1], [10923], [0]. Второе соотношение в образе не выполняется.

При $p = 65537$ GAP падает на пустом образе, а PqPCover(ElementaryAbelianGroup(32771^2)) возвращал группу неверного порядка. Автономный pq проверяет только нечётность $p$: $p = 9$ с соотношением $a^3$ зацикливается в invert_modp (деление на ноль; на arm64 бесконечный цикл).

Исправление (#116): $p \le 32749$, наибольшее простое меньше $2^{15}$; это проверяет GAP для опции Prime и для простого, взятого у $p$-группы, а pq — вместе с проверкой простоты. Не воспроизведено и в исправление не входит: переполнение неприведённой суммы в src/update.c:126 при $p$ порядка $10^3$; на 137 случайных презентациях с 64-битным зондом сумма не превысила $1{,}06 \cdot 10^9 < 2^{31}$.

Без сообщения? Да. Построение 2-групп по умолчанию: не затронуто; $p = 2$.

17. Больше 65535 pc-порождающих в одном классе

добавлен 18.10.2026 · данные: pc_generator_limit.g · тест в PR: tst/pc_generator_limit.tst

Суть. Номера порождающих упакованы в 16 бит (MAXPC = 65535), а предел проверяется только перед классом, в основном меню (src/options.c:101). Внутри класса create_tail, extend_tail и update_generators нумеруют новые порождающие без проверки, и номер больше 65535 совпадает с чужим: порождающий $65536 + k$ хранится как квадрат порождающего $k$.

procId := PqStart(FreeGroup(361) : Prime := 2);;
PqPcPresentation(procId : ClassBound := 1);;
PqNextClass(procId);
PqFactoredOrder(procId);   # [ 2, 65702 ] по испорченной презентации; исправлено: ошибка

Ранг 360 ($2^{65340}$) считается верно; рангу 361 нужно 65702 порождающих.

Исправление (#114): проверка перед каждым новым порождающим; при выходе за предел pq останавливается с сообщением The number of pc generators would exceed the limit 65535, которое GAP превращает в ошибку.

Без сообщения? Да. Построение 2-групп по умолчанию: не затронуто; у $p$-накрытий 2-групп порядка не больше $2^{11}$ десятки или сотни порождающих.

18. Для коммутаторного соотношения с показателем резервируется мало памяти

добавлен 18.10.2026 · данные: commutator_workspace.g · тест в PR: tst/commutator_workspace.tst

Суть. Для определяющего соотношения, в котором коммутатор возведён в степень, collect_relations резервирует $7 \cdot \mathrm{lastg} + 7$ слов над занятой рабочей памятью, а собирание коммутатора и power используют $11 \cdot \mathrm{lastg} + 9$. У предела рабочей памяти power затирает собранную левую часть и данные под ней. ASan ничего не видит: запись остаётся внутри рабочей памяти.

F := FreeGroup("a", "b");; G := F / [Comm(F.2, F.1)^3];;
Pq(G : Prime := 2, ClassBound := 3, Relators := ["[b,a]^3"], PqWorkspace := 210);   # порядок 2^7; верно: 2^6
Pq(G : Prime := 2, ClassBound := 3, PqWorkspace := 210);                            # 2^6

Исправление (#109): резервировать $11 \cdot \mathrm{lastg} + 9$; у предела pq теперь останавливается с Ran out of space.

Без сообщения? Да. Построение 2-групп по умолчанию: не затронуто; нужна степень коммутатора в строках опции Relators (pc-группы передают коммутаторы с показателем 1) и рабочая память (по умолчанию $10^7$ слов), заполненная почти до конца, с запасом меньше примерно $4 \cdot \mathrm{lastg}$ слов.

19. StandardPresentation с pQuotient в другой pc-презентации

добавлен 18.10.2026 · данные: sp_pquotient.g · тест в PR: tst/sp_pquotient_basis.tst

Суть. С опцией pQuotient := Q автоморфизмы группы $Q$ передаются в pq показателями относительно PcgsPCentralSeriesPGroup(Q), но саму $Q$ pq не получает: он строит собственную pcp фактора класса $c - 1$ и читает показатели в этом базисе. Там, где базисы различаются, pq работает с отображениями, которые не являются автоморфизмами его группы, и стандартная презентация получается другой или pq падает. Это механизм дефекта 1; его исправление (#96) касается только пути PqDescendants.

Key := F -> List(RelatorsOfFpGroup(F), LetterRepAssocWord);;
G := SmallGroup(64, 6);;              # 2-класс 3
Q := PcGroupCode(33300, 16);;         # копия его фактора класса 2, IdGroup [ 16, 3 ]
Key(StandardPresentation(G : pQuotient := Q, ClassBound := 3)) = Key(StandardPresentation(G));   # false; исправлено: true
H := SmallGroup(64, 112);;
StandardPresentation(H : pQuotient := PcGroupCode(2768831001, 32), ClassBound := 3);   # pq падает; исправлено: SP группы H

Проверка. Все 177 групп порядка 64 2-класса не меньше 3, по две случайные копии фактора класса $c - 1$ на каждую (354 копии): 59 неверных стандартных презентаций и 109 падений pq, одинаково на master и на correctness; с исправлением 0 и 0. Каждая 25-я группа порядка 128 и каждая 500-я порядка 256: с исправлением 0 и 0 (прогоны master там не завершились из-за загрузки машины; до места, где они остановились, порядок 128 дал 3 неверные презентации и 5 падений). Если $Q$ — фактор, вычисленный самим pq, или группа SmallGroups с тем же номером, расхождений нет ни на одной сборке.

Причина. PQ_SP_STANDARD_PRESENTATION (lib/anupqi.gi:2418) вызывает PQ_AUT_INPUT(datarec, datarec.pQuotient), а сама $Q$ в pq не передаётся.

Исправление (#118). Перед отправкой автоморфизмов GAP запрашивает у pq его текущую pcp, как в дефекте 1, и берёт фактор $p$-накрытия по последним порождающим — это группа pq в его собственной pcp; автоморфизмы вычисляются для неё. Если её порядок не равен $|Q|$, вызов завершается ошибкой. Даже при верном базисе pQuotient класса не меньше 2 не даёт канонической стандартной презентации: по замыслу pq принимает свою pcp фактора за стандартную и стандартизирует только начиная с класса $c + 1$. Для проверки изоморфизма годится только класс 1, как и по умолчанию.

Без сообщения? Да, для 59 из 354 копий выше; на 109 pq упал. Построение 2-групп по умолчанию: не затронуто; PqDescendants и PqPCover этим путём не идут, а PqStandardPresentation без pQuotient начинает с класса 1 и $GL(d,p)$, что от базиса не зависит. Внутри пакета в pQuotient передаётся только фактор класса 1 или собственный вывод pq.

Второй проход: падения и ошибки

20. Файлы сохранённых групп при сборке с GMP

добавлен 18.10.2026 · данные: gmp_separator.g · тест в PR: tst/gmp_separator.tst

Суть. При сборке с GMP save_pga (src/write.c:40) пишет порядок группы автоморфизмов десятичным текстом и сразу за ним, без разделителя, двоичную запись, первое поле которой — int p. При чтении mpz_inp_str забирает цифры, пока они идут подряд; если младший байт $p$ — код цифры ASCII ($p \bmod 256 \in [48, 57]$: 53, 307, 311, 313, 563, …), он уходит в число, и запись читается со сдвигом на байт.

PqDescendants(CyclicGroup(53) : StepSize := 1);   # pq падает; исправлено: одна группа, IdGroup [ 2809, 1 ]

Исправление (#105): перевод строки после числа, проверяемый при чтении.

Без сообщения? Нет; во всех наших случаях было падение. Построение 2-групп по умолчанию: не затронуто; при $p = 2$ первый байт равен 2 и цифрой не является, а вывод для 2-групп с исправлением тот же.

21. Слова Шрайера пишутся без проверки места

добавлен 18.10.2026 · данные: stabiliser_space.g

Суть. stabiliser_generators (src/stabiliser.c:154–185) пишет слова Шрайера в рабочую память и никогда не проверяет, хватает ли места. С настоящими автоморфизмами слова короткие (не больше 25 слов по замерам при примерно $10^7$ свободных); с отображениями, которые не являются автоморфизмами (дефект 1), слово не замыкается, затирает таблицы pq в верхней части рабочей памяти и затем выходит за пределы выделенного блока.

G := PcGroupCode(723005982809892258877217511128, 256);;   # копия SmallGroup(256, 1325)
PqDescendants(G : StepSize := 1);   # master: pq падает (ASan: heap-buffer-overflow, stabiliser.c:173); с #96: 7 групп

Исправление (#108): проверка перед каждой буквой; если места нет — Ran out of space during computation, как в других местах pq. Уменьшая PqWorkspace шаг за шагом, мы не нашли размера, при котором предыдущие проверки проходят, а переполняется только этот цикл. Нового теста нет: после #96 этот вход до цикла не доходит.

Без сообщения? На master, как следствие дефекта 1, память затирается до падения; неверного ответа от этого мы не видели. Построение 2-групп по умолчанию: не затронуто, если #96 принят.

22. Переполнение числа допустимых подгрупп

добавлен 18.10.2026 · данные: degree_overflow.g · тест в PR: tst/degree_overflow.tst

Суть. Для стандартной презентации pq строит перестановочное представление на допустимых подгруппах; его степень — гауссов биномиальный коэффициент, который при большом ранге Фраттини превышает $2^{31}$. compute_degree это обнаруживает (src/degree.c:51–60) и печатает сообщение, но под GAP (pq -G считается интерактивным режимом) возвращается, а не завершается, и pq продолжает работу с недосчитанной степенью, пока не упадёт. GAP видит сообщение раньше и выдаёт ошибку, так что через GAP результат — чистая ошибка; автономный pq с вводом из файла падал (SEGV).

G := PcGroupCode(5720184869874057666191942025326282294078349057, 2^9);;   # ранг 6, класс 2, |G'| = 2^3
StandardPresentation(G);
# Error, pq program terminated, with error condition:
#   Evaluation in compute_degree may cause integer overflow

Исправление (#113): завершаться в любом режиме. Сам предел остаётся (задача #32 в трекере пакета): перестановочное представление степени больше $2^{31}$ строить непрактично.

Без сообщения? Нет. Построение 2-групп по умолчанию: PqDescendants не затронут. Стандартные презентации некоторых 2-групп порядков от $2^9$ до $2^{11}$ останавливаются с этой ошибкой — и до исправления, и после: при $p = 2$ степень на шаге коммутаторов равна $\binom{q}{s}_2$, где $q = \binom{d}{2}$, и переполняется начиная с ранга 6 при $|G'| \ge 2^3$, с ранга 7 при $s \ge 2$ и всегда с ранга 9. Это ошибка, а не неверный результат.

Второй проход: неверные данные

23. В относительных порядках для GAP повторяется последний

добавлен 18.10.2026 · данные: relative_orders_gap.g · тест в PR: tst/relative_orders_gap.tst

Суть. GAP_auts перебирает до i <= nmr_soluble, а затем ещё раз пишет последний элемент (src/GAP_present.c:336–340), поэтому в relativeOrders из ANUPQAutomorphisms на один элемент больше. В GAP_link_via_file.c:78 граница верная.

des := PqDescendants(Pq(FreeGroup(2) : Prime := 3, ClassBound := 1) : StepSize := 1);;
ANUPQAutomorphisms(des[3]).relativeOrders;   # [ 3, 2, 2 ] при 2 автоморфизмах; исправлено: [ 3, 2 ]

Исправление (#107): < вместо <=. Не изменено: в PqSupplementInnerAutomorphisms agAutos — это центральные автоморфизмы, а относительные порядки покрывают и разрешимую часть стабилизатора, поэтому длины agOrder и agAutos по-прежнему различаются; исправление изменило бы документированный интерфейс.

Без сообщения? Неверные данные, но не неверная группа: ANUPQAutomorphisms читают только PqSupplementInnerAutomorphisms и SavePqList, а PqDescendants берёт автоморфизмы из AutPGrp. Построение 2-групп по умолчанию: не затронуто.

24. Относительные порядки разрешимого стабилизатора не пересчитываются

добавлен 18.10.2026 · данные: stabiliser_relative.g · тест в PR: tst/stabiliser_relative.tst

Суть. В разрешимой ветке stabiliser_of_rep (src/stabiliser.c:74–88) pq строит порождающие стабилизатора, но оставляет nmr_soluble и относительные порядки от предыдущей стадии, поэтому ANUPQAutomorphisms(G).relativeOrders может перечислять больше разрешимых порождающих, чем есть автоморфизмов.

for G in PqDescendants(Pq(FreeGroup(3) : Prime := 2, ClassBound := 1) : StepSize := 1) do
  a := ANUPQAutomorphisms(G);
  Print(Length(a.centralAutos) + Length(a.otherAutos), " ", Length(a.relativeOrders) - 1, "\n");
od;   # 7 7, 4 5, 5 5, 3 5; исправлено: 7 7, 4 4, 5 5, 3 3   (- 1: повтор из дефекта 23)

Исправление (#115): пересчитывать оба значения по словам Шрайера. Порождающий $t$ стабилизатора — это автоморфизм $\alpha_t$, умноженный на слово в автоморфизмах с меньшими номерами, причём $\alpha_t$ возрастает; поэтому разрешимые — те, у которых $\alpha_t$ не больше прежнего nmr_soluble, с прежним относительным порядком автоморфизма $\alpha_t$.

Почему потомки всё равно верны. Устаревшие значения попадают только в вывод для GAP и в собственные файлы групп pq. Единственное место, где они могли бы изменить орбиты, — обращение к GAP за неразрешимым стабилизатором — их не получает (отладочная сборка: 513 вызовов, ни одного расхождения). Полные деревья потомков 2-групп до $2^7$ и 3-групп до $3^6$ против SmallGroups: 0 расхождений, на master и с исправлением.

Без сообщения? Только неверные данные. Построение 2-групп по умолчанию: не затронуто.