Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 191 additions & 178 deletions benchmark/Manifest.toml

Large diffs are not rendered by default.

43 changes: 34 additions & 9 deletions src/ConstraintSolver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
# only call if the function got initialized already
if constraint.is_initialized
feasible =
still_feasible(com, constraint, constraint.fct, constraint.set, vidx, value)
still_feasible(com, constraint, vidx, value)
!feasible && break
end
end
Expand Down Expand Up @@ -214,13 +214,15 @@

Change the state of the search space given the current position in the tree (`from_nidx`) and the index we want
to change to (`to_nidx`)
Return whether reverse_pruning! was called
"""
function checkout_from_to!(com::CS.CoM, from_nidx::Int, to_nidx::Int)
backtrack_vec = com.backtrack_vec
from = backtrack_vec[from_nidx]
to = backtrack_vec[to_nidx]
# if the parent of to is the current node then we don't have to do anything
if to.parent_idx == from.idx
return
return false
end
reverse_pruning!(com, from.idx)

Expand All @@ -237,7 +239,7 @@
depth -= 1
end
if parent_nidx == to.parent_idx
return
return true
else
from = parent
end
Expand All @@ -256,7 +258,7 @@
to = parent
if backtrack_vec[prune_steps[1]].parent_idx == from.parent_idx
!isempty(prune_steps) && restore_prune!(com, prune_steps)
return
return true
end
end
@assert from.depth == to.depth
Expand All @@ -271,6 +273,7 @@
end

!isempty(prune_steps) && restore_prune!(com, prune_steps)
return true
end

"""
Expand Down Expand Up @@ -374,13 +377,21 @@
end

"""
set_bounds!(com, backtrack_obj)
set_bounds!(com, backtrack_obj, needed_reverse_prune::Bool)

Set lower/upper bounds for the current variable index `backtrack_obj.vidx`.
Return if simple removable is still feasible
"""
function set_bounds!(com, backtrack_obj)
function set_bounds!(com, backtrack_obj, needed_reverse_prune::Bool)
vidx = backtrack_obj.vidx

# set all first node calls to true as there might have been a reverse pruning
if needed_reverse_prune
for constraint in com.constraints
set_first_node_call!(constraint, true)
end
end

!remove_above!(com, com.search_space[vidx], backtrack_obj.ub) && return false
!remove_below!(com, com.search_space[vidx], backtrack_obj.lb) && return false
return true
Expand Down Expand Up @@ -452,9 +463,11 @@
function checkout_new_node!(com::CS.CoM, last_id, new_id)
if last_id != 0
com.c_backtrack_idx = 0
checkout_from_to!(com, last_id, new_id)
needed_reverse_prune = checkout_from_to!(com, last_id, new_id)
com.c_backtrack_idx = new_id
return needed_reverse_prune
end
return false

Check warning on line 470 in src/ConstraintSolver.jl

View check run for this annotation

Codecov / codecov/patch

src/ConstraintSolver.jl#L470

Added line #L470 was not covered by tests
end

"""
Expand Down Expand Up @@ -589,7 +602,7 @@
vidx = backtrack_obj.vidx

com.c_backtrack_idx = backtrack_obj.idx
checkout_new_node!(com, last_backtrack_id, backtrack_obj.idx)
needed_reverse_prune = checkout_new_node!(com, last_backtrack_id, backtrack_obj.idx)

# if backtracking was started
# => remove all values which are root infeasible
Expand All @@ -611,7 +624,7 @@
last_backtrack_id = backtrack_obj.idx

# limit the variable bounds
if !set_bounds!(com, backtrack_obj)
if !set_bounds!(com, backtrack_obj, needed_reverse_prune)
update_log_node!(com, last_backtrack_id; feasible = false)
continue
end
Expand Down Expand Up @@ -766,6 +779,13 @@

options.no_prune && return :NotSolved

# set for all constraints that a new round of pruning is initialized
# therefore every constraint should know that when it is called
# it will be the first time in a new round
for constraint in com.constraints
set_first_node_call!(constraint, true)
end

# check if all feasible even if for example everything is fixed
feasible = prune!(com; pre_backtrack = true, initial_check = true)
# finished pruning will be called in second call a few lines down...
Expand All @@ -789,6 +809,11 @@
push!(com.solutions, new_sol_obj)
return :Solved
end

for constraint in com.constraints
set_first_node_call!(constraint, true)
end

feasible = prune!(com; pre_backtrack = true)
call_finished_pruning!(com)

Expand Down
2 changes: 2 additions & 0 deletions src/Variable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function rm!(
end
changes && push_to_changes!(v, (:rm, x, 0, 1))
end
notify_constraints_var_changed!(com, v.idx)
return true
end

Expand All @@ -108,6 +109,7 @@ function fix!(com::CS.CoM, v::CS.Variable, x::Int; changes = true, check_feasibi
v.first_ptr = vidx
v.min = x
v.max = x
notify_constraints_var_changed!(com, v.idx)
return true
end

Expand Down
90 changes: 88 additions & 2 deletions src/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ end
"""
call_finished_pruning!(com)

