﻿/*
 *                                                                            
 *  LEAR10 : learning first order rules from positive and negative ambiguous                
 *           ground clauses   
 * author    Dominique BOUTHINON (db@lipn.univ-paris13.fr)    
 *           LIPN Universite Paris 13 (France)
 * date     18/10/2012                                      
*/
 
:- module( 
          lear10,
          [
             init/0,
             learn_test/4,            % (File_tran, +File_test, + File_excel
                                      %  + Beam_width)
             n_train_test/4,          % (+File_train, + File_test, 
                                      %  +Nb files, +Beam width)
             learn/3,                 % (+File_ex,  +Beam_width, -Rules)
             train_test_validation/4, % (+File_ex, +File_test, 
                                      %  +Nb_folds, +Beam_width)
             cross_validation/4       % (+File_ex, + File_bkg, 
                                      %  +Nb_fold, +Beam_width)                                    
          ]
          ).

:- dynamic 
           faux/0,
           or/1,
           get_indentation/1,
           train:domains/3,
           train:target/3,
           test:domains/3,
           test:target/3,
           bk:mode/1,
           train:loaded_examples/0,
           test:loaded_examples/0,
           bk:loaded_background/0,
           train:examplesP/1,
           train:examplesN/1, 
           train:examples_graphs_pos/1,
           train:examples_graphs_neg/1,
           train:tabs/2,
           test:tabs/2,
           train:tab_pos/1,
           train:tab_neg/1,
           test:examplesP/1,
           test:examplesN/1,
           test:examples_graphs_pos/1,
           test:examples_graphs_neg/1,
           test:tab_pos/1,
           test:tab_neg/1,
           bk:target_relation/1,
           bk:types_instantiable/1,
           bk:types_not_instantiable/1,
           bk:types/1,
           bk:types_under_oi/1,
           bk:types_not_under_oi/1,
           no_seed/0,
           learn:first_step/0.

/* ========================================================================== */
/*                       CONSTANTES DU PROGRAMMES                             */
/* ========================================================================== */
	
modes([+]).     % modes associes aux arguments des predicats du langage

/* ========================================================================== */
/*                             RUN LEARNING                                   */
/* ========================================================================== */
        
learn( 
          File,     
          K,
          Rules                   % Learned concept.
      ) :-
        
        call_modules,
        writeln('loading examples...'),
        load_examples_file(File),
        load_background_file(File),

        init,
        learn(K, Rules),
        finish,
        erase_examples,
        writeln('end of learning.').
        
        
learn(K, Rules) :-
        first_message,
        get_examples(LPos, LTab_pos, LNeg, _LTab_neg),
        build_rules(LPos, LTab_pos, LNeg, K, Rules),
        !.


/* ========================================================================== */
/*                             INITIALIZATION AND FINISH                      */
/* ========================================================================== */
     
init :-
   get_types(LTypes),
   bk:assert(types(LTypes)),
   bk:types_instantiable(LTypes_instantiable),
   subtract(LTypes, LTypes_instantiable, LTypes_not_instantiable),
   bk:assert(types_not_instantiable(LTypes_not_instantiable)),
 
   build_list_types_under_oi,
   set_types_not_under_oi,
   build_list_types_not_under_oi,
                      
   findall(Atom, bk:mode(Atom), LAtoms), 
   build_atoms_types(LAtoms).
 

finish :-
   retractall(bk:pattern_types(_, _)),
   retractall(bk:sets_vars_under_oi(_)),
   retractall(bk:sets_vars_not_under_oi(_)),
   retractall(bk:valid_patterns_instantiable(_)),
   retractall(bk:types_instantiable(_)),
   retractall(bk:types_not_instantiable(_)),
   retractall(bk:types(_)),
   retractall(bk:types_under_oi(_)),
   retractall(bk:types_not_under_oi(_)).


call_modules :-
   getenv('LEAR_DIR', Lear),
   
   atom_concat(Lear, '/manage_examples2.pl', Load),
   use_module(Load),
   
   atom_concat(Lear, '/miscellaneous.pl', Misc),
   use_module(Misc),   

   atom_concat(Lear, '/domains.pl', Domains),
   use_module(Domains),
   
   atom_concat(Lear, '/best_conjunction6.pl', Best),
   use_module(Best),
   
   atom_concat(Lear, '/utilities_lear11.pl', Utils),
   use_module(Utils),
   
   atom_concat(Lear, '/list.pl', List),
   use_module(List),
   
   atom_concat(Lear, '/term.pl', Term),
   use_module(Term),
   
   atom_concat(Lear, '/assumptions2.pl', Assumptions),
   use_module(Assumptions),

   atom_concat(Lear, '/compatibility5.pl', Comp),
   use_module(Comp).
   
              
/* ========================================================================== */
/*                                  BUILD RULES                               */
/* ========================================================================== */    

build_rules(
            ENC,        % non covered positive ambiguous examples
            LTab_pos,   % descriptions of tables of non covered pos examples
            LNegA,      % negative ambiguous examples
            K,          % width of the beam
            Rules_found % learned concept
          ) :-  

   length(LNegA, Nb_neg),
   Max_errors is Nb_neg + 1,
   learn:retractall(max_errors(_)),
   learn:assert(max_errors(Max_errors)),

   writeln('initializing assumptions...'),      
   initialize_negative_assumptions(LNegA),

   get_top_rules(Top),
   find_rules(Top, ENC, LTab_pos, LNegA,  K, Rules_found),
   !,
   clear_negative_assumptions(LNegA),
   learn:retract(max_errors(_)).
  
  
build_rules(
            _ENC,      % non covered positive ambiguous examples,
            _LTab_pos,
            LNegA,     % negative ambiguous examples
            _K,        % width of the beam
            _Rules_found % learned concept
          ) :-
   learn:retract(no_seed),  % No positive example is a valid seed :
   !,                       % the variabilization of each possibility
                            % of each ambiguous example 'covers' each
                            % possibility of each negative example.
   writeln('************** NO VALID SEED *********************'),
   clear_negative_assumptions(LNegA),
   learn:retract(max_errors(_)),
   fail.                   


