Дефекты и исправления
В каждом разделе: суть дефекта, воспроизведение, причина, исправление, почему оно верно, и проверка. Ссылки на
код — по master 81bcf35. Для дефектов второго прохода мы прямо говорим, может ли дефект дать неверный результат
без всякого сообщения и достижим ли он при построении 2-групп с параметрами по умолчанию
(PqDescendants, PqPCover, PqStandardPresentation для 2-группы, без
дополнительных опций).
- Первый проход: дефекты 1–9, PR #96–#104
- Второй проход, неверный результат без сообщения: дефекты 10–19
- Второй проход, падения и ошибки: дефекты 20–22
- Второй проход, неверные данные: дефекты 23–24
Первый проход
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 упал |
|---|---|---|---|
| master | 2123 | 121 | 1 |
| с исправлением | 2123 | 0 | 0 |
Неверные ответы начинаются с порядка 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.tsttst/group_name.tsttst/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-групп по умолчанию: не затронуто.