Call `finished_pruning_constraint!` for every constraint which implements that function as saved in `constraint.impl.finished_pruning`
Call `finished_pruning_constraint!` for every constraint.
"""
function call_finished_pruning!(com)
for constraint in com.constraints
Expand All @@ -89,7 +89,7 @@ end
"""
call_restore_pruning!(com, prune_steps)

Call `call_restore_pruning!` for every constraint which implements that function as saved in `constraint.impl.restore_pruning`
Call `call_restore_pruning!` for every constraint.
"""
function call_restore_pruning!(com, prune_steps)
for constraint in com.constraints
Expand Down Expand Up @@ -139,6 +139,26 @@ function get_two_unfixed(com::CS.CoM, constraint::Constraint)
return local_vidx_1, vidx_1, local_vidx_2, vidx_2
end

"""
notify_constraints_var_changed!(com, vidx::Int)

Notify all constraints that the domain of `vidx` changed. Calls [`changed_var!`](@ref)
"""
function notify_constraints_var_changed!(com::CS.CoM, vidx::Int)
constraints = com.constraints
for ci in com.subscription[vidx]
constraint = constraints[ci]
changed_var!(com, constraint, constraint.fct, constraint.set, vidx)
end
end

"""
changed_var!(com::CS.CoM, connstraint::Constraint, fct, set, vidx)

