:- module(ic_sbds).
:- export sbds_initialise_body / 6.
:- export sbds_initialise_body / 5.
:- export unify_sbds / 2.
:- export print_sbds / 2.
:- export sbds_get_matrix / 2.
:- export sbds_get_index / 2.
:- export is_sbds_var / 1.
:- export sbds_try / 2.
:- export sbds_initialise / 5.
:- export sbds_initialise / 4.
:- tool(sbds_initialise / 5,sbds_initialise_body / 6).
:- tool(sbds_initialise / 4,sbds_initialise_body / 5).
:- comment(sbds_initialise_body / 6, hidden).
:- comment(sbds_initialise_body / 5, hidden).
:- comment(summary, "Symmetry Breaking During Search (SBDS)").
:- comment(desc, "Symmetry Breaking During Search (SBDS) library, provides predicates to initalise symmetry breaking during search search and to perform the search, as well as utility functions for prining and unification.").
:- comment(author, "Warwick Harvey & Karen Petrie").
:- comment(date, "$Date: 2006/09/23 01:53:35 $").
:- comment(is_sbds_var / 1, [summary : "checks whether a variable is an SBDS variable", args : ["Var" : "Variable to check"], amode : is_sbds_var(?), see_also : [sbds_initialise / 4, sbds_initialise / 5], desc : html("\tSucceeds if and only if Var is a variable and has been initialised\n\tas an SBDS variable (through a call to sbds_initialise/4 or\n\tsbds_initialise/5)."), fail_if : "Var is not a variable or not an SBDS variable."]).
:- comment(sbds_get_index / 2, [summary : "get the index of an SBDS attributed variable", args : ["Var" : "SBDS variable", "Idx" : "The index of Var"], amode : sbds_get_index(?, -), desc : html("\tUnifies Idx with the index of the SBDS variable Var.  The index of\n\tthe variable is its position in the matrix provided in the\n\tcorresponding call to sbds_initialise/4 or sbds_initialise/5"), fail_if : "Var is not an SBDS variable."]).
:- comment(sbds_get_matrix / 2, [summary : "get the matrix (array) of SBDS attributed variables", args : ["Var" : "SBDS variable", "Matrix" : "The matrix that Var appears in"], amode : sbds_get_matrix(?, -), desc : html("\tUnifies Matrix with the matrix (array) provided to a call to\n\tsbds_initialise/4 or sbds_initialise/5 which contains Var."), fail_if : "Var is not an SBDS variable."]).
:- comment(sbds_initialise / 4, [summary : "Initialises the data needed for sbds", args : ["VarMatrix" : "Matrix of Search Variables", "SymPreds" : "List of symmetry predicates", "FixPred" : "Predicate to assign a variable to a value", "Options" : "List of Options to use during search"], amode : sbds_initialise(+, +, ++, +), see_also : [sbds_initialise / 5, sbds_try / 2], desc : html("<H4><I> Symmetry Predicates </I></H4>\n\tVarMatrix, is the matrix of variables, which are searched over to allocate \t\tvalues to, in this case a 1-dimensonal matrix, for the n-dimensonal case see \t\tsbds_initalise/5.\n\t<br>\n\t<br>\n\tThe symmetry predicates should transform a variable and value to their \n\tsymmetrical equivalent. The last four arguments of these predicates should \n\ttherefore be the original variable, the original value (which are input) then \t\tthe symmetrical variable and the symmetrical value (which are output). Before \t\tthese parameters you can give any other parameters which are useful in your \t\timplementation\n\ti.e. the matrix of variables. So my symmetry predicate might be:\n\tsymmetry_predicate(Matrix, Var, Val, SymVar, SymVal).\n\t<br>\n\t<br>\n\tWhen creating the list of symmetry predicates (the parameter given to \t\t\tsbds_initalise), \n\tyou only need to specify the parameters that you have added. So for the above \t\tpredicate,\n\tthe entry to the list would be: symmetry_predicate(Matrix)\n\tThis is shown below in the N-Queens model.\n\t<br>\n\t<br>\n\tThe FixPred is the predicate which will fix and exclude a variable to a value \t\tat decision points in the search tree, it must have three parameters the first \t\ttwo will be the variable and the value, and the third will be a boolean which \t\tspecifies whether the variable is being fixed or excluded i.e. is this \t\t\tconstraint true or false. #= / 3, is usually used for thse purposes.\n\t<br>\n\t<br>\n\tThe Options list, will be a list of options which can be used during search \t\ti.e. whether SBDS should be used at every node of the search tree. None of \t\tthese options are implemented as yet, so it should always be an empty list.\n\t<br><H4><I> What SBDS initialise does: </I></H4>\n\tCalled before search commences. Sets up the symmetries to indicate that \t\t\n\tthey are all unbroken initally and initalises all the variables etc. that will \t\tbe utilised during search."), exceptions : [abort : "Options is not an empty list"], eg : html("<H3>Nqueens using a 1 dimensonal array</H3>\n\t<H4><I> The Symmetry Predicates for Nqueens Symmetries:</I></H4>\n\t<dl>\n\t<dt>r90(Matrix, N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[Value],</dd>\n\t\t<dd>SymValue is N + 1 - Index.</dd>\n\t</dl>\n\t\n\t<dl>\n\t<dt>r180(Matrix, N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[N + 1 - Index],</dd>\n\t\t<dd>SymValue is N + 1 - Value.</dd>\n\t</dl>\n\t\n\t<dl>\n\t<dt>r270(Matrix, N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[N + 1 - Value],</dd>\n\t\t<dd>SymValue is Index.</dd>\n\t</dl>\n\n\t<dl>\n\t<dt>rx(Matrix, N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[N + 1 - Index],</dd>\n\t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\n\t<dl>\n\t<dt>ry(Matrix, N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[Index],</dd>\n\t\t<dd>SymValue is N + 1 - Value.</dd>\n\t</dl>\n\n\t<dl>\n\t<dt>rd1(Matrix, _N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[Value],</dd>\n\t\t<dd>SymValue is Index.</dd>\n\t</dl>\n\n\t<dl>\n\t<dt>rd2(Matrix, N, Index, Value, SymVar, SymValue) :-</dt>\n\t\t<dd>SymVar is Matrix[N + 1 - Value],</dd>\n\t\t<dd>SymValue is N + 1 - Index.</dd>\n\t</dl>\n\t\t\n\t<H4><I> Then to initialise SBDS</I></H4>\n\t<dl>%If the Board is a list of variables then we change it to a matrix<br>\t\n\t<dt>Matrix =.. [[] | Board],</dt>\n\t<dt>%The list of symmetry predicates,</dt>\n\t<dt>Syms = [</dt>\n\t\t<dd>r90(Matrix, N),</dd>\n\t\t<dd>r180(Matrix, N),</dd>\n\t\t<dd>r270(Matrix, N),</dd>\n\t\t<dd>rx(Matrix, N),</dd>\n\t\t<dd>ry(Matrix, N),</dd>\n\t\t<dd>rd1(Matrix, N),</dd>\n\t\t<dd>rd2(Matrix, N)</dd>\n\t<dt>],</dt>\n\t<dt>%the call to sbds_initalise,</dt>\n\t<dt>sbds_initialise(Matrix, Syms, #=, []).</dt></dl> ")]).
:- comment(sbds_initialise / 5, [summary : "Initialises the data needed for sbds", args : ["VarMatrix" : "Matrix of Search Variables", "NDims" : "The Dimenson of VarMatrix", "SymPreds" : "List of symmetry predicates", "FixPred" : "Predicate to assign a variable to a value", "Options" : "List of Options to use during search"], amode : sbds_initialise(+, ++, +, ++, +), see_also : [sbds_initialise / 4, sbds_try / 2], desc : html("<H4><I> Symmetry Predicates </I></H4>\n\tVarMatrix, is the matrix of variables, which are searched over to allocate \t\tvalues to.\n\t<br>\n\t<br>\n\tNDims is the dimenson of VarMatrix\n\t<br>\n\t<br>\n\tThe symmetry predicates should transform a variable and value to their \n\tsymmetrical equivalent. The last four arguments of these predicates should \n\ttherefore be the original variable, the original value (which are input) then \t\tthe symmetrical variable and the symmetrical value (which are output). Before \t\tthese parameters you can give any other parameters which are useful in your \t\timplementation i.e. the matrix of variables. So my symmetry predicate might be:\n\tsymmetry_predicate(Matrix, Var, Val, SymVar, SymVal).\n\t<br>\n\t<br>\n\tWhen creating the list of symmetry predicates (the parameter given to \t\t\tsbds_initalise), you only need to specify the parameters that you have added. \t\tSo for the above predicate, the entry to the list would \t\t\t\tbe: symmetry_predicate(Matrix).\n\tThis is shown below in the N-Queens model.\n\t<br>\n\t<br>\n\tThe FixPred is the predicate which will fix and exclude a variable to a value \t\tat decision points in the search tree, it must have three parameters the first \t\ttwo will be the variable and the value, and the third will be a boolean which \t\tspecifies whether the variable is being fixed or excluded i.e. is this \t\t\tconstraint true or false. #= / 3, is usually used for thse purposes.\n\t<br>\n\t<br>\n\tThe Options list, will be a list of options which can be used during search \t\ti.e. whether SBDS should be used at every node of the search tree. None of \t\tthese options are implemented as yet, so it should always be an empty list.\n\t<br><H4><I> What SBDS initialise does: </I></H4>\n\tCalled before search commences. Sets up the symmetries to indicate that \t\t\n\tthey are all unbroken initally and initalises all the variables etc. that will \t\tbe utilised during search."), exceptions : [abort : "Options is not an empty list"], eg : html("<H3>Nqueens using a boolean encoding on a 2 dimensonal array</H3>\n\t<H4><I> The Symmetry Predicates for Nqueens Symmetries:</I></H4>\n\t<dl>\n\t<dt>r90(Matrix, N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[Y, N + 1 - X],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\t<dl>\n\t<dt>r180(Matrix, N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[N + 1 - X, N + 1 - Y],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\t<dl>\n\t<dt>r270(Matrix, N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[N + 1 - Y, X],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\t\n\t<dl>\n\t<dt>rx(Matrix, N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[N + 1 - X, Y],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\t<dl>\n\t<dt>ry(Matrix, N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[X, N + 1 - Y],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\t<dl>\n\t<dt>rd1(Matrix, _N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[Y, X],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\t<dl>\n\t<dt>rd2(Matrix, N, [X,Y], Value, SymVar, SymValue) :-</dt>\n    \t\t<dd>SymVar is Matrix[N + 1 - Y, N + 1 - X],</dd>\n    \t\t<dd>SymValue is Value.</dd>\n\t</dl>\n\t<H4><I> Then to initialise SBDS</I></H4>\n\t<dt>%The list of symmetry predicates,</dt>\n\t<dt>Syms = [</dt>\n\t\t<dd>r90(Matrix, N),</dd>\n\t\t<dd>r180(Matrix, N),</dd>\n\t\t<dd>r270(Matrix, N),</dd>\n\t\t<dd>rx(Matrix, N),</dd>\n\t\t<dd>ry(Matrix, N),</dd>\n\t\t<dd>rd1(Matrix, N),</dd>\n\t\t<dd>rd2(Matrix, N)</dd>\n\t<dt>],</dt>\n\t<dt>%the call to sbds_initalise,</dt>\n\t<dt>sbds_initialise(Matrix, 2, Syms, #=, []).</dt></dl> ")]).
:- comment(sbds_try / 2, [summary : "tries to assign a variable to a value", args : ["Var" : "SBDS Variable", "Val" : "Value to try and assign to Value"], amode : sbds_try(?, ++), see_also : [sbds_initialise / 4, sbds_initialise / 5, ic : search / 6], desc : html("Called for each variable, value pair tried at a decision point \t\tduring search.\n\t<br>\n\t<br>\n\tInitally tries to assign a variable to a value, if this is succesful then it \t\tupdates the symmetry functions to take account of any symmetries which have \t\tbeen broken by this assignment.\n\t<br>\n\t<br>\n\tIf this assignment is unsuccesful then it stipulates that the variable cannot \t\tbe assigned to this value, before placing constraints to eliminate the \t\t\tsymmetrical equivalent of this assgnment. If these symmetries are not already \t\tbroken"), exceptions : [abort : "Var is not an sbds attributed variable"], eg : html("<H4><I> Replacement for labeling/1 which takes SBDS into account \t\t</I></H4>\n\t<dl>\n\t<dt>sbds_labeling(AllVars) :-</dt>\n        <dd>( foreach(Var, AllVars) do</dd>\n\t  <dl><dd>count_backtracks,</dd>\n            <dd>sbds_indomain(Var)</dd></dl>                       \n        <dd>).</dd>\n        </dl>\n\t<H4><I>Replacement for indomain/1 which takes SBDS into account.</I></H4>\n\t<dl>\n\t<dt>% value ordering is input order</dt>\n\t<dt>sbds_indomain(X) :-</dt>\n\t\t<dd>nonvar(X).</dd>\n\t<dt>sbds_indomain(X) :-</dt>\n\t\t<dd>var(X),</dd>\n\t\t<dd>mindomain(X, LWB),</dd>\n\t\t<dd>%sbds_try called here</dd>\n\t\t<dd>sbds_try(X, LWB),</dd>\n\t\t<dd>sbds_indomain(X).</dd>\n\t</dl>")]).
