| 1 | % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | :- module(bool_pred,[free_var/1, bool_negate_check/2, | |
| 6 | negate/2, bool_equality/3]). | |
| 7 | ||
| 8 | /* | |
| 9 | a module that supports a boolean type: pred_false, pred_true | |
| 10 | with an efficient negation procedure | |
| 11 | */ | |
| 12 | ||
| 13 | :- use_module(tools). | |
| 14 | :- use_module(self_check). | |
| 15 | :- use_module(error_manager). | |
| 16 | :- use_module(tools_portability, [exists_source/1]). | |
| 17 | ||
| 18 | :- use_module(module_information,[module_info/2]). | |
| 19 | :- module_info(group,kernel). | |
| 20 | :- module_info(description,'This module provides reified equality and negation for pred_true/pred_false.'). | |
| 21 | ||
| 22 | % Portable attributed variable handling. | |
| 23 | % Use SICStus-style library(atts) and verify_attributes/3 if available, | |
| 24 | % otherwise the SWI/hProlog-style attr builtins and attr_unify_hook/2. | |
| 25 | :- if(\+ exists_source(library(atts))). | |
| 26 | ||
| 27 | get_bool_pred_info(Var,Neg,ReifList) :- get_attr(Var,bool_pred,bool_pred_info(Neg,ReifList)). | |
| 28 | put_bool_pred_info(Var,Neg,ReifList) :- put_attr(Var,bool_pred,bool_pred_info(Neg,ReifList)). | |
| 29 | ||
| 30 | attr_unify_hook(bool_pred_info(NegThis,ThisReifList),Value) :- | |
| 31 | (var(Value) -> (get_attr(Value,bool_pred,bool_pred_info(NegValue,ValReifList)) | |
| 32 | -> check_equalities(ThisReifList,Value,NegValue,EqGoal1,NewReifList,RR), | |
| 33 | check_equalities(ValReifList,Value,NegThis,EqGoal2,RR,[]), | |
| 34 | (nonvar(Value) -> print(attr_unify_hook_instantiated_value(Value)),nl /* check equalities could have forced value to be known now; should not happen */ | |
| 35 | ; put_attr(Value,bool_pred,bool_pred_info(NegValue,NewReifList))), | |
| 36 | NegThis = NegValue, | |
| 37 | Value \== NegThis, | |
| 38 | EqGoal1, | |
| 39 | EqGoal2 | |
| 40 | ; put_attr(Value,bool_pred,bool_pred_info(NegThis,ThisReifList)) | |
| 41 | ) | |
| 42 | ; (ThisReifList=[] | |
| 43 | -> negate3(Value,NegThis) | |
| 44 | ; negate3(Value,NegThis), check_inst_equalities(ThisReifList,Value)) | |
| 45 | ). | |
| 46 | ||
| 47 | :- else. | |
| 48 | ||
| 49 | :- use_module(library(atts)). | |
| 50 | ||
| 51 | :- attribute bool_pred_info/2. | |
| 52 | ||
| 53 | get_bool_pred_info(Var,Neg,ReifList) :- get_atts(Var,+bool_pred_info(Neg,ReifList)). | |
| 54 | put_bool_pred_info(Var,Neg,ReifList) :- put_atts(Var,+bool_pred_info(Neg,ReifList)). | |
| 55 | ||
| 56 | % verify_attributes is called whenever a variable Var that might have attributes is about to be bound to Value (it might have none). | |
| 57 | % verify(VariableBeforeUnification, ValueWithWhichVariableUnifies, GoalWhichSICStusShouldCallAfterUnif) | |
| 58 | verify_attributes(ThisVar,Value,Goal) :- get_atts(ThisVar,+bool_pred_info(NegThis,ThisReifList)),!, | |
| 59 | % enter_log(ThisVar=Value), %% print(verify(ThisVar,Value,NegThis,ThisReifList)),nl, %% | |
| 60 | (var(Value) -> (get_atts(Value,+bool_pred_info(NegValue,ValReifList)) | |
| 61 | -> Goal=[NegThis=NegValue,ThisVar\==NegThis,EqGoal1,EqGoal2], | |
| 62 | check_equalities(ThisReifList,Value,NegValue,EqGoal1,NewReifList,RR), | |
| 63 | check_equalities(ValReifList,ThisVar,NegThis,EqGoal2,RR,[]), | |
| 64 | (nonvar(Value) -> print(verify_attribute_instantiated_value(Value)),nl /* check equalities could have forced value to be known now; should not happen */ | |
| 65 | ; put_atts(Value,+bool_pred_info(NegValue,NewReifList))) | |
| 66 | ; put_atts(Value,+bool_pred_info(NegThis,ThisReifList)), Goal=[] | |
| 67 | ) | |
| 68 | ; (ThisReifList=[] | |
| 69 | -> Goal=[negate3(Value,NegThis)] %(var(NegThis) -> Goal=[negate3(Value,NegThis)] ; Goal=[]) | |
| 70 | ; Goal=[negate3(Value,NegThis),check_inst_equalities(ThisReifList,Value)]) | |
| 71 | ). %, exit_log(Goal),trace. %, print(done_verify(ThisVar,Value,Goal)),nl. | |
| 72 | verify_attributes(_, _, [] ). | |
| 73 | ||
| 74 | ||
| 75 | :- endif. | |
| 76 | ||
| 77 | % store for each bool value the negated variable & list of reifications it is involved in | |
| 78 | % bool_pred_info(NegationOfVariable, List of eqcheck(OtherVariable,ReificationVariable) | |
| 79 | ||
| 80 | % check that we have a real variable without any bool_pred constraints attached | |
| 81 | free_var(X) :- var(X), \+ get_bool_pred_info(X,_,_). | |
| 82 | ||
| 83 | % check if currently X is negation of Y; call only for variable X | |
| 84 | bool_negate_check(X,Y) :- get_bool_pred_info(X,OtherX,_),Y==OtherX. | |
| 85 | ||
| 86 | negate(X,Y) :- %enter_log(negate(X,Y)), | |
| 87 | ? | (nonvar(X) -> negate3(X,Y) |
| 88 | ; nonvar(Y) -> negate3(Y,X) | |
| 89 | ; get_bool_pred_info(X,OtherX,_) -> Y=OtherX, X \== Y, update_bool_pred_negation(Y,X) | |
| 90 | ; get_bool_pred_info(Y,OtherY,_) -> put_bool_pred_info(X,Y,[]), X=OtherY, X \== Y | |
| 91 | ; X \== Y, put_bool_pred_info(Y,X,[]), put_bool_pred_info(X,Y,[]) | |
| 92 | ).% ,exit_log(negate(X,Y)). | |
| 93 | ||
| 94 | update_bool_pred_negation(X,_NegX) :- nonvar(X),!. | |
| 95 | update_bool_pred_negation(X,NegX) :- | |
| 96 | (get_bool_pred_info(OtherX,_,_) -> OtherX=NegX | |
| 97 | ; put_bool_pred_info(X,NegX,[])). | |
| 98 | ||
| 99 | %negate3(X,Y) :- print(negate3(X,Y)),nl,fail. | |
| 100 | negate3(pred_true,pred_false). % for kernel_equality,... results | |
| 101 | negate3(pred_false,pred_true). | |
| 102 | %negate3(bool_true,bool_false). % for boolean values | |
| 103 | %negate3(bool_false,bool_true). | |
| 104 | ||
| 105 | ||
| 106 | :- assert_must_succeed((bool_pred:enter_log(a),bool_pred:enter_log(b), | |
| 107 | bool_pred:exit_log(b),bool_pred:exit_log(a))). | |
| 108 | % a simple logging facility to replay calls later | |
| 109 | :- dynamic logging_level/1. | |
| 110 | logging_level(0). | |
| 111 | ||
| 112 | enter_log(X) :- retract(logging_level(L)), | |
| 113 | indent_ws(L),print(X), print(','),nl, | |
| 114 | L1 is L+1, assertz(logging_level(L1)). | |
| 115 | exit_log(G) :- retract(logging_level(L)), | |
| 116 | (L>0 -> L1 is L-1, assertz(logging_level(L1)), indent_ws(L1), print('exit '), print(G),nl | |
| 117 | ; add_internal_error('*** negative - logging level !',L), | |
| 118 | assertz(logging_level(L))). | |
| 119 | indent_ws(X) :- X<1,!. | |
| 120 | indent_ws(X) :- print(' '), X1 is X-1, indent_ws(X1). | |
| 121 | ||
| 122 | ||
| 123 | % check if any reification can now be determined | |
| 124 | % Idea: add other boolean operators here as well: imply, and, or ?? | |
| 125 | check_equalities([],_,_,true) --> []. | |
| 126 | check_equalities([eqcheck(V2,Res)|T],V1,NegV1,Goal) --> | |
| 127 | ( {V2==V1} -> {Goal=(Res=pred_true,G2)} | |
| 128 | ; {V2==NegV1} -> {Goal=(Res=pred_false,G2)} | |
| 129 | ; {V1==Res} -> {Goal=(V2=pred_true,G2)} % Warning: V2 could expect bool_true ! | |
| 130 | ; {NegV1==Res} -> {Goal=(V2=pred_false,G2)} % Warning: V2 could expect bool_false ! | |
| 131 | ; %{print(no_new_info),nl}, | |
| 132 | [eqcheck(V2,Res)], {Goal=G2} | |
| 133 | ),check_equalities(T,V1,NegV1,G2). | |
| 134 | ||
| 135 | % will be called with second argument instantiated to pred_false/pred_true | |
| 136 | ||
| 137 | :- public check_inst_equalities/2. % used above | |
| 138 | check_inst_equalities([],_). | |
| 139 | check_inst_equalities([eqcheck(V2,Res)|T],V1) :- | |
| 140 | (nonvar(V2) -> (V1=V2 -> Res=pred_true ; Res=pred_false) | |
| 141 | ; V1=pred_true -> Res=V2 | |
| 142 | ? | ; negate(V2,Res) |
| 143 | ? | ), check_inst_equalities(T,V1). |
| 144 | ||
| 145 | :- assert_must_succeed((bool_pred:negate(X,Y), bool_pred:negate(X,Z), Y==Z, Z=pred_true, X==pred_false)). | |
| 146 | % bool_pred:negate(X,Y), bool_pred:negate(X,Z), Y==Z, Z=pred_true, X==pred_false. | |
| 147 | /* | |
| 148 | CLP(FD) cannot do this: | |
| 149 | | ?- X in 0..1, Y in 0..1, Z in 0..1, X #\= Y, X#\=Z. | |
| 150 | X in 0..1, | |
| 151 | Y in 0..1, | |
| 152 | Z in 0..1 ? | |
| 153 | yes | |
| 154 | */ | |
| 155 | ||
| 156 | :- assert_must_succeed(( bool_pred:negate(X,Y), bool_pred:negate(Y,Z), X==Z, Z=pred_true, X==pred_true, Y==pred_false)). | |
| 157 | :- assert_must_succeed((bool_pred:negate(X,Y), bool_pred:negate(X2,Y2), X=X2, Y==Y2, Y=pred_false, X2==pred_true)). | |
| 158 | :- assert_must_succeed((bool_pred:negate(X,Y), bool_pred:negate(X,Z), Y==Z, Z=pred_true, X==pred_false)). | |
| 159 | :- assert_must_succeed((bool_pred:negate(X,Y), bool_pred:negate(Y,Z), \+ bool_pred:negate(Z,X))). | |
| 160 | :- assert_must_succeed((bool_pred:negate(X,Y), bool_pred:negate(Y,Z), bool_pred:negate(Z,X2), \+ X2=X)). | |
| 161 | :- assert_must_succeed((bool_pred:negate(X,Y), bool_pred:negate(Y,Z), X2=X, \+ bool_pred:negate(Z,X2))). | |
| 162 | :- assert_must_fail(bool_pred:negate(X,X)). | |
| 163 | :- assert_must_succeed(( bool_pred:negate(X,Y), bool_pred:negate(V,Z), bool_pred:bool_equality(X,V,RXV), Z=Y, X==V, RXV==pred_true)). | |
| 164 | :- assert_must_succeed(( bool_pred:negate(X,Y), bool_pred:negate(V,Z), bool_pred:bool_equality(X,V,RXV), Z=X, Y==V, RXV==pred_false)). | |
| 165 | :- assert_must_succeed(( bool_pred:negate(X,Y), bool_pred:negate(V,Z), bool_pred:bool_equality(X,V,RXV), RXV=pred_false, Y==V, X==Z)). | |
| 166 | :- assert_must_succeed(( bool_pred:negate(X,Y), bool_pred:negate(V,Z), bool_pred:bool_equality(X,V,RXV), RXV=pred_true, Y==Z, X==V)). | |
| 167 | :- assert_must_succeed((Y=pred_true, bool_pred:bool_equality(X,Y,R) , R==X)). | |
| 168 | :- assert_must_fail((Y=pred_false, bool_pred:bool_equality(X,Y,R) , R=X)). | |
| 169 | ||
| 170 | ||
| 171 | %:- attribute bool_equality/2. | |
| 172 | % bool_equality is actually solving: (X<=>Y) <=> R | |
| 173 | % Note this is commutative and associative in its three arguments | |
| 174 | bool_equality(X,Y,Res) :- %print(bool_equality(X,Y,Res)),nl, %% enter_log(bool_equality(X,Y,Res)), %% | |
| 175 | ( nonvar(X) -> eq1(X,Y,Res) | |
| 176 | ; nonvar(Y) -> eq1(Y,X,Res) | |
| 177 | ; nonvar(Res) -> eq1(Res,X,Y) | |
| 178 | ; X==Y -> Res=pred_true | |
| 179 | ; X==Res -> Y=pred_true % Warning: Y could expect bool_true ! | |
| 180 | ; Y==Res -> X=pred_true % Warning: X could expect bool_true ! | |
| 181 | ; get_bool_pred_info(X,NX,XList) | |
| 182 | -> (NX==Y -> Res=pred_false % Y is the negation of X | |
| 183 | ; NX==Res -> Y=pred_false % Warning: Y could expect bool_false ! | |
| 184 | ; put_bool_pred_info(X,NX,[eqcheck(Y,Res)|XList]), | |
| 185 | add_equality(Y,X,Res) | |
| 186 | ), | |
| 187 | blocking_force_eq3(Res,X,Y) | |
| 188 | ; add_new_equality(X,Y,Res),add_equality(Y,X,Res), | |
| 189 | blocking_force_eq3(Res,X,Y) | |
| 190 | ). % ,exit_log(eq(X,Y,Res)). | |
| 191 | ||
| 192 | % Missing rules (requires merging bool_true/pred_true, ...) | |
| 193 | % X==Res -> Y=pred_true && Y==Res -> X=pred_true | |
| 194 | % X==~Res -> Y=pred_false && Y==~Res -> X=pred_false [Missing above] | |
| 195 | ||
| 196 | add_equality(X,Y,Res) :- | |
| 197 | (get_bool_pred_info(X,NX,List) | |
| 198 | -> put_bool_pred_info(X,NX,[eqcheck(Y,Res)|List]) | |
| 199 | ; add_new_equality(X,Y,Res) | |
| 200 | ). | |
| 201 | add_new_equality(X,Y,Res) :- | |
| 202 | put_bool_pred_info(NX,X,[]),% NX is a fresh variable, no need to go through negate/2 | |
| 203 | put_bool_pred_info(X,NX,[eqcheck(Y,Res)]). | |
| 204 | ||
| 205 | % X -> bool_equality(Y,Res) : check if X==Y -> Res=pred_true or if Y==NegatedX -> Res=pred_false | |
| 206 | % we could also check if Res==X -> Y=pred_true | |
| 207 | ||
| 208 | ||
| 209 | eq1(pred_true,X,X). | |
| 210 | eq1(pred_false,X,Y) :- negate(X,Y). | |
| 211 | ||
| 212 | % equality if one of the bools (second arg) is known: wait for other bool (arg1) or result | |
| 213 | %:- block eq2(-,?,-). | |
| 214 | %eq2(X,Y,Res) :- nonvar(Res),!,eq1(Res,X,Y). | |
| 215 | %eq2(X,Y,Res) :- (X=Y -> Res=pred_true ; Res=pred_false). | |
| 216 | ||
| 217 | %:- block blocking_force_eq(-,?,?). % when((?=(Res,X) ; nonvar(Res)), --> we could detect force_eq(A,A,pred_true/false) | |
| 218 | %blocking_force_eq(Res,X,Y) :- force_eq(Res,X,Y). | |
| 219 | ||
| 220 | blocking_force_eq3(Res,X,Y) :- | |
| 221 | when((?=(Res,X) ; ?=(Res,Y) ; ?=(X,Y) ; nonvar(Res) ; nonvar(X) ; nonvar(Y)), bool_equality(X,Y,Res)). | |
| 222 | ||
| 223 | %blocking_force_eq2(Res,X,Y) :- when((?=(Res,X) ; ?=(Res,Y) ; ?=(X,Y) ; nonvar(Res)), force_eq2(Res,X,Y)). | |
| 224 | % | |
| 225 | %force_eq2(Res,X,Y) :- X==Res,!, %print(forcing_eq_eq1(Res,X,Y)),nl, | |
| 226 | % Y=pred_true. | |
| 227 | %force_eq2(Res,X,Y) :- Y==Res,!, %print(forcing_eq_eq2(Res,X,Y)),nl, | |
| 228 | % X=pred_true. | |
| 229 | %force_eq2(Res,X,Y) :- X==Y,!, %aprint(forcing_eq_eq3(Res,X,Y)),nl, | |
| 230 | % Res=pred_true. | |
| 231 | %force_eq2(pred_true,X,X). | |
| 232 | %force_eq2(pred_false,X,Y) :- negate(X,Y). | |
| 233 |