google / or-tools

Google's Operations Research tools:

Home Page:https://developers.google.com/optimization/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Extension of INFEASIBLE model reported as satisfiable

hanno-becker opened this issue · comments

What version of OR-Tools and what language are you using?
Version: main
Language: Python

Which solver are you using (e.g. CP-SAT, Routing Solver, GLOP, BOP, Gurobi)

CP-SAT

What operating system (Linux, Windows, ...) and version?

Linux

What did you do?

Extend a model reported as INFEASIBLE by further constraints.

What did you expect to see

Still see an INFEASIBLE model.

What did you see instead?

An OPTIMAL satisfying assignment.

Minimal example

The following Python script creates a small model and finds it (incorrectly, as far as I can tell) being reported as INFEASIBLE. The namings can be ignored but may be helpful for me in case the bug is somehow on my end.

from ortools.sat.python import cp_model

s = cp_model.CpSolver()
s.parameters.num_workers = 1

model = cp_model.CpModel()
IVAR6540400 = model.NewIntVar(0,16, 'mov_r1___42_start')
IVAR6540544 = model.NewIntVar(0,16, 'mov_r1___42_end')
IVAR6540640 = model.NewIntVar(0,16, 'mov_r0___42_start')
IVAR6540784 = model.NewIntVar(0,16, 'mov_r0___42_end')
IVAR6540880 = model.NewIntVar(0,16, 'vldrw32_q0__r3__start')
IVAR6540976 = model.NewIntVar(0,16, 'vldrw32_q0__r3__end')
IVAR6541072 = model.NewIntVar(0,16, 'vaddu32_q0__q0__q0_start')
IVAR6541168 = model.NewIntVar(0,16, 'vaddu32_q0__q0__q0_end')
IVAR6541264 = model.NewIntVar(0,16, 'vstrwu32_q0__r3__start')
IVAR6541360 = model.NewIntVar(0,16, 'vstrwu32_q0__r3__end')
IVAR6541456 = model.NewIntVar(0,16, 'add_r__r0___42_start')
IVAR6541552 = model.NewIntVar(0,16, 'add_r__r0___42_end')
IVAR6541648 = model.NewIntVar(0,16, 'add_s__r1___42_start')
IVAR6541744 = model.NewIntVar(0,16, 'add_s__r1___42_end')
IVAR6541840 = model.NewIntVar(0,16, 'add_r2__r2___42_start')
IVAR6541936 = model.NewIntVar(0,16, 'add_r2__r2___42_end')
IVAR6542176 = model.NewIntVar(0,16, 'mov_r1___42_out_0_lifetime_end')
IVAR6542224 = model.NewIntVar(0,16, 'mov_r1___42_out_0_lifetime_dur')
IVAR6542320 = model.NewIntVar(0,16, 'mov_r0___42_out_0_lifetime_end')
IVAR6542416 = model.NewIntVar(0,16, 'mov_r0___42_out_0_lifetime_dur')
IVAR6542512 = model.NewIntVar(0,16, 'vldrw32_q0__r3__out_0_lifetime_end')
IVAR6542608 = model.NewIntVar(0,16, 'vldrw32_q0__r3__out_0_lifetime_dur')
IVAR6542704 = model.NewIntVar(0,16, 'vaddu32_q0__q0__q0_out_0_lifetime_end')
IVAR6542800 = model.NewIntVar(0,16, 'vaddu32_q0__q0__q0_out_0_lifetime_dur')
IVAR6542896 = model.NewIntVar(0,16, 'add_r__r0___42_out_0_lifetime_end')
IVAR6542992 = model.NewIntVar(0,16, 'add_r__r0___42_out_0_lifetime_dur')
IVAR6543088 = model.NewIntVar(0,16, 'add_s__r1___42_out_0_lifetime_end')
IVAR6543184 = model.NewIntVar(0,16, 'add_s__r1___42_out_0_lifetime_dur')
IVAR6543280 = model.NewIntVar(0,16, 'add_r2__r2___42_out_0_lifetime_end')
IVAR6543376 = model.NewIntVar(0,16, 'add_r2__r2___42_out_0_lifetime_dur')

BOOLVAR6432112 = model.NewBoolVar('ALLOC(mov r1, #42)(r0)')
INTVAR6430816 = model.NewOptionalIntervalVar(IVAR6540400, IVAR6542224, IVAR6542176,
                                             BOOLVAR6432112,
                                             'USAGE(mov r1, #42)(r0)<ALLOC(mov r1, #42)(r0)>')
BOOLVAR5048240 = model.NewBoolVar('ALLOC(vldrw.32 q0, [r3] )(q0)')
INTVAR5046944 = model.NewOptionalIntervalVar(IVAR6540880, IVAR6542608,
                                             IVAR6542512, BOOLVAR5048240,
                                             'USAGE(vldrw.32 q0, [r3] )(q0)<ALLOC(vldrw.32 q0, [r3] )(q0)>')
BOOLVAR5047856 = model.NewBoolVar('ALLOC(vldrw.32 q0, [r3] )(q1)')
INTVAR5046704 = model.NewOptionalIntervalVar(IVAR6540880, IVAR6542608,
                                             IVAR6542512, BOOLVAR5047856,
                                             'USAGE(vldrw.32 q0, [r3] )(q1)<ALLOC(vldrw.32 q0, [r3] )(q1)>')
