#! /usr/bin/swipl  -q -G1g -L1g -T1g -t main -s
% (home + travail)


/* ========================================================================== */ 
/*                  CONVERT ALEPH FORMAT TO LEAR format (int)                 */
/*             (a key must not be completly included in arguments of facts)   */
/*             (Attention à la notion de Keys associés)                       */
/*             IMPORTANT :                                                    */
/*             Cette version verifie que les clés derivees ne ramenent pas    */
/*             d'informations concernant d'autres exemples que l'exemple      */
/*             courant                                                        */
/*             Dominique BOUTHINON (db@lipn.univ-paris13.fr)                  */
/*             1/12/12                                                        */
/*             IMPORTANT : VERSION A UTILISER DESORMAIS !!!                   */
/*             - numeroteration des examples negatifs commençant a 1          */
/*             - CALL : alephToLear3.pl examplesFile backgroundFile learFile  */
/*                                                                            */
/* ========================================================================== */

main :-
        catch(goal, E, (print_message(error, E), fail)),
        halt.

main :-
        halt(1).

goal :-
        get_arguments(File_train, File_bkg, File_lear),
	    convert(File_train, File_bkg, File_lear),
        writeln('end.').
 
get_arguments(File_train, File_bkg, File_lear) :-
        current_prolog_flag(argv, Argv),
        append(_, [--|[File_train, File_bkg, File_lear]], Argv).

/* ==================== ACTIVE MODULES and CONSULT DATA ===================== */
 
active_modules :-
   getenv('LEAR_DIR', Lear),
   atom_concat(Lear, '/file.pl', File),
   use_module(File),

   atom_concat(Lear, '/counter.pl', Counter),
   use_module(Counter),  

   atom_concat(Lear, '/list.pl', List),
   use_module(List),
   
   atom_concat(Lear, '/term.pl', Term),
   use_module(Term),

   atom_concat(Lear, '/utilities_lear11.pl', Utils),
   use_module(Utils).

/* =========================== TRANSLATION OF EXAMPLES ====================== */

   /* create a file containing positive and negative incomplete examples 
      in a Lear format */
convert(
		   File_train,    % train examples
		   File_bkg, 	   % background knowledge
		   File_lear	   % name of the ouptut file (with lear format)
	   ) :- 

    active_modules,
    writeln('reading files...'),
    consult_data(File_train, File_bkg),
    aleph_to_lear(File_lear).  
 
	/* get all defined user predicates of the background knowledge */  
aleph_to_lear(File_lear) :-

   writeln('converting examples...'),
   get_modes(LModes),
   get_examples(LModes, LPos, LNeg),

					  
% add the types that are possibly only in 
                           % the head of the target concept
   get_head(Concept),  
   discard_plus_minus([Concept], [Concept1]),
   LModes1 = [Concept1|LModes],

   get_types(LModes1, LTypes1),
   sort(LTypes1, LTypes),				% mandatory
   write_lear_examples(File_lear, LPos, LNeg, LModes1, LTypes).
  
/* ============================== CONSULT DATA ============================== */

consult_data(File_train, File_bkg) :-
   		% consult positive examples
   atom_concat(File_train, '.f', File_train_pos),
   pos:consult(File_train_pos),
   
		% consult negative examples
   atom_concat(File_train, '.n', File_train_neg),
   neg:consult(File_train_neg),
  
		% consult background knowledge
   atom_concat(File_bkg, '.b', File_bkg1),
   bkg:consult(File_bkg1).

/* ================== READ EXAMPLES AND BACKGROUND KNOWLEDGE ================ */  
    
get_examples(LModes, LPos, LNeg) :-
   get_head_concept(Concept),
   build_examples(LModes, Concept, pos, LPos),
   build_examples(LModes, Concept, neg, LNeg).  


get_head(Concept) :-
  bkg:current_predicate(modeh/2),
  !,
  bkg:modeh(_, Concept).

get_head(Concept) :-
  bkg:modeh(Concept).


	/* get head examples and predicates used in hypotheses */
get_head_concept(Head_concept) :-
  get_head(Concept),
  functor(Concept, Name, Arity),
  functor(Head_concept, Name, Arity).


	/* build examples from heads and facts using LPredicates */
build_examples(LModes, Head_ex, Label, LEx) :-
   findall(Head_ex, Label:Head_ex, LHead_ex),
   get_examples_properties(LModes, LHead_ex, LEx).


	/* get in background knowledge, the facts (properties) associated 
        with given heads of concepts */