build_rules(
            ENC,      % non covered positive ambiguous examples
            LTab_pos,
            LNegA,    % negative ambiguous examples
            K,        % width of the beam
            Rules_found % learned concept
          ) :-
   !, 
   K2 is K * 2,                                 % enlarge width of the beam
   write('********* beam width has been enlarged to ***************'),
   writeln(K2),
   clear_negative_assumptions(LNegA),

   (K2 > 10
         -> 
            (writeln('Beam too width, fail to learn'), fail)
         ;   build_rules(ENC, LTab_pos, LNegA, K2, Rules_found)
   ).
    
get_top_rules([]).     % most general set of rules

   
	/* cherche l'ensemble de regles compatible avec les exemples */
find_rules(Rules, [], [], _, _K, Rules) :-
        !,                       % All positive ambiguous examples are covered,
                                 % no negative example is covered.                      
        nl,
        writeln('================== FINAL SET OF RULES ===================== '),
        display_rules(Rules),
        nl.
        
find_rules(
            Rules,    % current Rules
            ENC,      % non covered positive ambiguous examples
            LTab_pos, 
            LNegA,    % negative ambiguous examples
            K,        % width of the beam
            Rules_found % learned concept
        ) :-
        

        nl,
        println('___________________________________________________________'),
        println('Current rules'), 
        display_rules(Rules),
        nl,

        learn:verbosity(Verbose),
        (
          Verbose >= 1
             ->
           (
              length(ENC,N),
              nl,
              write('number of non covered positive examples : '),
              writeln(N)
           ) ;
           true
         ), 

        find_best_conjunction(ENC, LTab_pos, LNegA, K,
                              (Best, Guidance, Quality, Errors, PC), 
                              ENCA, LTab_posA),

        retractall(learn:id_seed(_)), 
        learn:delete_count(nb_tries),
        learn:delete_count(past_nb_errors),
          
        Quality > 0,
        nl,
        writeln(' +++++++  added rule ++++++++: '),

        length(LNegA, Nneg),
        length(ENCA, Npos),

        display_best_conjunction2((Best, Guidance, Quality, Errors, PC),
                                  Npos, Nneg),

        learn:recorded(
                       best_hypothesis,
                       (LNum_pos_covered, LNum_pos_compatible, 
                        _LNum_pos_not_compatible,
                        _LNum_neg_not_compatible, _Nb_neg_compatible)
                       ), 
	
        write('compatible positive examples  : '),
        writeln(LNum_pos_compatible),     
	   write('covered positive examples  : '),
        writeln(LNum_pos_covered),   

        Best = (_, Hyp, _),
        append(Rules, [rule((Best, Guidance, Quality, Errors, PC), Npos, Nneg)], 
               Rule1),

        update_negative_examples(Hyp, LNegA),
        
        check_compatible_positive_examples(ENCA, LTab_posA,
                                           LNum_pos_compatible, 
                                           ENC1, LTab_pos1),
        learn:recorded(best_hypothesis, _, Reference),
        learn:erase(Reference),

        find_rules(Rule1, ENC1, LTab_pos1, LNegA,  K, Rules_found).
   

find_rules(
            _Rules,    % current Rules
            _ENC,      % non covered positive ambiguous examples
            _LTab_pos,
            _LNegA,    % negative ambiguous examples
            _K,        % length of the beam
            _Rules_found % learned concept
           ) :-
        % \+ no_seed,
        nl,
        nl,
        println('!!!!!!!!! NO SOLUTION !!!!!!!!!!!!!'),
        fail.
 
 
 /* ---------------------- update compatible examples ----------------------  */

check_compatible_positive_examples([], [], _, [], []) :-
   !.
    
check_compatible_positive_examples(
                                    [(Indice, _Head_ex, _Body_ex)|LExp], 
                                    [_Tab|LTab],
                                    LNum_pos_compatible, 
                                    LExp1,
                                    LTab1
                                            ) :-
   member(Indice, LNum_pos_compatible),
   !,
   check_compatible_positive_examples(LExp, LTab,
                                      LNum_pos_compatible, 
                                      LExp1, LTab1).
                                                
check_compatible_positive_examples(
                                    [Ex|LExp],
                                    [Tab|LTab], 
                                    LNum_pos_compatible, 
                                    [Ex|LExp1],
                                    [Tab|LTab1]
                                   ) :-
   check_compatible_positive_examples(LExp, LTab,
                                      LNum_pos_compatible, 
                                      LExp1, LTab1).
 

/* ========================================================================== */
/*                             EXPERIMENTATIONS                               */
/* ========================================================================== */    


   /* learn from File_train and test from File_test */
learn_test(
            File_train,
            File_test,
            File_excel,
            K                  % beam width
          ):-

   call_modules, 

   load_examples_file(File_train),
   get_examples(LPos_train, LPos_train_tab, LNeg_train, _LNeg_train_tab),
   length(LPos_train, N_pos_train),
   length(LNeg_train, N_neg_train),
   
   load_background_file(File_train),
   init,
   
   load_test_file(File_test),
   get_examples_test(LPos_test, LPos_test_tab, LNeg_test, _LNeg_test_tab),
     
    statistics(cputime, Time1),
   
    set_learn_mode(train),
    build_rules(LPos_train, LPos_train_tab, LNeg_train, K, Rules),
    
    set_learn_mode(test),

    test(
         Rules, LPos_test, LPos_test_tab, LNeg_test, 
         N_pos_test, N_true_pos, N_false_pos,
         N_neg_test, N_true_neg, N_false_neg,
         Precision_pos, Recall_pos, Fmeasure_pos,
         Precision_neg, Recall_neg, Fmeasure_neg,
         Precision
       ),
   

    statistics(cputime, Time2),
    Duration is round(Time2-Time1),

    write_results(
                   File_train,
                   File_train,
                   File_test,
                   Duration,
                   K,
                   N_pos_train, N_neg_train,
                   N_pos_test, Precision_pos, Recall_pos, Fmeasure_pos,
                   N_neg_test, Precision_neg, Recall_neg, Fmeasure_neg,
                   Precision, Rules
                  ),

    write_excel_results(
                   File_excel,
                   N_true_pos, N_false_pos,
                   N_true_neg, N_false_neg
                  ),
                 
   finish.



  /* learn from File_train and test from File_test */
