/* ========================================================================== */ 
/*                              AMBIGUITY GENERATOR FOR TILDE FORMAT	        */
/*             generate ambiguous examples from complete examples             */
/*                 by removing facts at random                                */
/*                 according to a i.i draw                                    */
/* ========================================================================== */


ambiguity :-
   file(F),
   proba(P),
   factor(M),
   exp(N),
   cae(F, P, M, N).


   /* create a file containing positive and negative incomplete examples in a tilde format */
cae(
                           File_ex,   % file containing intensional examples
                           File_out,  % name of the result file
                           Proba,     % probability of a fact to be discarded
                           M          % one create M times positive and
                                      % negative examples.  
                          ) :- 
                          
   use_module(library(random)), 

   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, '/randomize.pl', Random),
   use_module(Random),

   init_randomize,

   		% build name of the input file
   atom_concat(File_ex, '.int', File_ex_int),
   bk:consult(File_ex_int),
   
       % build name of the outpufile
   Proba_to_be_kept is 1.0 - Proba,
   
  
   atom_concat(File_out, '.kb', File_kb), 

   create_ambiguity(File_kb, Proba_to_be_kept, M).   
   
/* ========================= CREATING AMBIGUOUS EXAMPLES ==================== */  

create_ambiguity(FileName, Proba, M) :-

   open(FileName, write, Stream),
   reset_count(nb_examples),
   
   get_positive_examples(LPos),
   deal_with_test_examples(yes, LPos, Stream),
   
   get_negative_examples(LNeg),
   deal_with_test_examples(no, LNeg, Stream),
   
   length(LPos, N_pos),
   K_pos is N_pos * M,              % # of positive ambigous examples to create
   deal_with_examples(yes, K_pos, LPos, Proba, Stream),
   
   nl(Stream),
   nl(Stream),
   
   length(LNeg, N_neg),
   K_neg is N_neg * M,              % # of negative ambigous examples to create
   deal_with_examples(no, K_neg, LNeg, Proba, Stream),
   nl(Stream),
   
   close(Stream).
   
  
get_positive_examples(LPos) :-
   findall(L, bk:pos(_N, L), LPos).
   
get_negative_examples(LNeg) :-
   findall(L, bk:neg(_N,L), LNeg).   
   

	/* write test examples  */
deal_with_test_examples(_Signe, [], _Stream) :-
   !.

deal_with_test_examples(Signe, [Ex|Lex], Stream) :-
   write_example(test, Ex, Signe, Stream),
   deal_with_test_examples(Signe, Lex, Stream).


	/* draw an example Ex in Lex and discard some atoms of Ex accordin to
	   Proba */
deal_with_examples(Signe, K, Lex, Proba, Stream) :-
   length(Lex, N),
   n_times(K),
   draw_an_example(N, Lex, Ex),
   create_an_ambiguous_example(Ex, Proba, Ambiguous_ex),
   write(Ambiguous_ex),
   writeln(' ambiguous'),

   length(Ex, Ne),
   length(Ambiguous_ex, Na),
   write(Na),
   write('/'),
   write(Ne),
   nl,
   P is 1 - Na/Ne,
   writeln(P),
   nl,
   write_example(no_test, Ambiguous_ex, Signe, Stream),
   fail.
   
deal_with_examples(_Signe, _K, _Lex, _Proba, _Stream). 


	/* draw an example in Lex */
draw_an_example(N, Lex, Ex) :-
   randseq(1, N, [Index]),
   writeln(Index),
   nth1(Index, Lex, Ex),      % example is drawn
   write(Ex),
   writeln(' drawn'),
   nl.
   

/* write the current ambiguous example */ 
 
write_example(Test, Ex, Signe, Stream) :-
  get_count(nb_examples, N),
  fwrite_point_ln(Stream, begin(model(N))),
  fwrite_point_ln(Stream, arch(a,b,c,Signe)),
  write_testid(Test, Stream),
  write_atoms(Ex, Stream),
  fwrite_point_ln(Stream, end(model(N))),
  nl(Stream).
  
write_atoms([], _Stream) :-
   !.
   
write_atoms([A|L], Stream) :-   
  fwrite_point_ln(Stream, A),
  write_atoms(L, Stream).
  
write_testid(test, Stream) :-
   !,
   fwrite_point_ln(Stream, testid(1)).
   
write_testid(_, _Stream).   
  
     
    
/* ====================== SELECTING ATOMS TO BE DISCARDED =================== */

	/* create an ambiguous example from one complete example discarding
	   drawn literals */
	   
create_an_ambiguous_example([], _Proba, []) :-
   !.
   	   
create_an_ambiguous_example(
                            [Literal|Lliteral], 
                            Proba_to_be_kept, 
                            [Literal|Ambiguous_ex]
                           ) :-
                            
   random(P),    
   P =< Proba_to_be_kept,
   !,                                                     
  create_an_ambiguous_example(Lliteral, Proba_to_be_kept, Ambiguous_ex).                           
   
create_an_ambiguous_example(
                            [Literal|Lliteral], 
                            Proba_to_be_kept, 
                            Ambiguous_ex
                           ) :-  
   write(Literal),
   writeln(' discarded'),                        
   create_an_ambiguous_example(Lliteral, Proba_to_be_kept, Ambiguous_ex).                             


	/* create a set of ambiguous examples from one complete example */
make_ambiguity(LAtoms, Proba, LExamples) :-
   length(LAtoms, N),
   K is round(N*Proba),			% K is the number of atoms to discard
   check_K(N, K, K1),
   K2 is N-K1,
   combinations(LAtoms, K2, LExamples).
   
   
   /* check that K is at most N */   
check_K(N, K, K) :-
   K =< N,
   !.
   
check_K(N, _K, N).
 
   
discard_atoms(_Num, [], _LNums, []) :-
   !.
   
discard_atoms(
              Num,            % num of the current atom in the initial list 
              [_Atom|LAtoms], % list of atoms 
              LNums,          % nums of the atomes to be discarded  
              LAtoms_out      % remaining atoms
              ) :-
   select(Num, LNums, LNums1),
   !,
   Num1 is Num+1,
   discard_atoms(Num1, LAtoms, LNums1, LAtoms_out).
   
   
discard_atoms(
              Num,                % num of the current atom in the initial list 
              [Atom|LAtoms],      % list of atoms 
              LNums,              % nums of the atomes to be discarded  
              [Atom|LAtoms_out]   % remaining atoms
             ) :-
   Num1 is Num+1,
   discard_atoms(Num1, LAtoms, LNums, LAtoms_out).  
   
   
/* ============================== END OF LISTING ============================ */   
   
   
        
   


    
    