get_examples_properties(_LModes, [], []) :-
   !.

get_examples_properties(LModes, [Head_ex|LHead_ex], [[Head_ex|Ex]|LEx]) :-

   writeln(Head_ex),
   get_key_current_example(Head_ex, Key),
               % A Key is a list of (type, ident) unique for every example
			      % in case identifiers are compound we must consider other
               % identifiers
   get_list_keys(Key, LKey),	

			% in this version we consider a Key as a unique pair (type,value)
   flatten_keys(LKey, LKey1),
  
			% get in Ex1, all facts that contains in their arguments
               % a PART OF key in LKey
   get_properties_example_keys(LKey1, LModes, Ex1),	
   sort(Ex1, Ex2),

			% LTypes contains types used for identifying objects in 
               % the examples
   bkg:types_identifiers(LIdentifiers),	   
   build_associated_keys(Ex2, LModes, LIdentifiers, LAssociated_keys),

   flatten_keys(LAssociated_keys, LAssociated_keys1),

   append(LKey1, LAssociated_keys1, LAttributes),
   build_domains(LAttributes, LDomains),

  
			% LTypes contains types (other than those in LKey)
               % describing objects. These types are linked with type of LKey	
   get_properties_example_keys1(LAssociated_keys1, LDomains, LModes, Ex3),

   append(Ex2, Ex3, Ex4),
   sort(Ex4, Ex),

   get_examples_properties(LModes, LHead_ex, LEx).


	/* build domains of values found in list of (Type, Value) */
build_domains([], []) :-
   !.

build_domains([(Type, Value)|L], LDomains) :-
   build_domains(L, LDomains1),
   insert_domain(LDomains1, Type, Value, LDomains).


insert_domain([], Type, Value, [(Type, [Value])]) :-
   !.

insert_domain([(Type, LValues)|L], 
              Type, Value, 
              [(Type, [Value|LValues])|L]) :-
   !.

insert_domain(
              [(Type1, LValues)|L], 
              Type, Value, 
              [(Type1,LValues)|L1]
             ) :-
   insert_domain(L, Type, Value, L1).



flatten_keys(LKey, LKey_out) :-
 flatten(LKey, LKey1),
 sort(LKey1, LKey_out).



	/* build keys associated with Key identifying examples */
build_associated_keys(Ex, LModes, LTypes, LKeys) :-
   select_modes(Ex, LModes, LModes1),
   build_keys_facts(Ex, LModes1, LTypes, LKeys).


build_keys_facts([], [], _LTypes, []) :-
   !.

build_keys_facts([Fact|LFacts], [Mode|LModes], LTypes, LKeys) :-
   build_keys_one_fact(Fact, Mode, LTypes, LKeys1),
   build_keys_facts(LFacts, LModes, LTypes, LKeys2),
   append(LKeys1, LKeys2, LKeys).

   
build_keys_one_fact(Fact, Mode, LTypes, LKeys) :-
   Fact=..[Pred|LArgs_fact],
   Mode=..[Pred|LArgs_mode],
   associate_types_values(LArgs_fact, LArgs_mode, LTypes, LKeys).


associate_types_values([], [], _LTypes, []) :-
   !.

associate_types_values([Value|LValues], [Arg_type|LArgs_types], 
                        LTypes, [[(Arg_type, Value)]|LKeys]) :-
   member(Arg_type, LTypes),
   !,
   associate_types_values(LValues, LArgs_types, LTypes, LKeys).

associate_types_values([_|LValues], [_|LArgs_types], LTypes, LKeys) :-
   associate_types_values(LValues, LArgs_types, LTypes, LKeys).
    

	/* select the modes in LModes that describe the facts in LFacts */
select_modes([], _LModes, []) :-
   !.

select_modes([Fact|LFacts], LModes, [Mode|LModes1]) :-
   functor(Fact, Pred, Arity),
   functor(Mode, Pred, Arity),
   member(Mode, LModes),
   !,
   select_modes(LFacts, LModes, LModes1).

select_modes([_Fact|LFacts], LModes, LModes1) :-
   select_modes(LFacts, LModes, LModes1).
   

/* ========= GET PROPERTIES CONTAINING A PART OF THE KEY OF EXAMPLES ======== */

