potassco / clasp

⚙️ A conflict-driven nogood learning answer set solver

Home Page:https://potassco.org/clasp/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Conflict Statistics using Propagators

MaxOstrowski opened this issue · comments

In clingo-dl the use of:
PropagateControl::add_clause
does not increment the conflict statistics of clasp.
Is this in general the case ?
In our special case, all conflicts added this way only have one literal assigned on the current decision level.

I cannot answer this. I am quite sure this is not a bug but rather about what clasp is actually considering a conflict. Maybe @BenKaufmann can help?

Added a MWE https://github.com/MaxOstrowski/Bug.git
The test program is
{a(1..10)}.
The propagator adds clauses of the inverted trail if it contains more than 1 element.
Output is:

Solving...
check
trail 1 trail 12
check
trail 1 trail 12 trail -2 found
check
trail 1 trail 12 trail -2 found trail -11 found
ret: 0
add conflict 2
check
trail 1 trail 12 trail -2 found trail 11 found
ret: 0
add conflict 2
check
trail 1 trail 12 trail 2 found
check
trail 1 trail 12 trail 2 found trail 11 found
ret: 0
add conflict 2
check
trail 1 trail 12 trail 2 found trail -11 found
ret: 0
add conflict 2
UNSATISFIABLE

Models : 0
Calls : 1
Time : 0.000s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
CPU Time : 0.000s

Choices : 3
Conflicts : 2 (Analyzed: 1)

The added clauses are 2,11; 2,-11; -2,-11;-2,11. (ignore literal 1 and 12 as they are aux facts)
Only two of them count as conflict, although all of them are conflicting.
I do understand the behaviour, just want to know if this is intended.
It may be misleading.

@MaxOstrowski
PropagateControl::add_clause (and therefore Clasp::ClingoPropagator::addClause()) handles
asserting clauses, i.e. clauses that are conflicting but only have a single literal assigned at the highest decision level, differently than other conflicting clauses. In general, adding a conflicting clause induces a conflict (thereby incrementing the conflict counter), which is then handled by clasp's conflict resolution engine (which increments the Analyzed counter if the conflict is resolved via FirstUIP resolution) . However, if addClause() detects that a given conflict clause is already asserting and backtracking to the assertion level is possible, it simply backtracks to the assertion level and assigns the asserting literal without incrementing the conflict counter.

So, the behaviour is deliberate but if you all agree that not incremting the conflict counter is misleading, I can easily change that.

So, the behaviour is deliberate but if you all agree that not incremting the conflict counter is misleading, I can easily change that.

I would count such kind of "conflicts". I even think that, ideally, there would be a separate counter for them. As far as I know such kind of conflicts can also occur when a program is non-tight or there are edge constraints. I am not sure how/if such kind of conflicts are represented in the statistics.

Conflicts from unfounded sets/edge constraints are handled as "regular conflicts". That is, they are counted as conflicts and then passed to the resolution engine, where they are typically converted to and counted as FirstUIP conflict clauses. That's the reason why for non-tight programs the stats often won't show any "loop" constraints although some conflicts resulted from unfounded sets.

We could easily handle asserting clauses from addClause() similarly. For Max's program, that would result in the following stats:

Choices      : 3
Conflicts    : 4        (Analyzed: 3)
Restarts     : 0
Problems     : 1        (Average Length: 1.00 Splits: 0)
Lemmas       : 3        (Deleted: 0)
  Binary     : 1        (Ratio:  33.33%)
  Ternary    : 0        (Ratio:   0.00%)
  Conflict   : 3        (Average Length:    1.3 Ratio: 100.00%)
  Loop       : 0        (Average Length:    0.0 Ratio:   0.00%)
  Other      : 0        (Average Length:    0.0 Ratio:   0.00%)

Note that there are no Other lemmas then because all added clauses were counted as "Conflict".

If we'd only update the conflict counter, we'd get:

Choices      : 3
Conflicts    : 4        (Analyzed: 1)
Restarts     : 0
Problems     : 1        (Average Length: 1.00 Splits: 0)
Lemmas       : 3        (Deleted: 0)
  Binary     : 2        (Ratio:  66.67%)
  Ternary    : 0        (Ratio:   0.00%)
  Conflict   : 1        (Average Length:    1.0 Ratio:  33.33%)
  Loop       : 0        (Average Length:    0.0 Ratio:   0.00%)
  Other      : 2        (Average Length:    2.0 Ratio:  66.67%)

This already shows a slight difference between the approaches because in the former case, the minimization strategy of conflict resolution actually simplifies one of the binary asserting clauses to a unary cluase.

Treating them just like conflicts from the unfounded set checker seems the better option to me. After all, we can think of the unfounded set checker as a propagator.

EDIT: If the distinction between asserting/non-asserting conflicts is important for a propagator, we always have the statistics interface to add such information ourselves.