---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[3], line 6
4 from IPython.core.display import SVG
5 import pyomo.environ as pyo
----> 6 from pysat.solvers import Solver
7 from pysat.formula import CNF
8 import py_svg_combinatorics as psc
ModuleNotFoundError: No module named 'pysat.solvers'
Генератор случайных данных
Приводим к задаче о разрешимости
Задача разрешима для заданного , если .
Доказательство, что
Сведем 3SAT к нашей задаче
Сведение будем проводить в 2 этапа:
Сведение 3SAT к Subset Sum Problem(SSP).
Сведение SSP к Knapsack.
Формулировка SSP:
Дан массив целых чисел , данно целое число . Возможно ли выбрать подмножество ?
Сначала простая часть (пункт (2)): Построим по -> :
Пусть , . Тогда в задаче о рюкзаке нам нужно выбрать и , то есть
Эквивалентность данной и полученной из равносильности выше.
Сведение 3SAT к SSP: Пункт (1) смотрите здесь
В сведение мы считали, что числа выше записаны в десятиричной системе. Но так получаются слишком большие числа и pyomo умирает (pyomo работает с 32-битными интами, а у нас длина чисел ).
Как можно немного облегчить себе жизнь: несложно доказать, что, если бы эта таблица была записана не в 10-ричной, а в 4-ричной системе, то сведение все еще будет справедливо(*). Тогда, переходя к knapsack, можно перевести эти числа в 10-ричную систему и они станут гораздо меньше (длина уменьшится в раз). Как будет видно далее, с большими формулами pyomo все еще не справляется, но так оно начинает справляться хотя бы с небольшими.
StasFomin: Ну, если экономить, то нам надо
«ровно бит» на «выбор переменная или отрицание» — и переполнение там невозможно, ждем единицу,
и по «два бита на скобку» — где мы ждем «тройку», и для тройки тоже переполнение невозможно. — если младшие два бита переполнятся максимум мы в них получим «1», и по индукции, никакие биты для скобок не переполнятся.
и вот это 2*m+n — вроде в 64 битный UINT можно попробовать упихать. 24 скобки от 16 переменных, уже на что-то похоже например, вполне можно поиграть с рандомом.
Доказательство (*): Если таблица (и финальная сумма) написаны в 4-ричной системы счисления, то сведение все еще работает. От чего оно вообще может не работать? От того, что у нас в правой половине таблицы (где в каждом столбце по 5 единиц) может произойти переполнение и в другой столбец при сложении может прийти единичка. На самом деле, это ничего нам не ломает: рассмотрим сколько единиц должно быть "выбрано" для самого правого столбца: их должно быть ровно 3 (мы не сможем набрать 7 единиц, чтобы произошло переполнение и в нашем столбце была тройка: , а у нас максимум 5 единиц). Значит на следующий столбец не пойдет единица. Далее доказываем по индукции, что для каждого столбца все еще нужно взять ровно 3 единицы, то есть основа сведения не поменялась.
Сводим полученую SSP задачку в Knapsack (плюс перевод в десятичную)
Проверяем, что наше сведение работает
Для этого генерируем случайную 3-КНФ формулу и решаем ее двумя способами: (1) используя SAT-solver и (2) превращая в экземляр нашей задачи, после чего решая Knapsack при помощи pyomo.
Проверка разрешимости 3-КНФ через преобразование к Knapsack и решение через pyomo-ЦЛП
Проверка разрешимости 3-КНФ через pysat Solver
Проверим что это вообще работает на тривиальных 3-КНФ.
Тест, который генерирует случайных 3-КНФ формул длины до и проверяет, что наш ILP и pysat солвер выдают один и тот же ответ на задачу разрешимости
Теперь просто гоняем тесты
Много тестов на маленьких формулах:
Наше сведение хорошо решает тесты, можно предположить, что сведение легальное.
К сожалению, на больших формулах начинают происходить ошибки.
Предположение: судя по документации, Pyomo не работает с long. А числа у нас генерятся размера , то есть при больших формулах в вычислениях pyomo может происходить переполнение
StasFomin: 'numpy.uint64' — недостаточно? Если при сведении начинаются какие-то экспоненциальные длины чисел, значит не очень это сведение-то полиномиальное. Надо посмотреть внимательно.
О многом говорит "termination condition: infeasible". Это значит, что полученная задача Knapsack неразрешима, то есть , что явно не так ('1' * k + 3 * 'p', что больше любого ). То есть произошло переполнение и оказалось меньше, чем на самом деле.
С этим ничего не поделать: нам не научить pyomo работать с long.
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb Cell 59 line 1
----> <a href='vscode-notebook-cell://xn--17-6kce2c.xn--80apqgfe.xn--p1ai/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb#Y151sdnNjb2RlLXJlbW90ZQ%3D%3D?line=0'>1</a> wtf_scip_01()
/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb Cell 59 line 1
<a href='vscode-notebook-cell://xn--17-6kce2c.xn--80apqgfe.xn--p1ai/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb#Y151sdnNjb2RlLXJlbW90ZQ%3D%3D?line=16'>17</a> m.hint_constraints.add(expr = m.x[ 2*(lit-1)+1 ] == 0)
<a href='vscode-notebook-cell://xn--17-6kce2c.xn--80apqgfe.xn--p1ai/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb#Y151sdnNjb2RlLXJlbW90ZQ%3D%3D?line=17'>18</a> else:
---> <a href='vscode-notebook-cell://xn--17-6kce2c.xn--80apqgfe.xn--p1ai/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb#Y151sdnNjb2RlLXJlbW90ZQ%3D%3D?line=18'>19</a> m.hint_constraints.add(expr = m.x[ 2*(lit-1) ] == 0)
<a href='vscode-notebook-cell://xn--17-6kce2c.xn--80apqgfe.xn--p1ai/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb#Y151sdnNjb2RlLXJlbW90ZQ%3D%3D?line=19'>20</a> m.hint_constraints.add(expr = m.x[ 2*(lit-1)+1 ] == 1)
<a href='vscode-notebook-cell://xn--17-6kce2c.xn--80apqgfe.xn--p1ai/var/data/cocalc/1d588dae-0d14-422a-88b4-c470ec2c8303/hard-problems-formulations/maximum-knapsack.ipynb#Y151sdnNjb2RlLXJlbW90ZQ%3D%3D?line=20'>21</a> solution3 = pyo.SolverFactory('scip').solve(m)
File ~/hard-problems-formulations/.venv/lib64/python3.11/site-packages/pyomo/core/base/var.py:1033, in IndexedVar.__getitem__(self, args)
1031 def __getitem__(self, args):
1032 try:
-> 1033 return super().__getitem__(args)
1034 except RuntimeError:
1035 tmp = args if args.__class__ is tuple else (args,)
File ~/hard-problems-formulations/.venv/lib64/python3.11/site-packages/pyomo/core/base/indexed_component.py:646, in IndexedComponent.__getitem__(self, index)
644 if isinstance(index, EXPR.GetItemExpression):
645 return index
--> 646 validated_index = self._validate_index(index)
647 if validated_index is not index:
648 index = validated_index
File ~/hard-problems-formulations/.venv/lib64/python3.11/site-packages/pyomo/core/base/indexed_component.py:874, in IndexedComponent._validate_index(self, idx)
867 raise KeyError(
868 "Cannot treat the scalar component '%s' "
869 "as an indexed component" % (self.name,)
870 )
871 #
872 # Raise an exception
873 #
--> 874 raise KeyError(
875 "Index '%s' is not valid for indexed component '%s'" % (idx, self.name)
876 )
KeyError: "Index '-4' is not valid for indexed component 'x'"