learn_test(
            File_train,
            File_test,
            File_excel,
            K                  % beam width
          ):-
nl,
nl,
writeln(' !!!!!!!!!!!!!!! CANNOT LEARN !!!!!!!!!!!!!!!!!!!! '),
write('train file		: '),
writeln(File_train),
write('test file 		: '),
writeln(File_test),
write('excel file 		: '),
writeln(File_excel),
write('beam 			: '),
writeln(K),
nl,
nl.




		/* deal with N File_train and 1 File_test, K is the beam width */
n_train_test(File_train, File_test, N, K) :-
      call_modules,
      learn:assert(train_test_validation),
      
      load_test_file(File_test),
      get_examples_test(LPos_test, LNeg_test),

      statistics(cputime, Time1),
     
      do_train_test_validations(1, N, File_train, LPos_test, LNeg_test, K,
                                LPrecision_pos, LRecall_pos, LFmeasure_pos,
                                LPrecision_neg, LRecall_neg, LFmeasure_neg,
                                LPrecision, Rules),
     
     statistics(cputime, Time2),
     D is round(Time2-Time1),
     
     stats(LPrecision_pos, LRecall_pos, LFmeasure_pos,
           LPrecision_neg, LRecall_neg, LFmeasure_neg,
           LPrecision,  
           Average_precision_pos, Deviation_precision_pos, 
           Average_recall_pos, Deviation_recall_pos,
           Average_fmeasure_pos, Deviation_fmeasure_pos,
           Average_precision_neg, Deviation_precision_neg, 
           Average_recall_neg, Deviation_recall_neg,
           Average_fmeasure_neg, Deviation_fmeasure_neg,
           Average_precision, Deviation_precision),
     
     display_results_file2(
                   File_train,
                   File_train,
                   File_test,
                   D,
                   N, K,
                   LPrecision_pos, LRecall_pos, LFmeasure_pos,
                   LPrecision_neg, LRecall_neg, LFmeasure_neg,
                   LPrecision, Rules, 
                   Average_precision_pos, Deviation_precision_pos, 
         		    Average_recall_pos, Deviation_recall_pos,
                   Average_fmeasure_pos, Deviation_fmeasure_pos,
                   Average_precision_neg, Deviation_precision_neg, 
                   Average_recall_neg, Deviation_recall_neg,
                   Average_fmeasure_neg, Deviation_fmeasure_neg,
                   Average_precision, Deviation_precision),  
                 
      format('end of ~d train test validations. ~n', N),

      display_duration(D),
      display_infos(D, Average_precision, Deviation_precision),
       
      erase_examples_test,
      learn:retract(train_test_validation),
      retract_learn_mode.

	/* deal with a current training file */
do_train_test_validations(Num, N, _File_train, _LPos_test, _LNeg_test, _K,
                         [], [],[], [], [], [], [], []) :-
   Num > N,
   !.

do_train_test_validations(
                          Num, 
                          N, 
                          File_train, 
                          LPos_test, 
                          LNeg_test, 
                          K,
                          [Precision_pos|LPrecision_pos],
                          [Recall_pos|LRecall_pos],
                          [F_measure_pos|LF_measure_pos],
                          [Precision_neg|LPrecision_neg],
                          [Recall_neg|LRecall_neg],
                          [F_measure_neg|LF_measure_neg],
                          [Precision|LPrecision], 
                          [Rules|LRules]
                          ) :-
                         
   atom_number(ANum, Num),
   atom_concat(File_train, ANum, File_train1),       
   
   load_background_file(File_train1),
   init,
                       
                              
   load_examples_file(File_train1),
   get_examples(LPos_train, LNeg_train),


   set_learn_mode(train),
   build_rules(LPos_train, LNeg_train, K, Rules),
       

   set_learn_mode(test),
   test(
         Rules, LPos_test, LNeg_test, 
         Precision_pos, Recall_pos, F_measure_pos,
         Precision_neg, Recall_neg, F_measure_neg,
         Precision
       ),

   erase_examples,
   erase_background,
   finish,
   
   Num1 is Num+1,
   do_train_test_validations(Num1, N, File_train, LPos_test, LNeg_test, K,
                             LPrecision_pos, LRecall_pos, LF_measure_pos,
                             LPrecision_neg, LRecall_neg, LF_measure_neg,
                             LPrecision, LRules).

/* -------------------------------------------------------------------------- */
   

   /* split File_train in N parts, learn from each part and test on File_test */
   /* When N is 1, just makes a learn and a test                              */