This method needs to be implemented by the constraint when it needs to update something when a variable changed
"""
changed_var!(com::CS.CoM, constraint::Constraint, fct, set, vidx) = nothing

"""
init_constraint!(
com::CS.CoM,
Expand Down Expand Up @@ -307,3 +327,69 @@ function update_init_constraint!(
return true
end

set_first_node_call!(constraint::Constraint, val::Bool) = constraint.first_node_call = val

function prune_constraint!(
com::CS.CoM,
constraint::Constraint;
logs = false
)
check_first_node_call!(com, constraint)
feasible = _prune_constraint!(com, constraint, constraint.fct, constraint.set; logs = logs)
constraint.first_node_call = false
return feasible
end

function still_feasible(
com::CoM,
constraint::Constraint,
vidx::Int,
value::Int,
)
check_first_node_call!(com, constraint)
feasible = _still_feasible(com, constraint, constraint.fct, constraint.set, vidx, value)
constraint.first_node_call = false
return feasible
end

function is_constraint_violated(
com::CoM,
constraint::Constraint,
)
check_first_node_call!(com, constraint)
violated = _is_constraint_violated(com, constraint, constraint.fct, constraint.set)
constraint.first_node_call = false
return violated
end

function is_constraint_solved(com::CS.CoM, constraint::Constraint)
check_first_node_call!(com, constraint)
solved = _is_constraint_solved(com, constraint, constraint.fct, constraint.set)
constraint.first_node_call = false
return solved
end

function _is_constraint_solved(com::CS.CoM, constraint::Constraint, fct, set)
variables = com.search_space
!all(isfixed(variables[ind]) for ind in constraint.indices) && return false
values = CS.value.(variables[constraint.indices])
return _is_constraint_solved(com, constraint, constraint.fct, constraint.set, values)
end

function is_constraint_solved(
com::CS.CoM,
constraint::Constraint,
values::Vector{Int},
)
check_first_node_call!(com, constraint)
solved = _is_constraint_solved(com, constraint, constraint.fct, constraint.set, values)
constraint.first_node_call = false
return solved
end

function check_first_node_call!(com, constraint::Constraint)
!constraint.first_node_call && return
first_node_call!(com, constraint, constraint.fct, constraint.set)
end

first_node_call!(::CS.CoM, ::Constraint, fct, set) = nothing
22 changes: 16 additions & 6 deletions src/constraints/activator_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
"""
function activate_complement_inner!(com, constraint::ActivatorConstraint)
complement_constraint = constraint.complement_constraint
if !constraint.complement_inner_constraint
if !constraint.complement_activated && implements_activate(typeof(complement_constraint), typeof(complement_constraint.fct), typeof(complement_constraint.set))
!activate_constraint!(com, complement_constraint, complement_constraint.fct, complement_constraint.set) && return false
constraint.complement_inner_constraint = true
constraint.complement_inner_constraint_in_backtrack_idx = com.c_backtrack_idx
constraint.complement_activated = true
constraint.complement_activated_in_backtrack_idx = com.c_backtrack_idx

Check warning on line 28 in src/constraints/activator_constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/constraints/activator_constraints.jl#L27-L28

Added lines #L27 - L28 were not covered by tests
end
return true
end
Expand Down Expand Up @@ -118,9 +118,19 @@
constraint.inner_activated_in_backtrack_idx = 0
end
if constraint isa ReifiedConstraint
if constraint.complement_inner_constraint && backtrack_id == constraint.complement_inner_constraint_in_backtrack_idx
constraint.complement_inner_constraint = false
constraint.complement_inner_constraint_in_backtrack_idx = 0
if constraint.complement_activated && backtrack_id == constraint.complement_activated_in_backtrack_idx
constraint.complement_activated = false
constraint.complement_activated_in_backtrack_idx = 0

Check warning on line 123 in src/constraints/activator_constraints.jl

View check run for this annotation

Codecov / codecov/patch

src/constraints/activator_constraints.jl#L122-L123

Added lines #L122 - L123 were not covered by tests
end
complement_constraint = constraint.complement_constraint
if complement_constraint !== nothing
reverse_pruning_constraint!(
com,
complement_constraint,
complement_constraint.fct,
complement_constraint.set,
backtrack_id,
)
end
end

Expand Down
17 changes: 9 additions & 8 deletions src/constraints/all_different.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,17 @@ function update_best_bound_constraint!(
end

"""
prune_constraint!(com::CS.CoM, constraint::AllDifferentConstraint, fct::MOI.VectorOfVariables, set::CPE.AllDifferent; logs = true)
_prune_constraint!(com::CS.CoM, constraint::AllDifferentConstraint, fct::MOI.VectorOfVariables, set::CPE.AllDifferent; logs = false)

Reduce the number of possibilities given the `AllDifferentConstraint`.
Return whether still feasible and throws a warning if infeasible and `logs` is set to `true`
"""
function prune_constraint!(
function _prune_constraint!(
com::CS.CoM,
constraint::AllDifferentConstraint,
fct::MOI.VectorOfVariables,
set::CPE.AllDifferent;
logs = true,
logs = false,
)
indices = constraint.indices
pvals = constraint.pvals
Expand Down Expand Up @@ -436,12 +436,12 @@ function prune_constraint!(
end

"""
still_feasible(com::CoM, constraint::AllDifferentConstraint, fct::MOI.VectorOfVariables, set::CPE.AllDifferent, vidx::Int, value::Int)
_still_feasible(com::CoM, constraint::AllDifferentConstraint, fct::MOI.VectorOfVariables, set::CPE.AllDifferent, vidx::Int, value::Int)

Return whether the constraint can be still fulfilled when setting a variable with index `vidx` to `value`.
**Attention:** This assumes that it isn't violated before.
"""
function still_feasible(
function _still_feasible(
com::CoM,
constraint::AllDifferentConstraint,
fct::MOI.VectorOfVariables,
Expand All @@ -461,7 +461,8 @@ function still_feasible(
return true
end

function is_constraint_solved(
function _is_constraint_solved(
com,
constraint::AllDifferentConstraint,
fct::MOI.VectorOfVariables,
set::CPE.AllDifferent,
Expand All @@ -471,7 +472,7 @@ function is_constraint_solved(
end

"""
is_constraint_violated(
_is_constraint_violated(
com::CoM,
constraint::AllDifferentConstraint,
fct::MOI.VectorOfVariables,
Expand All @@ -481,7 +482,7 @@ end
Checks if the constraint is violated as it is currently set. This can happen inside an
inactive reified or indicator constraint.
"""
function is_constraint_violated(
function _is_constraint_violated(
com::CoM,
constraint::AllDifferentConstraint,
fct::MOI.VectorOfVariables,
Expand Down
Loading