get_properties_example_keys([], _,  []) :-
   !.

get_properties_example_keys([Key|LKey], LModes,  Ex) :-
   get_properties_example(LModes, Key, Ex1),
   get_properties_example_keys(LKey, LModes, Ex2),
   append(Ex1, Ex2, Ex).


	/* get all properties (facts) having a part of Key as parameters */
get_properties_example([], _Key, []) :-
   !.

get_properties_example([Mode|LModes], Key, Ex) :-
   find_instances_key(Mode, Key, LFacts1),
   get_properties_example(LModes, Key, LFacts2),
   append(LFacts1, LFacts2, Ex).
    

	/* find instances of Predicate that contains a part of Key */
find_instances_key(Mode, Key, LFacts) :-
   Mode=..[_|LTypes],
   build_predicate_instances(Key, [Mode], LModes),
   replace_types_by_variables(LModes, LTypes, LModes1),
   find_all_instances(LModes1, LFacts).


/* =================== GET PROPERTIES LINKED WITH THE KEY =================== */

get_properties_example_keys1([], _, _, []) :-
   !.

get_properties_example_keys1([Key|LKey], LDomains, LModes,  Ex) :-
   get_properties_example1(LModes, Key, LDomains, Ex1),
   get_properties_example_keys1(LKey, LDomains, LModes, Ex2),
   append(Ex1, Ex2, Ex).


	/* get all properties (facts) having a part of Key as parameters */
get_properties_example1([], _Key, _LDomains, []) :-
   !.

get_properties_example1([Mode|LModes], Key, LDomains, Ex) :-
   find_instances_key1(Mode, Key, LDomains, LFacts1),
   get_properties_example1(LModes, Key, LDomains, LFacts2),
   append(LFacts1, LFacts2, Ex).
    

	/* find instances of Predicate that contains a part of Key */
find_instances_key1(Mode, Key, LDomains, LFacts) :-
   Mode=..[_|LTypes],
   build_predicate_instances(Key, [Mode], LModes),
   replace_types_by_variables(LModes, LTypes, LModes1),
   find_all_instances(LModes1, LFacts1),
   discard_non_relevant_facts(LFacts1, LDomains, LTypes, LFacts).


	/* discard facts not associated with the current example */
discard_non_relevant_facts([], _, _, []) :-
   !.

discard_non_relevant_facts([Fact|LFact], LDomains, LTypes, [Fact|LFact1]) :-
	relevant_fact(Fact, LTypes, LDomains),
	!,
   discard_non_relevant_facts(LFact, LDomains, LTypes, LFact1).


discard_non_relevant_facts([_Fact|LFact], LDomains, LTypes, LFact1) :-
   discard_non_relevant_facts(LFact, LDomains, LTypes, LFact1).
   

	/* check whether a given fact is associated or not with the current
      example */
relevant_fact(Fact, LTypes, LDomains) :-
   Fact =..[_|LArgs],
   make_couples(LTypes, LArgs, LCouples),
   build_domains(LCouples, LDomains_facts),
   check_relevance(LDomains_facts, LDomains).


	/* check whether each Type both appearing in L and LKey_ex is 
   	associated with a value appearing in LKey */
check_relevance([], _LDomains) :-
   !.

check_relevance([(Type, LValue)|L], LDomains) :-
   check_relevance_type(Type, LValue, LDomains),
   check_relevance(L, LDomains).


	/* check wether LValue is a subset of authorized values for this Type */ 
check_relevance_type(Type, LValue, LDomains) :-
   member((Type, L) , LDomains),
   !,                             % Type is referenced in LDomains
   my_subset(LValue, L).

	% Type is not referenced in LDomains
check_relevance_type(_Type, _LValue, _LDomains).



	/* build coples with an argument of each list */
make_couples([], [], []) :-
   !.

make_couples([X1|L1], [X2|L2], [(X1,X2)|L]) :-
   make_couples(L1, L2, L).


	/* find in bkg all facts having patterns in LModes */
find_all_instances([], []) :-
   !.

find_all_instances([Mode|LModes], LFacts) :-
   findall(Mode, bkg:Mode, LFacts1),
   find_all_instances(LModes, LFacts2),
   append(LFacts1, LFacts2, LFacts).


	/* replace all types of LTypes in LModes by variables */
replace_types_by_variables([], _LTypes, []) :-
   !.