train_test_validation(
                        File_train,
                        File_test,
                        N,
                        K
                      ):-
   call_modules, 
   load_examples_file(File_train),
   get_examples(LPos, LNeg),
   
   load_background_file(File_train),
   init,
   
   load_test_file(File_test),
   get_examples_test(LPos_test, LNeg_test),
     
   length(LPos, N_pos),
   Length_pos is (N_pos // N),      % # examples in each positive train set
   length(LNeg, N_neg),
   Length_neg is (N_neg // N),      % # examples in each negative train set /
     
   
    
   statistics(cputime, Time1),
   
   train_test(1, N, K, 1, Length_pos, LPos, 1, Length_neg, LNeg,
              LPos_test, LNeg_test, LRules,
              LPrecision_pos, LRecall_pos, LFmeasure_pos,
              LPrecision_neg, LRecall_neg, LFmeasure_neg,
              LPrecision),

   
    statistics(cputime, Time2),
    D is round(Time2-Time1),

   stats(LPrecision_pos, LRecall_pos, LFmeasure_pos,
         LPrecision_neg, LRecall_neg, LFmeasure_neg,
         LPrecision,  
         Average_precision_pos, Deviation_precision_pos, 
         Average_recall_pos, Deviation_recall_pos,
         Average_fmeasure_pos, Deviation_fmeasure_pos,
         Average_precision_neg, Deviation_precision_neg, 
         Average_recall_neg, Deviation_recall_neg,
         Average_fmeasure_neg, Deviation_fmeasure_neg,
         Average_precision, Deviation_precision),
       
   display_results_file(
                   File_train,
                   File_train,
                   File_test,
                   D,
                   N_pos, N_neg, Length_pos, Length_neg, N, K,
                   LPrecision_pos, LRecall_pos, LFmeasure_pos,
                   LPrecision_neg, LRecall_neg, LFmeasure_neg,
                   LPrecision, LRules, 
                   Average_precision_pos, Deviation_precision_pos, 
         		    Average_recall_pos, Deviation_recall_pos,
                   Average_fmeasure_pos, Deviation_fmeasure_pos,
                   Average_precision_neg, Deviation_precision_neg, 
                   Average_recall_neg, Deviation_recall_neg,
                   Average_fmeasure_neg, Deviation_fmeasure_neg,
                   Average_precision, Deviation_precision),  
                 
   finish,
   !.
   

train_test(N, N_end, _K, _Deb_pos, _Length_pos, _LPos, _Deb_neg, _Length_neg, _LNeg,
           _LPos_test, _LNeg_test, [], 
           [], [], [], [], [], [], []) :-
    N > N_end,        
   !.
   
train_test(
           N, N_end, K, Deb_pos, Length_pos, LPos, Deb_neg, Length_neg, LNeg,
           LPos_test, LNeg_test, [Rules|LRules], 
           [Precision_pos|LPrecision_pos], [Recall_pos|LRecall_pos], [F_measure_pos|LF_measure_pos],
           [Precision_neg|LPrecision_neg], [Recall_neg|LRecall_neg], [F_measure_neg|LF_measure_neg],
           [Precision|LPrecision]
          ) :-
    nl,
    nl,
    writeln('============================================================== '),
    write('train set number '),
    writeln(N),
    nl,
    
    get_train(Deb_pos, Length_pos, LPos, LPos_train),
    get_train(Deb_neg, Length_neg, LNeg, LNeg_train),
    

    set_learn_mode(train),
    build_rules(LPos_train, LNeg_train, K, Rules),
    
    set_learn_mode(test),
    test(
         Rules, LPos_test, LNeg_test, 
         Precision_pos, Recall_pos, F_measure_pos,
         Precision_neg, Recall_neg, F_measure_neg,
         Precision
       ),

    N1 is N+1,
    Deb_pos1 is Deb_pos + Length_pos,
    Deb_neg1 is Deb_neg + Length_neg,
    
    train_test(N1, N_end, K, Deb_pos1, Length_pos, LPos, Deb_neg1, Length_neg, LNeg,
               LPos_test, LNeg_test, LRules, 
               LPrecision_pos, LRecall_pos, LF_measure_pos,
               LPrecision_neg, LRecall_neg, LF_measure_neg,
               LPrecision).

get_train(Deb, Length, L, Lout) :-
   End is Deb + Length - 1,
   select_deb_end(L, Deb, End, Lout, _).

/* ========================== CROSS VALIDATIONS ============================= */

cross_validation3(
                   File_train,
                   File_bkg,
                   File_name_test,    % contains non ambiguous examples
                                      % for the test phase
                   Nb_folds,
         
          K                  % Width of beam search
                  ) :-
        load_examples_file(File_train),
        load_background_file(File_bkg),
        init,
        load_test_file(File_name_test),
        cross_validation3(Nb_folds, K),
        finish,
        !.


 cross_validation3(Nb_folds, _) :-
        Nb_folds =< 1,
        !,
        write('Error : the number of folds is '),
        write(Nb_folds),
        writeln('. It mus be strictly greater than one !').

cross_validation3( _, K) :-
        K =< 0,
        !,
        write('Error : the width of the beam search is '),
        write(K),
        writeln('. It mus be strictly greater than zero !').


cross_validation3(
                   Nb_folds,  % number of validations
                   K          % beam width
                  ) :-
                  
        statistics(cputime, Time1),
        get_examples(LPosA, LNegA),
        length_sets(LPosA, LNegA,Nb_folds, Nb_pos_test, Nb_neg_test),

        get_examples_test(LPos_test, LNeg_test),
        length_sets(LPos_test, LNeg_test, Nb_folds, Nb_test_plus, Nb_test_neg),

        folds3(
               Nb_folds, 1, Nb_pos_test, Nb_neg_test, Nb_test_plus,
               Nb_test_neg, K,
               1, Nb_pos_test, 1, Nb_neg_test, 1, 1, LPrecisions, LRules
               ),

        stats(LPrecisions, Average_precision, Standard_deviation),
        display_results(LPrecisions, LRules,
                         Average_precision,
                         Standard_deviation),
        format('end of ~d cross-validations. ~n', Nb_folds),
        statistics(cputime, Time2),
        D is round(Time2-Time1), 
        display_duration(D),
        display_info(D, Average_precision, Standard_deviation),
        !.
     
        
 folds3(
         1, Fold_number, _Nb_pos_test, _Nb_neg_test, 
         _Nb_test_plus, _Nb_test_neg, K, 
         Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
         _Nb_pos_plus_deb, _Nb_pos_neg_deb,
         [Precision], [Rules]
        ) :-
        
         !,
         
        display_fold(Fold_number),
        build_train(
                     Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                     LEx_pos_train, LEx_neg_train 
                   ),
           
        set_learn_mode(train),
        build_rules(LEx_pos_train, LEx_neg_train, K, Rules),

        get_examples_test(LEx_pos_test, LEx_neg_test),
        set_learn_mode(test),
        test(Rules, LEx_pos_test, LEx_neg_test, Precision).

 folds3(
                Nb_folds_remaining, Fold_number, Nb_pos_test, Nb_neg_test,
                Nb_test_plus, Nb_test_neg, K,
                Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                Nb_pos_plus_deb, Nb_pos_neg_deb,
                [Precision|LPrecisions], [Rules|LRules]
         ) :-
        
        nl,
        display_fold(Fold_number),

        build_train(
                     Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                     LEx_pos_train, LEx_neg_train
                   ),
              
        set_learn_mode(train),  
        build_rules(LEx_pos_train, LEx_neg_train, K, Rules),
        
        get_examples_test(LEx_pos_test, LEx_neg_test),
        set_learn_mode(test),
        test(Rules, LEx_pos_test, LEx_neg_test, Precision),

        Nb_folds_remaining1 is Nb_folds_remaining - 1,
        Fold_number1 is Fold_number + 1,
        Deb_pos_test1 is End_pos_test + 1,
        End_pos_test1 is Deb_pos_test1 + Nb_pos_test - 1,
        Deb_neg_test1 is End_neg_test + 1,
        End_neg_test1 is Deb_neg_test1 + Nb_neg_test - 1,
        Nb_pos_plus_deb1 is Nb_pos_plus_deb + Nb_test_plus,
        Nb_pos_neg_deb1 is Nb_pos_neg_deb + Nb_test_neg,

                                                               % call next fold
        folds3(
                 Nb_folds_remaining1, Fold_number1, Nb_pos_test, Nb_neg_test,
                 Nb_test_plus, Nb_test_neg,
                 K, Deb_pos_test1, End_pos_test1, Deb_neg_test1, End_neg_test1,
                 Nb_pos_plus_deb1, Nb_pos_neg_deb1, LPrecisions, LRules
               ).


cross_validation2(
                  File_train,
                  File_bkg,
                  File_name_test,    % contains non ambiguous examples
                                                      % for the test phase
                  Nb_folds,
                  K                         % Width of beam search
                  ) :-
        load_examples_file(File_train),
        load_background_file(File_bkg),
        init,
        load_test_file(File_name_test),
        cross_validation2(Nb_folds, K),
        finish.


cross_validation2(Nb_folds, _) :-
        Nb_folds =< 1,
        !,
        write('Error : the number of folds is '),
        write(Nb_folds),
        writeln('. It mus be strictly greater than one !').

cross_validation2( _, K) :-
        K =< 0,
        !,
        write('Error : the width of the beam search is '),
        write(K),
        writeln('. It mus be strictly greater than zero !').


cross_validation2(
                  Nb_folds,      % number of validations
                  K              % beam width
                 ) :-
        get_examples(LPosA, LNegA),
        length_sets(LPosA, LNegA,Nb_folds, Nb_pos_test, Nb_neg_test),

        get_examples_test(LPos_test, LNeg_test),
        length_sets(LPos_test, LNeg_test,Nb_folds, Nb_test_plus, Nb_test_neg),

        folds2(
               Nb_folds, 1, Nb_pos_test, Nb_neg_test, Nb_test_plus, Nb_test_neg,
               K,1, Nb_pos_test, 1, Nb_neg_test, 1, 1, LPrecisions, LRules),

        stats(LPrecisions, Average_precision, Sqrt_variance_precision),
        display_results(LPrecisions, LRules,
                         Average_precision,
                                         Sqrt_variance_precision),
        format('end of ~d cross-validations. ~n', Nb_folds).


        folds2(
                1, Fold_number, _Nb_pos_test, _Nb_neg_test,
                Nb_test_plus, Nb_test_neg, K,
                Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                Nb_pos_plus_deb, Nb_pos_neg_deb,
                [Precision], [Rules]
         ) :-
         !,
        display_fold(Fold_number),
        build_train(
                      Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                      LEx_pos_train, LEx_neg_train
                    ),
        build_test(
                   Nb_test_plus, Nb_test_neg, Nb_pos_plus_deb, Nb_pos_neg_deb,
                   LEx_pos_test, LEx_neg_test
                           ),

        set_learn_mode(train),
        build_rules(LEx_pos_train, LEx_neg_train, K, Rules),

        set_learn_mode(test),
        test(Rules, LEx_pos_test, LEx_neg_test, Precision).


folds2(
                Nb_folds_remaining, Fold_number, Nb_pos_test, Nb_neg_test,
                Nb_test_plus, Nb_test_neg, K,
                Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                Nb_pos_plus_deb, Nb_pos_neg_deb,
                [Precision|LPrecisions], [Rules|LRules]
         ) :-
        display_fold(Fold_number),

        build_train(
                     Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                     LEx_pos_train, LEx_neg_train
                    ),

        build_test(
                    Nb_test_plus, Nb_test_neg, Nb_pos_plus_deb, Nb_pos_neg_deb,
                    LEx_pos_test, LEx_neg_test
                  ),

        set_learn_mode(train),
        build_rules(LEx_pos_train, LEx_neg_train, K, Rules),
        
        set_learn_mode(test),
        test(Rules, LEx_pos_test, LEx_neg_test, Precision),

        Nb_folds_remaining1 is Nb_folds_remaining - 1,
        Fold_number1 is Fold_number + 1,
        Deb_pos_test1 is End_pos_test + 1,
        End_pos_test1 is Deb_pos_test1 + Nb_pos_test - 1,
        Deb_neg_test1 is End_neg_test + 1,
        End_neg_test1 is Deb_neg_test1 + Nb_neg_test - 1,
        Nb_pos_plus_deb1 is Nb_pos_plus_deb + Nb_test_plus,
        Nb_pos_neg_deb1 is Nb_pos_neg_deb + Nb_test_neg,

                 % call next fold
        folds2(
                Nb_folds_remaining1, Fold_number1, Nb_pos_test, Nb_neg_test,
                Nb_test_plus, Nb_test_neg,
                K, Deb_pos_test1, End_pos_test1, Deb_neg_test1, End_neg_test1,
                Nb_pos_plus_deb1, Nb_pos_neg_deb1, LPrecisions, LRules
              ).


 build_train(
             Deb_pos_test, End_pos_test1, Deb_neg_test, End_neg_test1,
             LEx_pos_train, LEx_neg_train
            ) :-
        get_examples(LEx_pos, LEx_neg),
        length(LEx_pos, Npos),
        length(LEx_neg, Nneg),

        End_pos_test is min(Npos, End_pos_test1),
        End_neg_test is min(Nneg, End_neg_test1),

        select_deb_end(
                        LEx_pos, Deb_pos_test, End_pos_test,
                        _LEx_pos_test, LEx_pos_train
                                   ),
        select_deb_end(
                        LEx_neg, Deb_neg_test, End_neg_test,
                                        _LEx_neg_test, LEx_neg_train
                                   ).

build_test(
            Nb_test_plus, Nb_test_neg, Nb_pos_plus_deb, Nb_pos_neg_deb,
            LEx_pos_test, LEx_neg_test
           ) :-

        get_examples_test(LEx_pos, LEx_neg),
        length(LEx_pos, Npos),
        length(LEx_neg, Nneg),

        Nb_pos_plus_end is min(Npos, Nb_pos_plus_deb + Nb_test_plus - 1),
        Nb_pos_neg_end  is min(Nneg, Nb_pos_neg_deb  + Nb_test_neg - 1),

        select_deb_end(
                        LEx_pos, Nb_pos_plus_deb, Nb_pos_plus_end,
                        LEx_pos_test, _LEx_pos_train
                      ),
        select_deb_end(
                        LEx_neg, Nb_pos_neg_deb, Nb_pos_neg_end,
                                        LEx_neg_test, _LEx_neg_train
                      ).

% =============================================================================

cross_validation(
                 File_train,
                 File_bkg,
                 Nb_folds,
                 K                         % Width of beam search
                ) :-
        load_examples_file(File_train),
        load_background_file(File_bkg),
        init,
        cross_validation(Nb_folds, K),
        finish,
        !.


cross_validation(Nb_folds, _) :-
        Nb_folds =< 1,
        !,
        write('Error : the number of folds is '),
        write(Nb_folds),
        writeln('. It mus be strictly greater than one !').

cross_validation( _, K) :-
        K =< 0,
        !,
        write('Error : the width of the beam search is '),
        write(K),
        writeln('. It mus be strictly greater than zero !').


cross_validation(
                  Nb_folds,    % number of validations
                  K            % beam width
                ) :-
        get_examples(LPosA, LNegA),
        length_sets(LPosA, LNegA, Nb_folds, Nb_pos_test, Nb_neg_test),
        folds(Nb_folds, 1, Nb_pos_test, Nb_neg_test, K, 1, Nb_pos_test,
              1, Nb_neg_test, LPrecisions, LRules),
        stats(LPrecisions, Average_precision, Sqrt_variance_precision),
        display_results(LPrecisions, LRules,
                         Average_precision,
                                         Sqrt_variance_precision),
        format('end of ~d cross-validations. ~n', Nb_folds),
        !.


length_sets(LPosA, LNegA, Nb_folds, Nb_pos_test, Nb_neg_test) :-
        length(LPosA, Nb_pos),
        length(LNegA, Nb_neg),
        compute_length(Nb_folds, Nb_pos, Nb_pos_test),
        compute_length(Nb_folds, Nb_neg, Nb_neg_test).


compute_length(Nb_folds, Nb, _Nb_test) :-
        Nb_folds > Nb,
        !,
        format(
             'error : two many folds (~d) for the number of examples (~2d)~n',
             [Nb_folds, Nb]
              ),
        fail.

compute_length(Nb_folds, Nb, Nb_test) :-
        Nb_test1 is (Nb // Nb_folds),     
        /* / */
        Rem is Nb mod Nb_folds,
        (
          Rem < 0.5
                -> Nb_test is Nb_test1
                ;  Nb_test is Nb_test1 + 1
        ).

folds(
         1, Fold_number, _Nb_pos_test, _Nb_neg_test, K,
         Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
         [Precision], [Rules]
      ) :-
         !,
        display_fold(Fold_number),

        build_train_test(
                      Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                      LEx_pos_train, LEx_neg_train, LEx_pos_test, LEx_neg_test
                        ),

        set_learn_mode(train),
        build_rules(LEx_pos_train, LEx_neg_train, K, Rules),

        set_learn_mode(test),
        test(Rules, LEx_pos_test, LEx_neg_test, Precision).


folds(
       Nb_folds_remaining, Fold_number, Nb_pos_test, Nb_neg_test, K,
       Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
       [Precision|LPrecisions], [Rules|LRules]
      ) :-
        display_fold(Fold_number),
        
        
        build_train_test(
                     Deb_pos_test, End_pos_test, Deb_neg_test, End_neg_test,
                     LEx_pos_train, LEx_neg_train, LEx_pos_test, LEx_neg_test
                        ),
        
        set_learn_mode(train),
        build_rules(LEx_pos_train, LEx_neg_train, K, Rules),
        
        set_learn_mode(test),
        test(Rules, LEx_pos_test, LEx_neg_test, Precision),
         
           % prepare next fold
        Nb_folds_remaining1 is Nb_folds_remaining - 1,
        Fold_number1 is Fold_number + 1,
        Deb_pos_test1 is End_pos_test + 1,
        End_pos_test1 is Deb_pos_test1 + Nb_pos_test - 1,
        Deb_neg_test1 is End_neg_test + 1,
        End_neg_test1 is Deb_neg_test1 + Nb_neg_test - 1,

                       % call next fold
        folds(
               Nb_folds_remaining1, Fold_number1, Nb_pos_test, Nb_neg_test, K,
               Deb_pos_test1, End_pos_test1, Deb_neg_test1, End_neg_test1,
               LPrecisions, LRules
              ).


build_train_test(
                 Deb_pos_test, End_pos_test1, Deb_neg_test, End_neg_test1,
                 LEx_pos_train, LEx_neg_train, LEx_pos_test, LEx_neg_test
                ) :-

        get_examples(LEx_pos, LEx_neg),
        length(LEx_pos, Npos),
        length(LEx_neg, Nneg),

        End_pos_test is min(Npos, End_pos_test1),
        End_neg_test is min(Nneg, End_neg_test1),

        select_deb_end(
                        LEx_pos, Deb_pos_test, End_pos_test,
                        LEx_pos_test, LEx_pos_train
                      ),
        select_deb_end(
                        LEx_neg, Deb_neg_test, End_neg_test,
                        LEx_neg_test, LEx_neg_train
                       ).


/* =============== TEST A SET OF RULES AGAINST EXAMPLES ===================== */

test(
      Rules, LPos_graph, LPos_tab, LNeg, 
      N_pos, N_true_pos, N_false_pos,
      N_neg, N_true_neg, N_false_neg,
      Precision_pos, Recall_pos, F_measure_pos,
      Precision_neg, Recall_neg, F_measure_neg,
      Precision
     ) :-
        
        initialize_negative_assumptions(LNeg),
      
        numbers_pos_well_classed(Rules, LPos_graph, LPos_tab, N_true_pos),
        numbers_neg_well_classed(Rules, LNeg, N_true_neg),
        
        length(LPos_graph, N_pos),
        length(LNeg, N_neg),
        
       
        N_false_pos is N_neg - N_true_neg,
        N_false_neg is N_pos - N_true_pos,
       
        N_attributed_pos is N_true_pos + N_false_pos,
        N_attributed_neg is N_true_neg + N_false_neg,
        
        compute_precision(N_true_pos, N_attributed_pos, Precision_pos),
        Recall_pos    is (100.0 * N_true_pos) / N_pos,
       
        compute_fmeasure(Precision_pos, Recall_pos, F_measure_pos),
        
        compute_precision(N_true_neg, N_attributed_neg, Precision_neg),
        Recall_neg    is (100.0 * N_true_neg) / N_neg,
       
        compute_fmeasure(Precision_neg, Recall_neg, F_measure_neg),
         
        Precision is 100.0 * (N_true_pos + N_true_neg) / (N_pos + N_neg),
        
        clear_negative_assumptions(LNeg).
        

   /* compute precision, return -1 whether it is not defined */
compute_precision(0, 0, -1.0) :-        % N_attributed = 0 (so N_true = 0)
   !.
           
compute_precision(N_true, N_attributed, Precision) :-
   Precision is (100.0 * N_true) / N_attributed.
   

   /* compute fmeasure, return -1 whether it is not defined */
compute_fmeasure(-1.0, _Recall, -1.0) :-
   !.                                       % precision not defined
   
compute_fmeasure(0.0, 0.0, -1.0) :-
   !.                                       % precision is null
	        
compute_fmeasure(Precision, Recall, Fmeasure) :-
   Fmeasure is (2.0 * Precision * Recall) / (100.0 * (Precision + Recall)).
   
   
/* ======================= count well classed examples ====================== */
       
 
/* 
   ATTENTION : NORMALEMENT ON DEVRAIT NE PAS TENIR COMPTE DE L'ETIQUETTE
   DE CHAQUE EXEMPLE e TESTE ET FAIRE :
   a) tester comp+ sur e, resultat r+
   b) test comp- sur e,   resultat r-

   si e est positif :
       si r+ vrai et r- faux => ok,
       si r+ faux et r- vrai => nok
       sinon (seul cas possible : r+ vrai et r- vrai) =>  ?

   si e est negatif :
       si r+ vrai et r- faux => nok,
       si r+ faux et r- vrai => ok
       sinon (seul cas possible : r+ vrai et r- vrai) =>  ?

   QUAND UN EXEMPLE EST COMPLET (une seule possibilite) :
     r+ vrai <=> r- faux et r+ faux <=> r- vrai. Donc :

     on peut se contenter de tester comp+, 

   ATTENTION !!!
   dans tous les cas de figure :
   comp+ se teste sur les possibilites max, et comp- sur les min.
   NE JAMAIS FAIRE COMP- SUR LES MAX ET COMP+ SUR LES MIN
 */


numbers_pos_well_classed(_Rules, [], [], 0) :-
        !.

numbers_pos_well_classed(
                         Rules, 
                         [Graph|LPos_graph],
                         [Tab|LPos_tab], 
                         Nb_pos_well_classed
                        ) :-
        compatible_pos_rules(Rules, Graph, Tab),
        !,
        numbers_pos_well_classed(Rules, LPos_graph, LPos_tab,
                                 Nb_pos_well_classed1),
        Nb_pos_well_classed is Nb_pos_well_classed1 + 1.

numbers_pos_well_classed(
                         Rules, 
                         [_Graph|LPos_graph], 
                         [_Tab|LPos_tab],
                         Nb_pos_well_classed
                        ) :-
        numbers_pos_well_classed(Rules, LPos_graph, LPos_tab,
                                 Nb_pos_well_classed).


numbers_neg_well_classed(_Rules, [], 0) :-
        !.

numbers_neg_well_classed(Rules, [(Id, Head_neg, _Body_neg)|LEx_neg_test], 
                                 Nb_neg_well_classed) :-

        get_negative_assumptions(Id, LAssumptions),  
        compatible_neg_rules(Rules, Id, Head_neg, LAssumptions),
        !,
        numbers_neg_well_classed(Rules, LEx_neg_test, Nb_neg_well_classed1),
        Nb_neg_well_classed is Nb_neg_well_classed1 + 1.

numbers_neg_well_classed(Rules, [_Ex|LEx_neg_test], Nb_neg_well_classed) :-
        numbers_neg_well_classed(Rules, LEx_neg_test, Nb_neg_well_classed).


/* ================ COMPUTE STATISTICS ON THE LEARNT CONCEPT ================ */  

stats( LPrecision_pos, LRecall_pos, LFmeasure_pos,
       LPrecision_neg, LRecall_neg, LFmeasure_neg,
       LPrecision, 
       Average_precision_pos, Deviation_precision_pos, 
       Average_recall_pos, Deviation_recall_pos,
       Average_fmeasure_pos, Deviation_fmeasure_pos,
       Average_precision_neg, Deviation_precision_neg, 
       Average_recall_neg, Deviation_recall_neg,
       Average_fmeasure_neg, Deviation_fmeasure_neg,
       Average_precision, Deviation_precision
       ) :-
       
       compute_stats(LPrecision_pos, Average_precision_pos, Deviation_precision_pos),
       compute_stats(LRecall_pos, Average_recall_pos, Deviation_recall_pos),
       compute_stats(LFmeasure_pos, Average_fmeasure_pos, Deviation_fmeasure_pos),
       
       compute_stats(LPrecision_neg, Average_precision_neg, Deviation_precision_neg),
       compute_stats(LRecall_neg, Average_recall_neg, Deviation_recall_neg),
       compute_stats(LFmeasure_neg, Average_fmeasure_neg, Deviation_fmeasure_neg),
       
       compute_stats(LPrecision, Average_precision, Deviation_precision).
       
  
compute_stats(LData, Average, Deviation) :- 
        delete(LData, -1.0, LData1),         % discard non relevant values 
        compute_stats2(LData1, Average, Deviation).
        
compute_stats2([], -1, -1) :-
   !.

compute_stats2(LData, Average, Deviation) :-   
        sumlist(LData, Sum),
        sum_square_list(LData, Sum2),
        length(LData, N),
        Average is Sum/N,
        Variance1 is Sum2/N - Average*Average,
        check_zero(Variance1, Variance),
        Deviation is sqrt(Variance).


sum_square_list([], 0) :-
        !.

sum_square_list([X|L], Sum2) :-
        sum_square_list(L, Sum2A),
        Sum2 is X*X + Sum2A.

  
/* a very small float number is considered as 0.0 */
check_zero(Float, 0.0) :-
 abs(Float) < 1.0e-8,
 !.
 
check_zero(Float, Float). 

/* ========================================================================== */
/*                              BIAS DISCARDING FACTS                         */ 
/* ========================================================================== */

discard_not_linked_facts(LEx, LEx) :-
   learn:level_discard(0),
   !.

discard_not_linked_facts(LEx, LEx_out) :-
   learn:level_discard(Na),
   atom_number(Na, N),
   discard_facts(LEx, N, LEx_out).

	/* discard facts that are not linked to (at level at most N)
      the arguments of the target predicate */

discard_facts([], _N, []) :-
   !.

discard_facts(
		        [(Id, [[[Target]]|Ex])|LEx],     % examples
              N,                               % max level of link
              [(Id, [[[Target]]|Ex1])|LEx1]    % modified examples
              ) :-
   discard_facts_one_example(Ex, Target, N, ExA),
   simplify_tables(ExA, Ex1),
   discard_facts(LEx, N, LEx1).


	/* discard facts not sufficiently linked of the current examples */

discard_facts_one_example([], _Target, _N, []) :-
   !.
  
discard_facts_one_example([Tab|LTab], Target, N, [Tab1|LTab1]) :-
   discard_facts_one_table(Tab, Target, N, TabA),
   keep_maximal_lists(TabA, Tab1),
   discard_facts_one_example(LTab, Target, N, LTab1).

	
	/* discard non well linked facts of all possibilities of the table Tab */
discard_facts_one_table([], _Target, _N, []) :-
   !.

discard_facts_one_table([Possibility|LPossibility], Target, N, 
                        [Possibility1|LPossibility1]) :-
   discard_facts_one_possibility(Possibility, [Target], [Target], 
                                 1, N, [Target|Possibility1]),
   discard_facts_one_table(LPossibility, Target, N, LPossibility1).


	/* discard of LAtoms_to_link atoms that are at distance more than N 
      of Target, which is the first element of LAll_atoms_linked */
discard_facts_one_possibility(
						_LAtoms_to_link,
						_LAtoms_just_linked,
						LAll_atoms_linked, 
						K,                 % current distance to deal with
						N,                 % maximal distance to target
                              LAll_atoms_linked
                              ) :-
   K > N,
   !.

discard_facts_one_possibility(
										LAtoms_to_link,
										LAtoms_just_linked,
										LAll_atoms_linked, 
										K,                 % current distance to target
										N,                 % maximal distance to target
                              LRemaining_atoms   % kepted atoms
                              ) :-
   linked_atoms(LAtoms_to_link, LAtoms_just_linked, 
                LAtoms_linked, LAtoms_not_linked),
   !,
   append(LAll_atoms_linked, LAtoms_linked, LAll_atoms_linked1),
   K1 is K+1,
   discard_facts_one_possibility(LAtoms_not_linked, LAtoms_linked, 
                                 LAll_atoms_linked1, K1, N, LRemaining_atoms).


	/* keep those atoms of LAtoms_to_link that share an argument with an atom
      of LAtoms_just_linked */
linked_atoms([], _LAtoms_just_linked, [], []) :-
   !.

linked_atoms([Atom|LAtoms_to_link], LAtoms_just_linked, 
             [Atom|LAtoms_linked], LAtoms_not_linked) :-
   linked_with_an_atom(Atom, LAtoms_just_linked),
   !,
   linked_atoms(LAtoms_to_link, LAtoms_just_linked, 
                LAtoms_linked, LAtoms_not_linked).

linked_atoms([Atom|LAtoms_to_link], LAtoms_just_linked, 
              LAtoms_linked, [Atom|LAtoms_not_linked]) :-
   linked_atoms(LAtoms_to_link, LAtoms_just_linked, 
                LAtoms_linked, LAtoms_not_linked).


	/* check wheter Atom share an argument with an atom of LAtoms */
linked_with_an_atom(_Atom, []) :-
   !,
   fail.

linked_with_an_atom(Atom, [Atom1|_LAtoms]) :-
    linked(Atom, Atom1),
     !.

linked_with_an_atom(Atom, [_Atom1|LAtoms]) :-
    linked_with_an_atom(Atom, LAtoms).


	/* check wheter Atom1 and Atom2 have at least one similar argument */
linked(Atom1, Atom2) :-
   Atom1 =..[_|LArg1],
   Atom2 =..[_|LArg2],
   have_similar_element(LArg1, LArg2, strictly).
       
/* ========================== FIN DU LISTING ================================ */
