mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
library/array.m:
library/assoc_list.m:
library/bimap.m:
library/bitmap.m:
library/construct.m:
library/deconstruct.m:
library/dir.m:
library/hash_table.m:
library/injection.m:
library/io.stream_db.m:
library/kv_list.m:
library/list.m:
library/map.m:
library/robdd.m:
library/stream.string_writer.m:
library/term_conversion.m:
library/term_to_xml.m:
library/tree234.m:
library/type_desc.m:
library/version_hash_table.m:
For nearly every ordinary function in this directory that can fail in its
primary mode (all of which were semidet functions),
- provide a semidet predicate as an alternative, if it did not
already exist,
- implement the function in terms of the predicate, instead of vice versa,
- mark the semidet function as obsolete in favor of the semidet predicate
version,
- fix all the resulting warnings, and then
- comment out the obsolete pragmas (at least for now).
Note that this diff does not touch the semidet function in the
enum typeclass, or the functions that implement that method
in instances.
NEWS.md:
Announce the new predicates in the (documented) modules of the library.
browser/term_rep.m:
compiler/lp_rational.m:
compiler/mcsolver.m:
compiler/mode_ordering.m:
compiler/mode_robdd.equiv_vars.m:
compiler/mode_robdd.implications.m:
compiler/old_type_constraints.m:
compiler/pickle.m:
compiler/prog_event.m:
compiler/type_ctor_info.m:
compiler/var_table.m:
tests/hard_coded/bitmap_empty.m:
tests/hard_coded/construct_mangle.m:
tests/hard_coded/construct_packed.m:
tests/hard_coded/construct_test.m:
tests/hard_coded/dummy_type_construct.m:
tests/hard_coded/expand.m:
tests/hard_coded/foreign_enum_rtti.m:
tests/hard_coded/subtype_rtti.m:
tests/hard_coded/term_to_univ_test.m:
tests/hard_coded/type_to_term.m:
tests/hard_coded/type_to_term_bug.m:
Stop calling the semidet functions from the library that were temporarily
marked obsolete.
In a few places, add explicit type qualification to avoid warnings
about unresolved polymorphism.
tests/hard_coded/test_injection.exp:
Expect an abort message from the predicate version of a semidet function.
tests/declarative_debugger/ho_2.exp2:
Update this .exp file for a previous commit.
814 lines
26 KiB
Mathematica
814 lines
26 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2004-2007, 2009-2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: mcsolver.m.
|
|
% Main authors: rafe, richardf.
|
|
%
|
|
% Original author:
|
|
% Ralph Becket <rafe@cs.mu.oz.au>
|
|
% Fri Dec 31 14:45:18 EST 2004
|
|
%
|
|
% A constraint solver targeted specifically at David Overton's
|
|
% constraint-based mode analysis.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.mcsolver.
|
|
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.abstract_mode_constraints.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module pair.
|
|
|
|
% Convenient abbreviations.
|
|
:- type mcvar == abstract_mode_constraints.mc_var.
|
|
:- type mcvars == list(mcvar).
|
|
|
|
% Structure in which to collect constraints.
|
|
%
|
|
:- type prep_cstrts.
|
|
|
|
% Structure on which the solver operates.
|
|
%
|
|
:- type solver_cstrts.
|
|
|
|
% We start by collecting our constraints together in a prep_cstrts
|
|
% structure, before preparing them for the solver.
|
|
%
|
|
:- func new_prep_cstrts = prep_cstrts.
|
|
|
|
% Prepares the constraints described in abstract_mode_constraints.m
|
|
% appropriately.
|
|
%
|
|
:- pred prepare_abstract_constraints(list(mc_constraint)::in,
|
|
prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred equivalent(mcvar::in, mcvar::in, prep_cstrts::in, prep_cstrts::out)
|
|
is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred equivalent(list(mcvar)::in, prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred implies(mcvar::in, mcvar::in, prep_cstrts::in, prep_cstrts::out)
|
|
is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred not_both(mcvar::in, mcvar::in, prep_cstrts::in, prep_cstrts::out)
|
|
is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred different(mcvar::in, mcvar::in, prep_cstrts::in, prep_cstrts::out)
|
|
is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred assign(mcvar::in, bool::in, prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred equivalent_to_disjunction(mcvar::in, mcvars::in,
|
|
prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred at_most_one(mcvars::in, prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred exactly_one(mcvars::in, prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% NOTE: where possible, prepare_abstract_constraints/3 should be used
|
|
% rather than this predicate.
|
|
%
|
|
:- pred disjunction_of_assignments(list(list(pair(mcvar, bool)))::in,
|
|
prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
% Convert the set of constraints for use by the solver.
|
|
%
|
|
:- func make_solver_cstrts(prep_cstrts) = solver_cstrts.
|
|
|
|
% Nondeterministically enumerate solutions to the constraints.
|
|
%
|
|
:- pred solve(solver_cstrts::in, mc_bindings::out) is nondet.
|
|
|
|
% For debugging purposes only.
|
|
% :- pred main(io :: di, io :: uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module eqvclass.
|
|
:- import_module io.
|
|
:- import_module map.
|
|
:- import_module multi_map.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Assignment constraints.
|
|
%
|
|
:- type assgt
|
|
---> mcvar `assign` bool.
|
|
:- type assgts == list(assgt).
|
|
|
|
% Binary implication constraints. (Not a logical implication,
|
|
% just a unidirectional propagation path).
|
|
%
|
|
:- type impl
|
|
---> assgt `implies` assgt.
|
|
:- type impls == list(impl).
|
|
|
|
% Complex constraints.
|
|
%
|
|
:- type complex_cstrt
|
|
---> eqv_disj(mcvar, mcvars)
|
|
% var <-> OR(vars)
|
|
; at_most_one(mcvars)
|
|
; exactly_one(mcvars)
|
|
; disj_of_assgts(list(assgts)).
|
|
% Each element of the list is a list of assignments
|
|
% which should occur concurrently.
|
|
|
|
:- type complex_cstrts == list(complex_cstrt).
|
|
|
|
% Map from a variable to the complex constraints it participates in.
|
|
%
|
|
:- type complex_cstrt_map == multi_map(mcvar, complex_cstrt).
|
|
|
|
% A propagation graph is an optimised representation of a set of
|
|
% binary implication constraints. It consists of a pair of mappings
|
|
% from vars to consequent assignments, where prop_graph_yes field
|
|
% is the mapping when the var in question is bound to `yes', and the
|
|
% prop_graph_no field is the mapping when the var in question
|
|
% is bound to `no'.
|
|
%
|
|
:- type prop_graph
|
|
---> prop_graph(
|
|
prop_graph_yes :: multi_map(mcvar, assgt),
|
|
prop_graph_no :: multi_map(mcvar, assgt)
|
|
).
|
|
|
|
% We keep track of variables known to be equivalent in order
|
|
% to reduce the number of variables we have to worry about
|
|
% when solving the constraints.
|
|
%
|
|
:- type eqv_vars == eqvclass(mcvar).
|
|
|
|
% This type is just used to collect constraints before
|
|
% we prepare them for use by the solver.
|
|
%
|
|
:- type prep_cstrts
|
|
---> prep_cstrts(
|
|
prep_eqv_vars :: eqv_vars,
|
|
prep_assgts :: assgts,
|
|
prep_impls :: impls,
|
|
prep_complex_cstrts :: complex_cstrts
|
|
).
|
|
|
|
% This type just holds the various constraints passed to
|
|
% the solver. We separate constraints into four classes:
|
|
%
|
|
% - necessary equivalences are handled by renaming vars in
|
|
% an equivalence class to a single member of the equivalence
|
|
% class;
|
|
% - necessary assignments are dealt with after equivalence
|
|
% renaming;
|
|
% - the prop_graph represents the graph of binary implications,
|
|
% allowing propagation from assignments;
|
|
% - complex constraints, such as eqv_disj and at_most_one, which
|
|
% are attached to each variable in the complex constraint and
|
|
% tested after propagation through the prop_graph. Where
|
|
% possible, binary implications that follow from complex
|
|
% constraints are added to the prop_graph in order to simplify
|
|
% testing the complex constraints.
|
|
%
|
|
% TODO:
|
|
%
|
|
% - We should consider adding backjumping if performance is an issue.
|
|
%
|
|
:- type solver_cstrts
|
|
---> solver_cstrts(
|
|
vars :: mcvars,
|
|
eqv_vars :: eqv_vars,
|
|
assgts :: assgts,
|
|
prop_graph :: prop_graph,
|
|
complex_cstrt_map :: complex_cstrt_map
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
new_prep_cstrts = prep_cstrts(eqvclass.init, [], [], []).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Prepares the constraints described in abstract_mode_constraints.m
|
|
% appropriately.
|
|
%
|
|
prepare_abstract_constraints(Constraints, !PCs) :-
|
|
list.foldl(prepare_abstract_constraint, Constraints, !PCs).
|
|
|
|
% Prepares a constraint (as described in abstract_mode_constraints.m)
|
|
% appropriately.
|
|
%
|
|
:- pred prepare_abstract_constraint(mc_constraint::in,
|
|
prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
prepare_abstract_constraint(Constraint, !PCs) :-
|
|
(
|
|
Constraint = mc_atomic(VarConstraint),
|
|
prepare_var_constraint(VarConstraint, !PCs)
|
|
;
|
|
Constraint = mc_conj(Constraints),
|
|
prepare_abstract_constraints(Constraints, !PCs)
|
|
;
|
|
Constraint = mc_disj(Constraints),
|
|
( if
|
|
% Build var - bool pairs assuming structure
|
|
% mc_disj(mc_conj(assgts), mc_conj(assgts), ...), otherwise fail.
|
|
list.map(
|
|
( pred(mc_conj(Fls)::in, VarValPairs::out) is semidet :-
|
|
list.map((pred(mc_atomic(equiv_bool(Var, Val))::in,
|
|
(Var - Val)::out) is semidet), Fls, VarValPairs)
|
|
),
|
|
Constraints, DisjOfAssgts)
|
|
then
|
|
disjunction_of_assignments(DisjOfAssgts, !PCs)
|
|
else
|
|
sorry($pred, "Disjuction of constraints - general case.")
|
|
)
|
|
).
|
|
|
|
% Prepares an atomic constraint (as described in
|
|
% abstract_mode_constraints.m) appropriately.
|
|
%
|
|
:- pred prepare_var_constraint(var_constraint::in,
|
|
prep_cstrts::in, prep_cstrts::out) is det.
|
|
|
|
prepare_var_constraint(equiv_bool(Var, Value), !PCs) :-
|
|
assign(Var, Value, !PCs).
|
|
prepare_var_constraint(equivalent(Vars), !PCs) :-
|
|
equivalent(Vars, !PCs).
|
|
prepare_var_constraint(implies(Var1, Var2), !PCs) :-
|
|
implies(Var1, Var2, !PCs).
|
|
prepare_var_constraint(equiv_disj(X, Ys), !PCs) :-
|
|
equivalent_to_disjunction(X, Ys, !PCs).
|
|
prepare_var_constraint(at_most_one(Vars), !PCs) :-
|
|
at_most_one(Vars, !PCs).
|
|
prepare_var_constraint(exactly_one(Vars), !PCs) :-
|
|
exactly_one(Vars, !PCs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
equivalent(X, Y, !PCs) :-
|
|
!PCs ^ prep_eqv_vars := ensure_equivalence(!.PCs ^ prep_eqv_vars, X, Y).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
equivalent([], !PCs).
|
|
equivalent([X | Xs], !PCs) :-
|
|
list.foldl(equivalent(X), Xs, !PCs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
implies(X, Y, !PCs) :-
|
|
!PCs ^ prep_impls := [ (X `assign` yes) `implies` (Y `assign` yes),
|
|
(Y `assign` no) `implies` (X `assign` no)
|
|
| !.PCs ^ prep_impls ].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
not_both(X, Y, !PCs) :-
|
|
!PCs ^ prep_impls := [ (X `assign` yes) `implies` (Y `assign` no),
|
|
(Y `assign` yes) `implies` (X `assign` no)
|
|
| !.PCs ^ prep_impls ].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
different(X, Y, !PCs) :-
|
|
!PCs ^ prep_impls := [ (X `assign` yes) `implies` (Y `assign` no),
|
|
(X `assign` no) `implies` (Y `assign` yes),
|
|
(Y `assign` yes) `implies` (X `assign` no),
|
|
(Y `assign` no) `implies` (X `assign` yes)
|
|
| !.PCs ^ prep_impls ].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
assign(X, V, !PCs) :-
|
|
!PCs ^ prep_assgts := [X `assign` V | !.PCs ^ prep_assgts].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
equivalent_to_disjunction(X, Ys, !PCs) :-
|
|
(
|
|
Ys = [],
|
|
assign(X, no, !PCs)
|
|
;
|
|
Ys = [Y],
|
|
equivalent(X, Y, !PCs)
|
|
;
|
|
Ys = [_, _ | _],
|
|
!PCs ^ prep_complex_cstrts :=
|
|
[eqv_disj(X, Ys) | !.PCs ^ prep_complex_cstrts]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
at_most_one(Xs, !PCs) :-
|
|
(
|
|
Xs = []
|
|
;
|
|
Xs = [_]
|
|
;
|
|
Xs = [X, Y],
|
|
not_both(X, Y, !PCs)
|
|
;
|
|
Xs = [_, _, _ | _],
|
|
!PCs ^ prep_complex_cstrts :=
|
|
[at_most_one(Xs) | !.PCs ^ prep_complex_cstrts]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
exactly_one(Xs, !PCs) :-
|
|
(
|
|
Xs = [],
|
|
unexpected($pred, "exactly_one of zero variables")
|
|
;
|
|
Xs = [X],
|
|
assign(X, yes, !PCs)
|
|
;
|
|
Xs = [_, _ | _],
|
|
!PCs ^ prep_complex_cstrts :=
|
|
[exactly_one(Xs) | !.PCs ^ prep_complex_cstrts]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
disjunction_of_assignments(DisjOfAssgts, !PCs) :-
|
|
Assgtss =
|
|
list.map(list.map(func((Var - Value)) = Var `assign` Value),
|
|
DisjOfAssgts),
|
|
!PCs ^ prep_complex_cstrts :=
|
|
[disj_of_assgts(Assgtss) | !.PCs ^ prep_complex_cstrts].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
make_solver_cstrts(PCs) = SCs:-
|
|
% Simplify away equivalences.
|
|
Eqvs = PCs ^ prep_eqv_vars,
|
|
|
|
Assgts =
|
|
list.map(
|
|
func(X `assign` V) = (eqv_var(Eqvs, X) `assign` V),
|
|
PCs ^ prep_assgts
|
|
),
|
|
|
|
Impls =
|
|
list.map(
|
|
func((X `assign` VX) `implies` (Y `assign` VY)) =
|
|
((eqv_var(Eqvs, X) `assign` VX) `implies`
|
|
(eqv_var(Eqvs, Y) `assign` VY)),
|
|
PCs ^ prep_impls
|
|
),
|
|
|
|
ComplexCstrts =
|
|
list.map(eqv_complex_cstrt(Eqvs), PCs ^ prep_complex_cstrts),
|
|
|
|
% Construct the propagation graph.
|
|
PropGraph =
|
|
list.foldl(
|
|
func(((X `assign` VX) `implies` (Y `assign` VY)),
|
|
prop_graph(YesPG, NoPG)) =
|
|
( if VX = yes then
|
|
prop_graph(set(YesPG, X, (Y `assign` VY)), NoPG)
|
|
else
|
|
prop_graph(YesPG, set(NoPG, X, (Y `assign` VY)))
|
|
),
|
|
Impls,
|
|
prop_graph(multi_map.init, multi_map.init)
|
|
),
|
|
|
|
% Construct the complex constraints map.
|
|
ComplexCstrtsMap =
|
|
list.foldl(
|
|
func(ComplexCstrt, CCM) =
|
|
foldl(
|
|
func(Z, CCMa) = multi_map.set(CCMa, Z, ComplexCstrt),
|
|
complex_cstrt_vars(ComplexCstrt),
|
|
CCM
|
|
),
|
|
ComplexCstrts,
|
|
multi_map.init
|
|
),
|
|
|
|
% Find the set of variables we have to solve for.
|
|
AssgtVars =
|
|
list.foldl(
|
|
func((X `assign` _V), Vars) = [X | Vars],
|
|
Assgts,
|
|
[]
|
|
),
|
|
|
|
AndImplVars =
|
|
list.foldl(
|
|
func((X `assign` _VX) `implies` (Y `assign` _VY), Vars) =
|
|
[X, Y | Vars],
|
|
Impls,
|
|
AssgtVars
|
|
),
|
|
|
|
AndComplexCstrtVars =
|
|
list.foldl(
|
|
func(ComplexCstrt, Vars) =
|
|
complex_cstrt_vars(ComplexCstrt) ++ Vars,
|
|
ComplexCstrts,
|
|
AndImplVars
|
|
),
|
|
|
|
AllVars = sort_and_remove_dups(AndComplexCstrtVars),
|
|
|
|
% Make the solver constraints record.
|
|
SCs = solver_cstrts(AllVars, Eqvs, Assgts, PropGraph, ComplexCstrtsMap).
|
|
|
|
% eqv_var(Eqvs, Var) returns a representative member of all the
|
|
% variables equivalent to Var (in Eqvs)
|
|
%
|
|
:- func eqv_var(eqv_vars, mcvar) = mcvar.
|
|
|
|
eqv_var(Eqvs, X) = eqvclass.get_minimum_element(Eqvs, X).
|
|
|
|
% eqv_vars(Eqvs, Vars) just maps eqv_var to each Var in Vars.
|
|
%
|
|
:- func eqv_vars(eqv_vars, mcvars) = mcvars.
|
|
|
|
eqv_vars(Eqvs, Xs) = list.map(eqv_var(Eqvs), Xs).
|
|
|
|
% Returns all the variables that participate in this constraint.
|
|
%
|
|
:- func complex_cstrt_vars(complex_cstrt) = mcvars.
|
|
|
|
complex_cstrt_vars(eqv_disj(X, Ys)) = [X | Ys].
|
|
complex_cstrt_vars(at_most_one(Xs)) = Xs.
|
|
complex_cstrt_vars(exactly_one(Xs)) = Xs.
|
|
complex_cstrt_vars(disj_of_assgts(Assgtss)) =
|
|
list.foldl(list.foldl(func((V `assign` _), Vs) = [V | Vs]), Assgtss, []).
|
|
|
|
% Replaces all the variables in the supplied constraint with
|
|
% a representative variable from those constrained to be equivalent
|
|
% to the original.
|
|
%
|
|
:- func eqv_complex_cstrt(eqv_vars, complex_cstrt) = complex_cstrt.
|
|
|
|
eqv_complex_cstrt(Eqvs, eqv_disj(X, Ys)) =
|
|
eqv_disj(eqv_var(Eqvs, X), eqv_vars(Eqvs, Ys)).
|
|
|
|
eqv_complex_cstrt(Eqvs, at_most_one(Xs)) =
|
|
at_most_one(eqv_vars(Eqvs, Xs)).
|
|
|
|
eqv_complex_cstrt(Eqvs, exactly_one(Xs)) =
|
|
exactly_one(eqv_vars(Eqvs, Xs)).
|
|
|
|
eqv_complex_cstrt(Eqvs, disj_of_assgts(Assgtss)) =
|
|
disj_of_assgts(list.map(
|
|
list.map(func((Var `assign` Val)) = (eqv_var(Eqvs, Var) `assign` Val)),
|
|
Assgtss
|
|
)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Succeeds if Bindings satisfies the constraints SCs.
|
|
%
|
|
solve(SCs, Bindings) :-
|
|
solve(SCs, map.init, Bindings0),
|
|
bind_equivalent_vars(SCs, Bindings0, Bindings),
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.nl(OutputStream, !IO)
|
|
).
|
|
|
|
% solve(SCs, Bs0, Bs) succeeds if Bs satisfies the constraints SCs,
|
|
% given that Bs0 is known to not conflict with any of the constraints
|
|
% in SCs.
|
|
%
|
|
:- pred solve(solver_cstrts::in, mc_bindings::in, mc_bindings::out) is nondet.
|
|
|
|
solve(SCs, Bs0, Bs) :-
|
|
solve_assgts(SCs, SCs ^ assgts, Bs0, Bs1),
|
|
solve_vars(SCs, SCs ^ vars, Bs1, Bs).
|
|
|
|
% Propagates the binding for every variable that has been solved for
|
|
% to every variable it is equivalent to.
|
|
%
|
|
:- pred bind_equivalent_vars(solver_cstrts::in,
|
|
mc_bindings::in, mc_bindings::out) is det.
|
|
|
|
bind_equivalent_vars(SCs, !Bindings) :-
|
|
Equivalences = SCs ^ eqv_vars,
|
|
list.foldl(
|
|
( pred(Var::in, Binds0::in, Binds::out) is det :-
|
|
EquivVars = set.to_sorted_list(get_equivalent_elements(
|
|
Equivalences, Var)),
|
|
map.lookup(Binds0, Var, Val),
|
|
bind_all(EquivVars, Val, Binds0, Binds)
|
|
),
|
|
map.keys(!.Bindings), !Bindings).
|
|
|
|
% bind_all(Vars, Val, !Bindings) Binds Var to Val in Bindings for
|
|
% every Var in Vars.
|
|
%
|
|
:- pred bind_all(mcvars::in, bool::in, mc_bindings::in, mc_bindings::out)
|
|
is det.
|
|
|
|
bind_all(Vars, Val, !Bindings) :-
|
|
list.foldl(
|
|
( pred(Var::in, Binds0::in, Binds::out) is det :-
|
|
map.set(Var, Val, Binds0, Binds)
|
|
),
|
|
Vars, !Bindings).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% solve_assgts(SCs, Assgts, Bs0, Bs) attempts to perform each variable
|
|
% binding in Assgts, propagating the results when called for.
|
|
%
|
|
:- pred solve_assgts(solver_cstrts::in, assgts::in,
|
|
mc_bindings::in, mc_bindings::out) is semidet.
|
|
|
|
solve_assgts(SCs, Assgts, Bs0, Bs) :-
|
|
list.foldl(solve_assgt(SCs), Assgts, Bs0, Bs).
|
|
|
|
% solve_assgt(SCs, (X `assign` V), Bs0, Bs) attempts to bind variable X
|
|
% to value V. It propagates the results if it succeeds.
|
|
%
|
|
:- pred solve_assgt(solver_cstrts::in, assgt::in,
|
|
mc_bindings::in, mc_bindings::out) is semidet.
|
|
|
|
solve_assgt(SCs, (X `assign` V), Bs0, Bs) :-
|
|
% XXX
|
|
( if map.search(Bs0, X, V0) then
|
|
( if V = V0 then
|
|
true
|
|
else
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream,
|
|
mc_var_to_string(X) ++ " conflict", !IO)
|
|
)
|
|
),
|
|
V = V0,
|
|
Bs = Bs0
|
|
else
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream, ".", !IO)
|
|
),
|
|
% XXX
|
|
Bs1 = Bs0 ^ elem(X) := V,
|
|
|
|
Assgts = var_consequents(SCs ^ prop_graph, X, V),
|
|
solve_assgts(SCs, Assgts, Bs1, Bs2),
|
|
|
|
ComplexCstrts = var_complex_cstrts(SCs ^ complex_cstrt_map, X),
|
|
solve_complex_cstrts(SCs, X, V, ComplexCstrts, Bs2, Bs)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% solve_complex_cstrts(SCs, X, V, ComplexCstrts, Bs0, Bs) succeeds
|
|
% if the binding (X `assign` V) (which should already have been added
|
|
% to Bs0) is consistant with the complex constraints variable X
|
|
% participates in SCs. It also propagates results where appropriate.
|
|
%
|
|
:- pred solve_complex_cstrts(solver_cstrts::in, mcvar::in, bool::in,
|
|
complex_cstrts::in, mc_bindings::in, mc_bindings::out) is semidet.
|
|
|
|
solve_complex_cstrts(SCs, X, V, ComplexCstrts, Bs0, Bs) :-
|
|
list.foldl(solve_complex_cstrt(SCs, X, V), ComplexCstrts, Bs0, Bs).
|
|
|
|
% solve_complex_cstrt(SCs, X, V, ComplexCstrt, Bs0, Bs) succeeds
|
|
% if the binding (X `assign` V) (which should already have been added
|
|
% to Bs0) is consistant with ComplexCstrt (in which X should participate
|
|
% in SCs). It also propagates results where appropriate.
|
|
%
|
|
:- pred solve_complex_cstrt(solver_cstrts::in, mcvar::in, bool::in,
|
|
complex_cstrt::in, mc_bindings::in, mc_bindings::out) is semidet.
|
|
|
|
solve_complex_cstrt(SCs, X, V, eqv_disj(Y, Zs), Bs0, Bs) :-
|
|
( if X = Y then
|
|
(
|
|
V = yes,
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream, "1<", !IO)
|
|
),
|
|
not all_no(Bs0, Zs),
|
|
Bs = Bs0
|
|
;
|
|
V = no,
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream, "0<", !IO)
|
|
),
|
|
solve_assgts(SCs, list.map(func(Z) = Z `assign` no, Zs), Bs0, Bs)
|
|
)
|
|
else
|
|
% X in Zs
|
|
(
|
|
V = yes,
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream, ">1", !IO)
|
|
),
|
|
solve_assgt(SCs, Y `assign` yes, Bs0, Bs)
|
|
;
|
|
V = no,
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream, ">0", !IO)
|
|
),
|
|
( if all_no(Bs0, Zs) then
|
|
solve_assgt(SCs, Y `assign` no, Bs0, Bs)
|
|
else
|
|
Bs = Bs0
|
|
)
|
|
)
|
|
).
|
|
|
|
solve_complex_cstrt(SCs, X, V, at_most_one(Ys0), Bs0, Bs) :-
|
|
(
|
|
V = no,
|
|
Bs = Bs0
|
|
;
|
|
V = yes,
|
|
list.delete_first(Ys0, X, Ys),
|
|
solve_assgts(SCs, list.map(func(Y) = Y `assign` no, Ys), Bs0, Bs)
|
|
).
|
|
|
|
solve_complex_cstrt(SCs, X, V, exactly_one(Ys0), Bs0, Bs) :-
|
|
(
|
|
V = no,
|
|
% A variable in Ys0 uniquely not bound to 'no' is bound to yes.
|
|
% Fails if all Ys0 are 'no'.
|
|
Ys = list.filter(
|
|
(pred(Y0::in) is semidet :- not map.search(Bs0, Y0, no)), Ys0),
|
|
(
|
|
Ys = [Y],
|
|
solve_assgts(SCs, [Y `assign` yes], Bs0, Bs)
|
|
;
|
|
Ys = [_, _ | _],
|
|
Bs = Bs0
|
|
)
|
|
;
|
|
V = yes,
|
|
list.delete_first(Ys0, X, Ys),
|
|
solve_assgts(SCs, list.map(func(Y) = Y `assign` no, Ys), Bs0, Bs)
|
|
).
|
|
|
|
solve_complex_cstrt(SCs, X, V, disj_of_assgts(Assgtss), Bs0, Bs) :-
|
|
% Filter for the assignments compatible with binding X to V.
|
|
list.filter(
|
|
(pred(Assgts::in) is semidet :-
|
|
list.member(X `assign` bool.not(V), Assgts)
|
|
),
|
|
Assgtss, _Conflicts, NotConflicting),
|
|
|
|
% If only one set of assignments is possible now, make them.
|
|
% If none are possible, fail.
|
|
|
|
(
|
|
NotConflicting = [Assignments],
|
|
solve_assgts(SCs, Assignments, Bs0, Bs)
|
|
;
|
|
NotConflicting = [_, _ | _],
|
|
Bs = Bs0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% var_consequents(PropGraph, X, V) returns the assignments
|
|
% directly entailed by biding X to V.
|
|
%
|
|
:- func var_consequents(prop_graph, mcvar, bool) = assgts.
|
|
|
|
var_consequents(prop_graph(YesPG, _NoPG), X, yes) =
|
|
( if multi_map.search(YesPG, X, Assgts) then
|
|
Assgts
|
|
else
|
|
[]
|
|
).
|
|
var_consequents(prop_graph(_YesPG, NoPG), X, no) =
|
|
( if multi_map.search(NoPG, X, Assgts) then
|
|
Assgts
|
|
else
|
|
[]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% var_complex_cstrts(ComplexCstrtMap, X) returns the complex constraints,
|
|
% if any, that variable X participates in.
|
|
%
|
|
:- func var_complex_cstrts(complex_cstrt_map, mcvar) = complex_cstrts.
|
|
|
|
var_complex_cstrts(ComplexCstrtMap, X) =
|
|
( if map.search(ComplexCstrtMap, X, CmplxCstrts) then
|
|
CmplxCstrts
|
|
else
|
|
[]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% solve_vars(SCs, Vars, Bs0, Bs) non-deterministically assigns a value
|
|
% to each of Vars and propagates results, looking for a solution to SCs.
|
|
%
|
|
:- pred solve_vars(solver_cstrts::in, mcvars::in,
|
|
mc_bindings::in, mc_bindings::out) is nondet.
|
|
|
|
solve_vars(SCs, Vars, Bs0, Bs) :-
|
|
list.foldl(solve_var(SCs), Vars, Bs0, Bs).
|
|
|
|
:- pred solve_var(solver_cstrts::in, mcvar::in,
|
|
mc_bindings::in, mc_bindings::out) is nondet.
|
|
|
|
solve_var(SCs, X, Bs0, Bs) :-
|
|
( if map.contains(Bs0, X) then
|
|
Bs = Bs0
|
|
else
|
|
( V = yes ; V = no ),
|
|
trace [compiletime(flag("debug_mcsolver")), io(!IO)] (
|
|
% XXX STREAM
|
|
io.output_stream(OutputStream, !IO),
|
|
io.write_string(OutputStream,
|
|
"\n" ++ mc_var_to_string(X) ++ " = ", !IO),
|
|
io.print(OutputStream, V, !IO)
|
|
),
|
|
solve_assgt(SCs, X `assign` V, Bs0, Bs)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% all_no(Bs, Xs) succeeds if Bs indicates all Xs are bound to no.
|
|
%
|
|
:- pred all_no(mc_bindings::in, mcvars::in) is semidet.
|
|
|
|
all_no(_, []).
|
|
all_no(Bs, [X | Xs]) :-
|
|
% XXX
|
|
map.search(Bs, X, no),
|
|
all_no(Bs, Xs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% main(!IO) :-
|
|
%
|
|
% NameBindingss = solutions(solve(append_simple)),
|
|
%
|
|
% io.nl(!IO),
|
|
% io.write_list(NameBindingss, "\n\n", io.print, !IO),
|
|
% io.nl(!IO).
|
|
|
|
:- func mc_var_to_string(mc_var) = string.
|
|
|
|
mc_var_to_string(MCVar) = int_to_string(var_to_int(MCVar)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module check_hlds.mcsolver.
|
|
%-----------------------------------------------------------------------------%
|