SAT Solver: SAT4J — оценка только подмножества предложений

У меня есть формула в файле .dimacs/.cnf, как показано ниже:

  p cnf 6 9
  1 0
 -2 1 0
 -1 2 0
 -5 1 0
 -6 1 0
 -3 2 0
 -4 2 0
 -3 -4 0
  3 4 -2 0

Можно ли извлечь только те предложения, которые содержат, например, переменные 2, 3 и 4 в SAT4j? Затем мне нужно проверить согласованность только для этого нового набора предложений, т. е. для:

  p cnf 4 6
 -2 1 0
 -1 2 0
 -3 2 0
 -4 2 0
 -3 -4 0
  3 4 -2 0

Я пытался использовать предположения, я пытался использовать ограничения, но я все еще не могу найти способ сделать это.

Спасибо за любое предложение.

Редактировать

Я думал, что есть метод типа solver.addClause(clause), но наоборот solver.getClause(clause)... Хотя, я кормлю решатель предложениями из файла .cnf.

Редактировать 2

Во-первых, предположения имеют тот же синтаксис, что и предложение,

val assumption: IVecInt = new VecInt(Array(1, 2))
val clause: IVecInt = new VecInt(Array(1, 2))

но переменные conjunctions в предположениях и disjunctions в предложении. Это разница. Верно? Мои тестовые примеры говорят об этом. (Мне просто нужно было получить дополнительное разрешение на это).

Во-вторых, моя проблема с использованием переменных селектора такова:

Простая формула a V b имеет три модели:

(a, b), 
(a, -b), 
(-a, b)

Когда я добавляю селекторную переменную, например, s, и ее предположение равно -s, то у меня есть такое же количество моделей, т. е. 3 модели:

(a, b, -s),
(a, -b, -s),
(-a, b, -s)

Когда предположение true, т. е. s, то у меня есть 4 модели вместо 0, которые я хочу:

(a, b, s), 
(a, -b, s), 
(-a, b, s), 
(-a, -b, s)

Конечно когда s = T, то (s V a V b) = (T V a V b) = T, но разве это способ удаления пункта (a V b)? Мне нужно количество моделей, настоящих моделей! Есть ли способ найти точные модели, «удалив» каким-то образом эти переменные (т. е. a и b), которые мы хотим исключить по предположению?

В данном случае это мой текущий код на Scala:

object Example_01 {

  val solver: ISolver = new ModelIterator(SolverFactory.newDefault())
  val reader: DimacsReader = new DimacsReader(solver)
  val problem: IProblem = reader.parseInstance("example_01.cnf")    

  def main(args: Array[String]): Unit = {

    var nrModels: Int = 0
    val assumptions: IVecInt = new VecInt(Array(10))

    try {
      while(solver.isSatisfiable(assumptions)) {
        println(reader.decode(problem.model()))
        nrModels += 1
      }
    } catch {
      case e: ContradictionException => println("UnSAT: ", e)
    }

    println("The number of models is: " + nrModels)
}

Спасибо за любую помощь.


person Avah    schedule 23.01.2017    source источник


Ответы (2)


Я хотел бы добавить еще один способ. Использование блокирующей оговорки.

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

Теперь, в вашем случае, вы должны добавить блокирующее предложение, которое относится только к тем переменным, которые вам нужны.

Предположим, у вас есть формула CNF

x and (y or z)

и вы получите решение x = 1, y = 1, z = 0.

Но, скажем, вас интересуют только x и z.

Из этого решения пункт о блокировке будет

!(x and !z)

Это запретит решение

x = 1, y = 1, z = 0, а также x = 1, y = 0, z = 0

Вы получите только одно решение

x = 1, z = 1 (y не имеет значения)

Надеюсь это поможет.

Если вы используете какой-либо счетчик модели, найдите возможность добавить проекционные переменные (иногда также называемые независимыми переменными). В основном вы хотите спроецировать все решения на подмножество переменных. Различные комбинации других переменных не должны влиять на количество моделей.

person Sukanya B    schedule 14.06.2017
comment
Я не знал об этом способе, хотя я искал довольно много! Я только что увидел ваш ответ, и теперь я собираюсь его проанализировать, так как я снова вернулся к этой проблеме. Спасибо за это, правда! - person user4712458; 09.08.2017
comment
Рад, что это помогло. Удачи. - person Sukanya B; 12.09.2017

Чтобы продолжить, добавьте к каждому предложению новую переменную селектора.

p cnf 15 9
7 1 0
8 -2 1 0
9 -1 2 0
10 -5 1 0
11 -6 1 0
12 -3 2 0
13 -4 2 0
14 -3 -4 0
15 3 4 -2 0

И тогда вам просто нужно присвоить дополнительной переменной значение true, чтобы отбросить предложение, и значение false, чтобы включить его в качестве предположений. В вашем случае предположение будет 7,-8,-9,10,11,-12,-13,14,-15.

Это не относится к Sat4j, это способ работы, изначально предложенный minisat, и который предоставляется большинством решателей SAT.

редактировать 2:

Вот способ использования предположений и подсчета моделей

