% =============================================================================
% Automated modeling in process-based qualitative reasoning
%  ** Knowledge-base module
% 
% April - July 2007
% 
% Hylke Buisman   - hbuisman@science.uva.nl
% =============================================================================

%==============================================================================
% INFORMATION
%==============================================================================

% It is important to know the GARP3 internal notation
% The notation is summed up below

% (In)equalities:
% A <  B              = smaller(A, B)
% A >  B              = greater(A, B)
% A =  B              = equal(A, B)
% A <= B              = smaller_or_equal(B, A)
% A >= B              = greater_or_equal(B, A)

% Causal dependencies:
% A    -- I+  --> B   = inf_pos_by
% A    -- I-  --> B   = inf_neg_by
% A    -- P+  --> B   = prop_pos(B,A)
% A    -- P-  --> B   = prop_neg(B,A)
% Q_A <-- Q   --> Q_B = q_correspondence(A, B)
% Q_A  -- Q   --> Q_B = dir_q_correspondence(A, B)
% Q_A <-- Qinv--> Q_B = mirror_q_correspondence(A, B)
% Q_A  -- Qinv--> Q_B = dir_mirror_q_correspondence(A, B)
% Q_A <-- V   --> Q_B = v_correspondence(A, B)
% Q_A  -- V   --> Q_B = dir_v_correspondence(A, B)

% Calculus:
% A = B - C           = equal(A, min(B, C))
% A = B + C           = equal(A, plus(B, C)) 



%==============================================================================
% DEPENDENCY CONSISTENCY CONSTRAINTS
%==============================================================================

% The dependency consistenty constraint rules
% check/return whether a certain dependency is consistent
% for given quantities in a given state.
% dependency_consistency_constraint/3
% dependency_consistency_constraint(+State, +QuantityList, ?Dependency)


%------------------------------------------------------------------------------
% Without interactions
%------------------------------------------------------------------------------

% Influences
dependency_consistency_constraint(StateId, [Q1, Q2], inf_pos_by(Q2, Q1)) :-
	sign(StateId, magnitude, Q1, Sign),
	sign(StateId, derivative, Q2, Sign),
	\+ member(Sign, [0, unkown]).
dependency_consistency_constraint(StateId, [Q1, Q2], inf_pos_by(Q2, Q1)) :-
	sign(StateId, magnitude, Q1, 0).
	
dependency_consistency_constraint(StateId, [Q1, Q2], inf_neg_by(Q2, Q1)) :-
	sign(StateId, magnitude, Q1, Sign),
	sign(StateId, derivative, Q2, Sign2),
	Sign is -Sign2,
	Sign \= 0. 
dependency_consistency_constraint(StateId, [Q1, Q2], inf_neg_by(Q2, Q1)) :-
	sign(StateId, magnitude, Q1, 0).

% Proportionalities w/o dep. interactions
dependency_consistency_constraint(StateId, [Q1, Q2], prop_pos(Q2, Q1)) :-
	sign(StateId, derivative, Q1, Sign),
	sign(StateId, derivative, Q2, Sign),
	\+ member(Sign, [unknown]).

dependency_consistency_constraint(StateId, [Q1, Q2], prop_neg(Q2, Q1)) :-
	sign(StateId, derivative, Q1, Sign),
	sign(StateId, derivative, Q2, Sign1),
	Sign is -Sign1.
	
% Correspondences
dependency_consistency_constraint(StateId, [Q1, Q2], q_correspondence(Q2Out, Q1Out)) :-
	get_qs_partition(Q1, Partition1),
	get_qs_partition(Q2, Partition2),
	equivalent_partitions(Partition1, Partition2),
	get_quantity_values(StateId, magnitude, Q1, V1),
	get_quantity_values(StateId, magnitude, Q2, V2),
	(nth0(Pos, Partition1, V1) ; nth0(Pos, Partition1, point(V1))), 
	(nth0(Pos, Partition2, V2) ; nth0(Pos, Partition2, point(V2))),
	% Sort order of Qs, since Q(Q1, Q2) <=> Q(Q2, Q1)
	sort([Q1, Q2], [Q1Out, Q2Out]).