replace_types_by_variables([Mode|LModes], LTypes, [Mode1|LModes1]) :-
   Mode=..[Functor|LArgs_mode],
   change_types(LArgs_mode, LTypes, LArgs_mode1),
   Mode1=..[Functor|LArgs_mode1],
	replace_types_by_variables(LModes, LTypes, LModes1).

change_types([], _LTypes, []) :-
   !.

change_types([Arg|LArgs], LTypes, [_X|LArgs1]) :-
   member(Arg, LTypes),
   !,
   change_types(LArgs, LTypes, LArgs1).

change_types([Arg|LArgs], LTypes, [Arg|LArgs1]) :-
    change_types(LArgs, LTypes, LArgs1).
   

	/* returns all combinations by replacing a type of each predicate with 
      a value of the same type of Key */

build_predicate_instances((Type,Value), LPredicates, LPredicates1) :-
   replace_one_parameter_predicates(LPredicates, Type, Value, LPredicates1).
  
   
replace_one_parameter_predicates([], _Type, _Value, []) :-
   !.

replace_one_parameter_predicates([Predicate|LPredicates], Type, Value, LP) :-
   replace_one_paremeter_one_predicate(Predicate, Type, Value, LP1),
   replace_one_parameter_predicates(LPredicates, Type, Value, LP2),
   append(LP1, LP2, LP).


	/* return all combinations by replacing a parameter Type of Predicate, 
      with Value */
replace_one_paremeter_one_predicate(Predicate, Type, Value, LPredicates) :-
   Predicate=..[Pred|LArgs],
   findall(LArgs1, replace_one_element(LArgs, Type, Value, LArgs1), LLArgs),
   make_predicates(LLArgs, Pred, LPredicates).


	/* return all combinations by replace one value Type of LArgs with Value */
replace_one_element([], _Type, _Value, []) :-
   !,
   fail.

replace_one_element([Type|LArgs], Type, Value, [Value|LArgs]).
        % ne pas mettre de cut

replace_one_element([Arg|LArgs], Type, Value, [Arg|LArgs1]) :-
   replace_one_element(LArgs, Type, Value, LArgs1).
   

 	/* build a list of predicates from list of arguments */
make_predicates([], _Pred, []) :-
   !.

make_predicates([Args|LArgs], Pred, [Pred1|LPred1]) :-
   Pred1=..[Pred|Args],
   make_predicates(LArgs, Pred, LPred1).     
   

/* ============================ MANAGE KEY OF EXAMPLES ====================== */

	/* get a list of idents, when Ident is a compound term */
get_list_keys(Key, [Key]) :-
   not_compound_element(Key),
   !.

get_list_keys(Key, [Key|LKey]) :-
   decompound(Key, Key1),
   get_list_keys(Key1, LKey).


	/* break down arguments elements of L */
decompound([], []) :-
   !.

decompound([(Type,Value)|L], [(Type, Value1)|L1]) :-
   Value=..[_|[Value1]],
   decompound(L, L1).
   
   
		/* check whether L contains or not a compound element */
not_compound_element([]) :-
   !.

not_compound_element([(_Type, Value)|_]) :-
   compound(Value),
   !,
   fail.

not_compound_element([_|L]) :-
   not_compound_element(L).
  

	/* get the list of (type, identifier) associated with current example */
get_key_current_example(Head_ex, Key) :-
   get_head(Mode),
   Mode=..[_|LArgs_mode],
   Head_ex=..[_|LArgs_ex],
   get_key_identifiers(LArgs_mode, LArgs_ex, Key).


	/* get the Key */
get_key_identifiers([], [], []) :-
   !.

get_key_identifiers([+Type|LTypes], [Value|LValues], [(Type,Value)|Key]) :-
   !,
   get_key_identifiers(LTypes, LValues, Key).

get_key_identifiers([_|LTypes], [_|LValues], Key) :-
   get_key_identifiers(LTypes, LValues, Key). 


/* ========================================================================== */
  

 	/* gather all modes in a list */
get_modes(LModes) :-
   get_the_modes(LModes1),
   discard_plus_minus(LModes1, LModes).

get_the_modes(LModes) :-
   bkg:current_predicate(modeb/2),
   !,
   findall(Mode, bkg:modeb(_, Mode), LModes).

get_the_modes(LModes) :-
   findall(Mode, bkg:modeb(Mode), LModes).

	/* gather all types present in LModes in a list */