    ISolver solver = SolverFactory.newDefault();
    ModelIterator iterator = new ModelIterator(solver);
    iterator.newVar(2); // for a and b
    int selector = iterator.nextFreeVarId(true);
    assert selector == 3;
    IVecInt clause = new VecInt();
    // (a, b),
    clause.push(1).push(2);
    solver.addClause(clause);
    clause.clear();
    // (s, -a, -b)
    clause.push(-1).push(-2).push(3);
    solver.addClause(clause);
    clause.clear();
    IVecInt assumptions = new VecInt();
    // we activate the second clause in the solver
    assumptions.push(-3);
    while (iterator.isSatisfiable(assumptions)) {
        System.out.println("1:>" +new VecInt(iterator.model()));
    }
    assert iterator.numberOfModelsFoundSoFar() == 2;
    // need to reset the solver, since iterating over the model
    // adds new constraints in the solver
    iterator.reset();
    clause = new VecInt();
    // (a, b),
    clause.push(1).push(2);
    solver.addClause(clause);
    clause.clear();
    // (s, -a, -b)
    clause.push(-1).push(-2).push(3);
    solver.addClause(clause);
    clause.clear();
    // we disable the second clause in the solver
    assumptions.clear();
    assumptions.push(3);
    while (iterator.isSatisfiable(assumptions)) {
        System.out.println("2:>" + new VecInt(iterator.model()));
    }
    assert iterator.numberOfModelsFoundSoFar() == 3;
}
person Daniel Le Berre    schedule 25.01.2017
comment
Я не знал об идее selector variables, так как это мой первый раз, когда я пытаюсь использовать решатель. Теперь мой вопрос: нужно ли мне добавлять и поддерживать вручную эти переменные (в качестве предположений), например, в SAT4j (как я его использую), или они каким-то образом поддерживаются решателем? Благодарю вас! - person user4712458; 25.01.2017
comment
В некоторых случаях Sat4j поддерживает для вас переменные селектора (решение MAXSAT, вычисление ядра UNSAT). Но если вам нужно какое-то конкретное поведение, вы должны поддерживать свой список селекторов. Обратите внимание, что вы можете попросить класс Solver поддерживать для вас доступный varId: Solver.nextFreeVarId(true) вернет следующий свободный идентификатор var в решателе и зарезервирует его для вас. - person Daniel Le Berre; 26.01.2017
comment
Спасибо за это, искренне. Я провел некоторый тест с переменными селектора и написал под Edit 2 (см. выше) свою озабоченность тем, что мне действительно хотелось бы знать, как ее решить. Я надеюсь, что есть решение для этого! :( - person user4712458; 27.01.2017