dependency_consistency_constraint(StateId, [Q1, Q2], mirror_q_correspondence(Q2Out, Q1Out)) :-
	get_qs_partition(Q1, Partition1),
	get_qs_partition(Q2, Partition2),
	reverse(Partition2, RevP2),
	equivalent_partitions(Partition1, RevP2),
	get_quantity_values(StateId, magnitude, Q1, V1),
	get_quantity_values(StateId, magnitude, Q2, V2),
	(nth0(Pos, Partition1, V1) ; nth0(Pos, Partition1, point(V1))), 
	(nth0(Pos, RevP2, V2) ; nth0(Pos, RevP2, point(V2))),
	% Sort order of Qs, since mirror_Q(Q1, Q2) <=> mirror_Q(Q2, Q1)
	sort([Q1, Q2], [Q1Out, Q2Out]).
	
% Consistency check for the min calculus. Not used yet, but should be implement
% to check consistency via the GARP3 engine. Note that the cio
% should be filtered to only contain `known' info to avoid cheating.
% dependency_consistency_constraint(StateId, [Q1, Q2, Q3], equal(Q1,min(Q2, Q3))) :-
% 	engine:state(StateId, SMD),
% 	engine:get_store(SMD, Store),
% 	engine:store_cio(Store, Cio),
% 	engine:intern_representation(equal(Q1, min(Q2, Q3)), Intern, Cio, Cio2),
% 	engine:derivable_assumable_relation(Intern, Cio2,__Cio3).

	
%------------------------------------------------------------------------------
% With itneractions
%------------------------------------------------------------------------------
	
% Rules indicating dependency consistency constraints for dependency interaction (PAIRS)
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	get_quantity_inequalities(StateId, [Q2, Q3], Ineq1),
	get_quantity_inequalities(StateId, [Q3, Q2], Ineq2),
	(
	member(equal(Q2, Q3), Ineq1) 
	;  
	member(equal(Q3, Q2), Ineq2)
	),
	get_quantity_values(StateId, derivative, Q3, zero).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	sign(StateId, magnitude, Q2, 1),
	sign(StateId, magnitude, Q2, 1),
	get_quantity_inequalities(StateId, [Q2, Q3], Ineq1),
	get_quantity_inequalities(StateId, [Q3, Q2], Ineq2),
	
	(
	member(smaller(Q2, Q3), Ineq1) 
	;  
	member(greater(Q3, Q2), Ineq2)
	),
	get_quantity_values(StateId, derivative, Q3, min).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	sign(StateId, magnitude, Q2, -1),
	sign(StateId, magnitude, Q2, -1),
	get_quantity_inequalities(StateId, [Q2, Q3], Ineq1),
	get_quantity_inequalities(StateId, [Q3, Q2], Ineq2),
	
	(
	member(smaller(Q2, Q3), Ineq1) 
	;  
	member(greater(Q3, Q2), Ineq2)
	),
	get_quantity_values(StateId, derivative, Q3, plus).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	sign(StateId, magnitude, Q2, 1),
	sign(StateId, magnitude, Q2, 1),
	get_quantity_inequalities(StateId, [Q2, Q3], Ineq1),
	get_quantity_inequalities(StateId, [Q3, Q2], Ineq2),
	
	(
	member(greater(Q2, Q3), Ineq1) 
	;  
	member(smaller(Q3, Q2), Ineq2)
	),
	get_quantity_values(StateId, derivative, Q3, plus).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	sign(StateId, magnitude, Q2, -1),
	sign(StateId, magnitude, Q2, -1),
	get_quantity_inequalities(StateId, [Q2, Q3], Ineq1),
	get_quantity_inequalities(StateId, [Q3, Q2], Ineq2),
	
	(
	member(greater(Q2, Q3), Ineq1) 
	;  
	member(smaller(Q3, Q2), Ineq2)
	),
	get_quantity_values(StateId, derivative, Q3, min).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	sign(StateId, magnitude, Q2, 1),
	sign(StateId, magnitude, Q2, -1),
	get_quantity_values(StateId, derivative, Q3, plus).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]) :-
	sign(StateId, magnitude, Q2, -1),
	sign(StateId, magnitude, Q2, 1),
	get_quantity_values(StateId, derivative, Q3, min).

	
%
% Constraints for interactions (4)
%
dependency_consistency_constraint(StateId, [Q1, Q2, Q3, Q4, Q5], 
		[inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3), inf_pos_by(Q1, Q4), inf_neg_by(Q1, Q5)]) :-
	dependency_consistency_constraint(StateId, [Q1, Q2, Q3], [inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3)]).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3, Q4, Q5], 
		[inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3), inf_pos_by(Q1, Q4), inf_neg_by(Q1, Q5)]) :-
	dependency_consistency_constraint(StateId, [Q1, Q4, Q5], [inf_pos_by(Q1, Q4), inf_neg_by(Q1, Q5)]).
dependency_consistency_constraint(StateId, [Q1, Q2, Q3, Q4, Q5], 
		[inf_pos_by(Q1, Q2), inf_neg_by(Q1, Q3), inf_pos_by(Q1, Q4), inf_neg_by(Q1, Q5)]) :-
	is_smaller_than(StateId, Q2, Q3),
	is_greater_than(StateId, Q4, Q5),
	sign(StateId, derivative, Q2, 0).
	

	
%==============================================================================
% DEPENDENCY CONFLICTS
%==============================================================================

% Rules that indicate when a conflict between two dependencies arises
% Some of these are formally not in conflict, but they are considered 
% highly unlikely. In effect, they should not be included in any model.
% dependency_conflict/2
% dependency_conflict(+D1, +D2)
dependency_conflict(prop_pos(A, B), prop_pos(B, A)).
dependency_conflict(prop_neg(A, B), prop_neg(B, A)).
dependency_conflict(prop_pos(A, B), prop_neg(A, B)).
dependency_conflict(inf_pos_by(A, B), inf_pos_by(B, A)). % ??
dependency_conflict(inf_neg_by(A, B), inf_neg_by(B, A)). % ??
dependency_conflict(inf_pos_by(A, B), inf_neg_by(A, B)). % ??
dependency_conflict(greater(A,B), D2) :-
	D2 =.. [Rel, A, B],
	member(Rel, [smaller, smaller_or_equal, equal]).
dependency_conflict(smaller(A,B), D2) :-
	D2 =.. [Rel, A, B],
	member(Rel, [greater, greater_or_equal, equal]).

% Not two I/P mixed or the same in the same direction
dependency_conflict(D1, D2) :-
	D1 =.. [R1, A, B],
	D2 =.. [R2, A, B],
	member(X, [inf_, pro_]),
	atom_prefix(R1, X),
	member(Y, [inf_, pro_]),
	atom_prefix(R2, Y).


%==============================================================================
% SELECTORS
%==============================================================================
	
% Split the dependency name in its type and its sign
% split_dependency_atom/4
% split_dependency_atom(?Dependency, ?DepType, ?DepSign, ?DepDirection)
split_dependency_atom(inf_pos_by, inf, pos, directed).
split_dependency_atom(inf_neg_by, inf, neg, directed).
split_dependency_atom(prop_pos, prop, pos, directed).
split_dependency_atom(prop_neg, prop, neg, directed).
% For correspondences, the DepSign is used to represent
% if it is mirrored (inverse) or not. Notated in XPCE notation
split_dependency_atom(q_correspondence, q_correspondence, @off, undirected).
split_dependency_atom(dir_q_correspondence, q_correspondence, @off, directed).
split_dependency_atom(mirror_q_correspondence, q_correspondence, @on, undirected).
split_dependency_atom(dir_mirror_q_correspondence, q_correspondence, @on, directed).
split_dependency_atom(v_correspondence, v_correspondence, na, undirected).
split_dependency_atom(dir_v_correspondence, v_correspondence, na, directed).



