type config = Config.config
type bound = Bound.bound
type statistic = Statistic.statistic
type symbol = Symbol.symbol
type literal = Term.literal
type choice_point = State.choice_point
type state = State.state
type raw_context_unifier = Context_unifier.raw_context_unifier
type problem_literals = Problem_literals.problem_literals
type selected = Selection_types.selected
type selection_lookahead = Selection_lookahead.selection_lookahead
type context = Context.context
type candidate = {
ca_raw_context_unifier: raw_context_unifier;
ca_term_quality: int;
ca_generation: int;
}
let compare_candidates ~(different: bool) (first: candidate) (second: candidate) : int =
if first.ca_term_quality < second.ca_term_quality then
-1
else if first.ca_term_quality > second.ca_term_quality then
1
else begin
begin
if first.ca_generation < second.ca_generation then
-1
else if first.ca_generation > second.ca_generation then
1
else
begin
Context_unifier.compare_context_unifiers ~different:different
first.ca_raw_context_unifier second.ca_raw_context_unifier
end
end
end
let candidate_to_string title candidate =
let context_element : Context.element =
Context_unifier.creating_context_element candidate.ca_raw_context_unifier
in
(title ^ Term.literal_to_string context_element.Context.el_literal)
^ "\n"
^ (Context_unifier.raw_context_unifier_to_string candidate.ca_raw_context_unifier)
^ "\n"
module Valid =
Heap.MinMaxHeap (
struct
type t = candidate
let compare = compare_candidates ~different:Const.stable_derivation
let to_string = candidate_to_string ""
end
)
module ForgottenOrder =
Set.Make (
struct
type t = (Context.element * candidate)
let compare (_, candidate0) (_, candidate1) =
compare_candidates ~different:Const.stable_derivation candidate0 candidate1
end
)
module ForgottenBarrier =
Hashtbl.Make (
struct
type t = Context.element
let equal = Context.element_equal
let hash element =
element.Context.el_id
end
)
type candidates = {
cd_config: config;
cd_bound: bound;
cd_statistic: statistic;
cd_state: state;
cd_context: context;
cd_problem_literals: problem_literals;
cd_unprocessed: raw_context_unifier array;
mutable cd_unprocessed_size: int;
cd_valid: Valid.t;
mutable cd_forgotten_order: ForgottenOrder.t;
cd_forgotten_barrier: candidate ForgottenBarrier.t;
mutable cd_forgotten_recomputation_barrier: (Context.element * candidate) option;
cd_exceeding_elements: (int, Context.element) Hashtbl.t;
mutable cd_check_exceeding: bool;
mutable cd_lookahead: selection_lookahead;
cd_lookahead_exceeding: selection_lookahead;
mutable cd_preselected: (candidate * literal * int) option;
}
exception EXCEEDING_EXHAUSTED
let candidate_to_string_ (config: config) (state: state)
(title: string) (candidate: candidate) (literal: literal) : string =
State.active_choice_point_to_string state
^ title ^ ": " ^ Term.literal_to_string literal ^ "\n"
^
if Config.print_derivation_context_unifier config then
Context_unifier.raw_context_unifier_to_string candidate.ca_raw_context_unifier
else
""
let null_candidate = {
ca_raw_context_unifier = Context_unifier.null_context_unifier;
ca_term_quality = -1;
ca_generation = -1;
}
let create (config: config) (bound: bound) (statistic: statistic) (state: state)
(context: context) (problem_literals: problem_literals) : candidates =
let unprocessed =
Array.make (Tools.max_int 1 Const.max_unprocessed_assert_candidates) Context_unifier.null_context_unifier
in
{
cd_config = config;
cd_bound = bound;
cd_statistic = statistic;
cd_state = state;
cd_context = context;
cd_problem_literals = problem_literals;
cd_unprocessed = unprocessed;
cd_unprocessed_size = 0;
cd_valid = Valid.create null_candidate;
cd_forgotten_order = ForgottenOrder.empty;
cd_forgotten_barrier = ForgottenBarrier.create 64;
cd_forgotten_recomputation_barrier = None;
cd_exceeding_elements = Hashtbl.create 64;
cd_check_exceeding = false;
cd_lookahead = Selection_lookahead.create Const.max_assert_lookahead;
cd_lookahead_exceeding = Selection_lookahead.create Const.max_assert_lookahead_exceeding;
cd_preselected = None;
}
let has_closing_candidate (candidates: candidates) : bool =
match candidates.cd_preselected with
| None -> false
| _ -> true
let size (candidates: candidates) : int =
Valid.size candidates.cd_valid
let rec retract_valid_min (candidates: candidates) : unit =
if Valid.is_empty candidates.cd_valid then begin
()
end
else begin
let candidate =
Valid.min candidates.cd_valid
in
if Context_unifier.is_raw_context_unifier_invalid candidate.ca_raw_context_unifier then begin
ignore (Valid.remove_min candidates.cd_valid: candidate);
retract_valid_min candidates;
end
end
let rec retract_valid_max (candidates: candidates) : unit =
if Valid.is_empty candidates.cd_valid then begin
()
end
else begin
let candidate =
Valid.max candidates.cd_valid
in
if Context_unifier.is_raw_context_unifier_invalid candidate.ca_raw_context_unifier then begin
ignore (Valid.remove_max candidates.cd_valid: candidate);
retract_valid_max candidates;
end
end
let get_barrier (candidates: candidates) (candidate: candidate) : candidate =
let context_element : Context.element =
Context_unifier.creating_context_element candidate.ca_raw_context_unifier
in
ForgottenBarrier.find candidates.cd_forgotten_barrier context_element
let is_candidate_forgotten (candidates: candidates) (candidate: candidate) : bool =
try
compare_candidates ~different:false (get_barrier candidates candidate) candidate <= 0
with
| Not_found ->
false
let update_forgotten (candidates: candidates) (forgotten: candidate) : unit =
let context_element : Context.element =
Context_unifier.creating_context_element forgotten.ca_raw_context_unifier
in
try
let barrier : candidate =
ForgottenBarrier.find candidates.cd_forgotten_barrier context_element
in
if compare_candidates ~different:true barrier forgotten > 0 then begin
ForgottenBarrier.replace candidates.cd_forgotten_barrier context_element forgotten;
candidates.cd_forgotten_order <- ForgottenOrder.remove (context_element, barrier) candidates.cd_forgotten_order;
candidates.cd_forgotten_order <- ForgottenOrder.add (context_element, forgotten) candidates.cd_forgotten_order
end
with
| Not_found ->
ForgottenBarrier.add candidates.cd_forgotten_barrier context_element forgotten;
candidates.cd_forgotten_order <- ForgottenOrder.add (context_element, forgotten) candidates.cd_forgotten_order
let is_marked_exceeding (candidates: candidates) (context_element: Context.element) : bool =
try
ignore (Hashtbl.find candidates.cd_exceeding_elements context_element.Context.el_id: Context.element);
true
with
| Not_found ->
false
let is_element_incomplete (candidates: candidates) (element: Context.element) : bool =
Config.restart candidates.cd_config = Flags.RS_Delayed
&&
not candidates.cd_check_exceeding
&&
is_marked_exceeding candidates element
let mark_exceeding (candidates: candidates) (context_element: Context.element) : unit =
Hashtbl.add candidates.cd_exceeding_elements context_element.Context.el_id context_element
let get_term_quality (candidates: candidates) (literal: literal) : int =
if Config.iterative_deepening candidates.cd_config = Flags.IT_TermDepth then
Term_attributes.weight_of_literal literal
else
Term_attributes.depth_of_literal literal
let build_assert_literal (raw_context_unifier: raw_context_unifier) : literal * int =
let input_partners =
raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_input_partners
in
let assert_literal_partner, index =
let rec check_at (index: int) : literal * int =
if index >= Array.length input_partners then begin
print_endline (Context_unifier.raw_context_unifier_to_string raw_context_unifier);
failwith "Selection_assert.build_assert_literal"
end
else begin
let input_partner =
input_partners.(index)
in
if raw_context_unifier.Context_unifier.rcu_context_partners.(input_partner.Context_unifier.ip_index)
==
Context_unifier.assert_partner
then
input_partner.Context_unifier.ip_literal, input_partner.Context_unifier.ip_index
else
check_at (index + 1)
end
in
check_at 0
in
let subst =
Context_unifier.recompute_unifier ~recompute:false raw_context_unifier
in
let assert_literal =
Subst.apply_to_literal ~insert_db:false
subst
assert_literal_partner
Subst.input_literal_offset
in
assert_literal, index
let build_candidate' (candidates: candidates) (raw_context_unifier: raw_context_unifier)
(assert_literal: literal) : candidate =
{
ca_raw_context_unifier = raw_context_unifier;
ca_term_quality = get_term_quality candidates assert_literal;
ca_generation = Context_unifier.generation_of_candidate raw_context_unifier.Context_unifier.rcu_context_partners;
}
let is_candidate_conflicting (candidates: candidates) (assert_literal: literal) : bool =
Selection_lookahead.check candidates.cd_lookahead assert_literal
||
Selection_lookahead.check candidates.cd_lookahead_exceeding assert_literal
||
(
match Context.check_contradictory candidates.cd_context assert_literal with
| Some _ -> true
| None -> false
)
let register_to_lookahead (candidates: candidates)
(raw_context_unifier: raw_context_unifier) (assert_literal: literal)
: unit =
if raw_context_unifier.Context_unifier.rcu_exceeding_complexity then begin
if Config.lookahead_exceeding candidates.cd_config then begin
Selection_lookahead.add ~no_duplicates:true
candidates.cd_lookahead_exceeding assert_literal raw_context_unifier
end
end
else
Selection_lookahead.add ~no_duplicates:true
candidates.cd_lookahead assert_literal raw_context_unifier
let add_valid (candidates: candidates) (candidate: candidate) : unit =
try
if Valid.size candidates.cd_valid < Const.max_assert_candidates then begin
Valid.add candidates.cd_valid candidate;
end
else if compare_candidates ~different:true candidate (Valid.max candidates.cd_valid) > 0 then begin
update_forgotten candidates candidate
end
else if Const.debug && is_candidate_forgotten candidates candidate then begin
failwith "Selection_assert.forgotten invalid";
end
else begin
let forgotten =
Valid.remove_max candidates.cd_valid
in
retract_valid_max candidates;
update_forgotten candidates forgotten;
Valid.add candidates.cd_valid candidate;
end
with
| Heap.OVERFLOW ->
raise (Const.NO_SOLUTION "Selection_assert.add_valid: candidate set overflow")
let build_candidate (candidates: candidates) (raw_context_unifier: raw_context_unifier) :
(candidate * literal * int) option =
let assert_literal, index =
build_assert_literal raw_context_unifier
in
let candidate =
build_candidate' candidates raw_context_unifier assert_literal
in
let complexity =
candidates.cd_bound#get_complexity assert_literal
in
match candidates.cd_forgotten_recomputation_barrier with
| Some (_, forgotten_barrier) when
compare_candidates ~different:false candidate forgotten_barrier < 0
||
candidates.cd_bound#exceeds complexity
->
None
| _ ->
if candidates.cd_check_exceeding then begin
if
Config.neg_assert_candidates candidates.cd_config != Flags.NAC_Use
&&
not assert_literal.Term.sign
then
()
else begin
let choice_point =
Context_unifier.get_creation_choice_point raw_context_unifier.Context_unifier.rcu_context_partners
in
if candidates.cd_bound#exceeds_current complexity choice_point then begin
match Context.check_subsumed candidates.cd_context assert_literal with
| Some _ -> ()
| None ->
ignore (
candidates.cd_bound#register complexity choice_point
: bool);
if candidates.cd_bound#has_min_exceeding then
raise EXCEEDING_EXHAUSTED
end
end;
None
end
else if
Config.neg_assert_candidates candidates.cd_config = Flags.NAC_Lookahead
&&
not assert_literal.Term.sign
then begin
if is_candidate_conflicting candidates assert_literal then
Some (build_candidate' candidates raw_context_unifier assert_literal, assert_literal, index)
else begin
register_to_lookahead candidates raw_context_unifier assert_literal;
None
end
end
else if candidates.cd_bound#exceeds complexity then begin
if
Config.lookahead_exceeding candidates.cd_config
&&
is_candidate_conflicting candidates assert_literal
then
Some (build_candidate' candidates raw_context_unifier assert_literal, assert_literal, index)
else begin
register_to_lookahead candidates raw_context_unifier assert_literal;
begin
match Config.restart candidates.cd_config with
| Flags.RS_Eager
| Flags.RS_Lazy ->
()
| Flags.RS_Delayed ->
let context_element =
Context_unifier.creating_context_element raw_context_unifier
in
if is_marked_exceeding candidates context_element then
()
else begin
match Context.check_subsumed candidates.cd_context assert_literal with
| Some _ ->
()
| _ ->
mark_exceeding candidates context_element;
end
end;
None
end
end
else begin
match Context.check_subsumed candidates.cd_context assert_literal with
| Some _ ->
None
| _ ->
if is_candidate_conflicting candidates assert_literal then begin
Some (candidate, assert_literal, index)
end
else begin
if Config.print_assert_candidates candidates.cd_config then begin
print_string (
candidate_to_string_
candidates.cd_config candidates.cd_state
"New Assert Candidate" candidate assert_literal);
end;
register_to_lookahead candidates raw_context_unifier assert_literal;
add_valid candidates candidate;
None
end;
end
let clear_unprocessed (candidates: candidates) : unit =
for i = 0 to candidates.cd_unprocessed_size - 1 do
candidates.cd_unprocessed.(i) <- Context_unifier.null_context_unifier;
done;
candidates.cd_unprocessed_size <- 0
let build_unprocessed ~(force: bool) (candidates: candidates) : bool =
if
not force
&&
candidates.cd_unprocessed_size < Array.length candidates.cd_unprocessed - 1
then begin
false
end
else begin
match candidates.cd_preselected with
| Some _
when not Const.stable_derivation ->
clear_unprocessed candidates;
true
| _ ->
let rec build_at (index: int) : bool =
if index >= candidates.cd_unprocessed_size then begin
false
end
else begin
let candidate =
candidates.cd_unprocessed.(index)
in
match build_candidate candidates candidate with
| None ->
build_at (index + 1)
| Some _ as conflicting ->
begin
match candidates.cd_preselected with
| Some (old_candidate, _, _) when
Context_unifier.compare_context_unifiers
~different:true
old_candidate.ca_raw_context_unifier candidate
<= 0 ->
()
| _ ->
candidates.cd_preselected <- conflicting;
end;
true
end
in
let closing_found =
build_at 0
in
clear_unprocessed candidates;
closing_found
end
let add (candidates: candidates) (candidate: raw_context_unifier) : bool =
Statistic.inc_computed_assert_candidates candidates.cd_statistic;
candidates.cd_unprocessed.(candidates.cd_unprocessed_size) <- candidate;
candidates.cd_unprocessed_size <- candidates.cd_unprocessed_size + 1;
build_unprocessed ~force:false candidates
let rec rebuild_forgotten (candidates: candidates) : unit =
retract_valid_min candidates;
if ForgottenOrder.is_empty candidates.cd_forgotten_order then
()
else begin
let (forgotten_element, forgotten_barrier) =
ForgottenOrder.min_elt candidates.cd_forgotten_order
in
if
Valid.is_empty candidates.cd_valid
||
compare_candidates ~different:true forgotten_barrier (Valid.min candidates.cd_valid) < 0
then begin
ForgottenBarrier.remove candidates.cd_forgotten_barrier forgotten_element;
candidates.cd_forgotten_order <- ForgottenOrder.remove (forgotten_element, forgotten_barrier) candidates.cd_forgotten_order;
candidates.cd_forgotten_recomputation_barrier <- Some (forgotten_element, forgotten_barrier);
Problem_literals.compute_for_element candidates.cd_problem_literals forgotten_element Context_unifier.Assert;
ignore (build_unprocessed ~force:true candidates : bool);
candidates.cd_forgotten_recomputation_barrier <- None;
rebuild_forgotten candidates;
end
end
let rec get_best_candidate (candidates: candidates) : (candidate * literal * int) option =
rebuild_forgotten candidates;
if Valid.is_empty candidates.cd_valid then begin
None
end
else begin
let candidate =
Valid.remove_min candidates.cd_valid
in
retract_valid_min candidates;
if Context_unifier.is_raw_context_unifier_invalid candidate.ca_raw_context_unifier then begin
get_best_candidate candidates;
end
else begin
let assert_literal, index =
build_assert_literal candidate.ca_raw_context_unifier
in
match Context.check_subsumed candidates.cd_context assert_literal with
| Some _ ->
get_best_candidate candidates;
| None ->
Some (candidate, assert_literal, index)
end
end
let select (candidates: candidates) : selected option =
ignore (build_unprocessed ~force:true candidates : bool);
let candidate =
match candidates.cd_preselected with
| Some _ as conflicting ->
candidates.cd_preselected <- None;
conflicting
| None ->
get_best_candidate candidates
in
match candidate with
| None ->
candidates.cd_lookahead <- Selection_lookahead.create Const.max_assert_lookahead;
None
| Some (candidate, assert_literal, index) ->
let assert_literal =
Term.insert_literal assert_literal
in
let context_literals =
Context_unifier.get_context_literals
candidate.ca_raw_context_unifier.Context_unifier.rcu_context_partners
in
let context_unifier =
Context_unifier.recompute_full_unifier ~recompute:false candidate.ca_raw_context_unifier
in
State.apply_propagation
candidates.cd_state
assert_literal
context_unifier
candidate.ca_raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_clause
candidate.ca_raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_vars
index
(Context_unifier.create_space_utility_inc candidate.ca_raw_context_unifier.Context_unifier.rcu_space)
context_literals;
Some {
Selection_types.candidate_type = State.Propagation;
Selection_types.literal = assert_literal;
Selection_types.raw_context_unifier = candidate.ca_raw_context_unifier;
}
let check_exceeding (candidates: candidates) : unit =
if Config.is_horn candidates.cd_config then begin
if not (candidates.cd_unprocessed_size = 0) then
failwith "Selection_assert.check_exceeding";
candidates.cd_check_exceeding <- true;
begin
try
Context.iter
(fun element ->
if is_marked_exceeding candidates element then
Problem_literals.compute_for_element candidates.cd_problem_literals element Context_unifier.Assert;
)
candidates.cd_context;
ignore (build_unprocessed ~force:true candidates: bool);
with
| EXCEEDING_EXHAUSTED ->
()
end;
clear_unprocessed candidates;
candidates.cd_check_exceeding <- false
end
let backtrack_forgotten (candidates: candidates) : unit =
candidates.cd_forgotten_recomputation_barrier <- None;
let invalid_elements =
ForgottenBarrier.fold
(fun element candidate acc ->
if State.is_choice_point_invalid element.Context.el_choice_point then
(element, candidate) :: acc
else
acc
)
candidates.cd_forgotten_barrier
[]
in
List.iter
(fun (element, _) ->
ForgottenBarrier.remove candidates.cd_forgotten_barrier element;
)
invalid_elements;
candidates.cd_forgotten_order <-
List.fold_left
(fun forgotten (element, candidate) ->
ForgottenOrder.remove (element, candidate) forgotten
)
candidates.cd_forgotten_order
invalid_elements
let backtrack_exceeding (candidates: candidates) : unit =
let invalid_elements =
Hashtbl.fold
(fun id element acc ->
if State.is_choice_point_invalid element.Context.el_choice_point then
id :: acc
else
acc
)
candidates.cd_exceeding_elements
[]
in
List.iter
(fun id ->
Hashtbl.remove candidates.cd_exceeding_elements id
)
invalid_elements
let backtrack (candidates: candidates) : unit =
begin
match candidates.cd_preselected with
| None -> ()
| Some (candidate, _, _) ->
if Context_unifier.is_raw_context_unifier_invalid candidate.ca_raw_context_unifier then
candidates.cd_preselected <- None
else
failwith "Selection_assert.backrack: preselected"
end;
candidates.cd_lookahead <- Selection_lookahead.create Const.max_assert_lookahead;
Selection_lookahead.backtrack candidates.cd_lookahead_exceeding;
retract_valid_min candidates;
retract_valid_max candidates;
backtrack_forgotten candidates;
backtrack_exceeding candidates;
clear_unprocessed candidates