get_types([], []) :-
   !.

get_types([Mode|LModes], LTypes) :-
   Mode=..[_|LTypes1],
   get_types(LModes, LTypes2),
   append(LTypes1, LTypes2, LTypes).
   

/* ==============================  WRITTING EXAMPLES ======================== */

write_lear_examples(
                     File,
                     LPos, 
                     LNeg,
                     LModes,
				 LTypes
                     ) :- 
   atom_concat(File, '.int', File1),
   open(File1, write, Stream),

   write(Stream, ':- style_check(-discontiguous).'), 
   nl(Stream),

   reset_count(nb_examples),
   write_examples(pos, 1, LPos, LModes, LTypes, Stream),
   nl(Stream),
   nl(Stream),
   write_examples(neg, 1, LNeg, LModes, LTypes, Stream),
   
   close(Stream).


write_examples(_Label, _N, [], _LModes, _LTypes, _Stream) :-
   !.

write_examples(Label, N, [Ex|Lex], LModes, LTypes, Stream) :-
   write_one_example(Label, N, Ex, LModes, LTypes, Stream),
   N1 is N+1,
   write_examples(Label, N1, Lex, LModes, LTypes, Stream).


/* write the current example in lea format */ 
write_one_example(Label, N, [Head|Ex], LModes, LTypes, Stream) :-
  fwrite(Stream, Label),
  write(Stream, '('),
  write(Stream, N),
  write(Stream,','),
  nl(Stream),
  fwrite_list(Stream, Ex),
  fwrite_point_ln(Stream, ')'),
  nl(Stream),
  fwrite_point_ln(Stream, target(N, Label, Head)),
  write_domains(Stream, Label, N, LModes, LTypes, [Head|Ex]),
  nl(Stream),
  nl(Stream).
  
  
write_domains(Stream, Label, N, LModes, LTypes, Ex) :-
  get_domains(LTypes, LModes, Ex, LDomains),
  write(Stream, 'domains('),
  write(Stream, N),
  write(Stream, ','),
  write(Stream, Label),
  write(Stream,','),
  fwrite_list(Stream, LDomains),
  write(Stream, ').').
  

	/* get values of types in the example Ex */  
get_domains([], _LModes, _Ex, []) :-
   !.
	
get_domains([Type|LTypes], LModes, Ex, [Domain|LDomains]) :-
   get_values_type(Type, LModes, Ex, LValues),
   sort(LValues, LValues1),
   not_empty(LValues1),
   !,
   Domain=..[Type|[LValues1]],
   get_domains(LTypes, LModes, Ex, LDomains).
   
get_domains([_Type|LTypes], LModes, Ex, LDomains) :-
    get_domains(LTypes, LModes, Ex, LDomains).
         

	/* get all values of the given type in the example Ex */
get_values_type(_Type, [], _Ex, []) :-
   !.
	
get_values_type(Type, [Mode|LModes], Ex, LValues) :-
   Mode=..[Pred|LArgs],
   get_indices(1, Type, LArgs, LIndices),
   length(LArgs, N),
   functor(Mode1, Pred, N), 
   get_values(LIndices, Mode1, Ex, LValues1),
   get_values_type(Type, LModes, Ex, LValues2),
   append(LValues1, LValues2, LValues).
   
   
	/* get indices in list L of occurrences of X */  
get_indices(_N, _X, [], []) :-
   !.
	
get_indices(N, X, [X|L], [N|LIndices]) :-
   !,
   N1 is N+1,
   get_indices(N1, X, L, LIndices).
   
get_indices(N, X, [_|L], LIndices) :-
   N1 is N+1,
   get_indices(N1, X, L, LIndices).
   
	/* get all values of arguments placed in positions given by LIndices */  
get_values([], _Mode, _Ex, []) :-
   !.
	
get_values([Indice|LIndices], Mode, Ex, LValues) :-
   findall(Value, (member(Mode, Ex), arg(Indice, Mode, Value)), LValues1),
   get_values(LIndices, Mode, Ex, LValues2),
   append(LValues1, LValues2, LValues).
     
write_atoms(_Stream, []) :-
   !.
   
write_atoms(Stream, [A|L]) :-   
  fwrite_point_ln(Stream, A),
  write_atoms(Stream, L).
  
     
/* ============================== END OF LISTING ============================ */   
   