BOOLVAR5047472 = model.NewBoolVar('ALLOC(vldrw.32 q0, [r3] )(q2)')
INTVAR5046224 = model.NewOptionalIntervalVar(IVAR6540880, IVAR6542608,
                                             IVAR6542512, BOOLVAR5047472,
                                             'USAGE(vldrw.32 q0, [r3] )(q2)<ALLOC(vldrw.32 q0, [r3] )(q2)>')
BOOLVAR5047088 = model.NewBoolVar('ALLOC(vadd.u32 q0, q0, q0)(q0)')
INTVAR5045936 = model.NewOptionalIntervalVar(IVAR6541072, IVAR6542800,
                                             IVAR6542704, BOOLVAR5047088,
                                             'USAGE(vadd.u32 q0, q0, q0)(q0)<ALLOC(vadd.u32 q0, q0, q0)(q0)>')
BOOLVAR5046800 = model.NewBoolVar('ALLOC(vadd.u32 q0, q0, q0)(q1)')
INTVAR5045504 = model.NewOptionalIntervalVar(IVAR6541072, IVAR6542800,
                                             IVAR6542704, BOOLVAR5046800,
                                             'USAGE(vadd.u32 q0, q0, q0)(q1)<ALLOC(vadd.u32 q0, q0, q0)(q1)>')
BOOLVAR5046416 = model.NewBoolVar('ALLOC(vadd.u32 q0, q0, q0)(q2)')
INTVAR5045120 = model.NewOptionalIntervalVar(IVAR6541072, IVAR6542800,
                                             IVAR6542704, BOOLVAR5046416,
                                             'USAGE(vadd.u32 q0, q0, q0)(q2)<ALLOC(vadd.u32 q0, q0, q0)(q2)>')
BOOLVAR5045600 = model.NewBoolVar('ALLOC(add r, r0, #42)(r)')
INTVAR5045696 = model.NewOptionalIntervalVar(IVAR6541456, IVAR6542992,
                                             IVAR6542896, BOOLVAR5045600,
                                             'USAGE(add r, r0, #42)(r)<ALLOC(add r, r0, #42)(r)>')
BOOLVAR5048624 = model.NewBoolVar('ALLOC(add s, r1, #42)(s)')
INTVAR5045216 = model.NewOptionalIntervalVar(IVAR6541648, IVAR6543184,
                                             IVAR6543088, BOOLVAR5048624,
                                             'USAGE(add s, r1, #42)(s)<ALLOC(add s, r1, #42)(s)>')
BOOLVAR5048912 = model.NewBoolVar('ALLOC(add r2, r2, #42)(r2)')
INTVAR5048432 = model.NewOptionalIntervalVar(IVAR6541840, IVAR6543376,
                                             IVAR6543280, BOOLVAR5048912,
                                             'USAGE(add r2, r2, #42)(r2)<ALLOC(add r2, r2, #42)(r2)>')

model.AddNoOverlap([INTVAR5046944,INTVAR5045936])
model.AddNoOverlap([INTVAR5046704,INTVAR5045504])
model.AddNoOverlap([INTVAR5046224,INTVAR5045120])
model.AddExactlyOne([BOOLVAR5048240, BOOLVAR5047856,
                     BOOLVAR5047472])
model.AddExactlyOne([BOOLVAR5047088, BOOLVAR5046800,
                     BOOLVAR5046416])
model.AddExactlyOne([BOOLVAR5045600])
model.AddExactlyOne([BOOLVAR5048624])
model.AddExactlyOne([BOOLVAR5048912])

model.ExportToFile("model_unsatisfiable.txt")

stat = s.Solve(model)
print(s.ResponseStats())

This results in

CpSolverResponse summary:
status: INFEASIBLE
objective: NA
best_bound: NA
booleans: 11
conflicts: 3
branches: 17
propagations: 30
integer_propagations: 0
restarts: 16
lp_iterations: 0
walltime: 0.0023828
usertime: 0.00238286
deterministic_time: 1.42e-05
gap_integral: 0

However, forcing one integer variable turns it into a satisfiable model:

model.Add(IVAR6541456 == 5)
model.ExportToFile("model_satisfiable.txt")
s.Solve(model)
print(s.ResponseStats())

This returns

CpSolverResponse summary:
status: OPTIMAL
objective: 0
best_bound: 0
booleans: 15
conflicts: 0
branches: 18
propagations: 1
integer_propagations: 18
restarts: 4
lp_iterations: 0
walltime: 0.00117795
usertime: 0.001178
deterministic_time: 6.52e-06
gap_integral: 0

The model files are attached:
model_satisfiable.txt
model_unsatisfiable.txt

Apologies if I the mistake is on my end... maybe I'm missing the forest for the trees here.

Anything else we should know about your project / environment

This came up as part of my work on our CP-SAT based assembly optimizer HeLight

Thanks!

I highly suspect that there is a bug with our symmetry handling code that trigger on your particular example.
You can run with the parameter "symmetry_level" set to 0 or 1 (but not 2, the default) until we fix the issue.
Thanks.

@fdidier Thanks a lot for the fast reply.

Out of interest, what's the "symmetry level" parameter about?

it is a parameter.
solver.parameters.symmetry_level = 1 (in python).

@lperron Yes, that was clear, I meant to ask what's behind, technically. Also, what's the expected performance impact?

I confirm that the example works with symmetry_level = 1 -- I'll see if it also fixes the original problem which dealt with a much larger model.

Thanks for the very fast turnaround @lperron @fdidier -- awesome.