1:- module( pepl, [
    2                   fam/1,
    3                   sload_pe/1,
    4                   sload_pe/2,
    5                   ssave/1,
    6                   switch_dbg/1,  % should we be using debug/1 ?
    7                   dbg_pepl/1,
    8                   pepl_citation/2,
    9                   pepl_version/2,
   10                   sls/0,
   11                   scall/1,
   12                   scall/6,
   13                   % all_path/2,
   14                   op( 600, xfy, :: )
   15                 ] ).   16
   17% employs requires...
   18
   19% :- ensure_loaded( '../src/all_path' ).
   20:- ensure_loaded( '../src/init_lib' ).   21:- ensure_loaded( '../src/estim'     ).         % /4.
   22:- ensure_loaded( '../src/myoption'  ).           
   23:- ensure_loaded( '../src/sload_pe'  ).           
   24:- ensure_loaded( '../src/set_prior' ).           
   25:- ensure_loaded( '../src/slp_file_location' ).        % /2.
   26
   27:- ensure_loaded( library(datafile_to_frequencies) ).  % /4.
   28:- ensure_loaded( library(lists) ).                    % append/3.
   29:- ensure_loaded( library(mold_vars_list) ).           % /2.
   30:- ensure_loaded( library(fam_setrand) ).              % /2.
   31:- ensure_loaded( library(pepl_messages) ).            % message/3.
   32
   33:- dynamic( dbg_flag/1 ).   34
   35:- license( mit ).

An implementation of the FAM algorithm.

Pepl is an implemention of the failure adjusted (FAM) algorithm which does parameter estimation (PE) of the probability labels of stochastic logic programs (SLPs).

See documentation fam/1 for details on how to run parameter estimation on SLPs.

Example stochastic programs are in directory slp and example run scripts are in examples.

Licence

This software is distributed under the MIT licence.

Installation and testing ...

Pepl runs on current versions of SWI (7) and Yap (6.3).

... on SWI

pack_install(pepl).
[library(pepl)].
[pack('pepl/examples/main')].
main.

... on Yap

Download latest sources from http://stoics.org.uk/~nicos/sware/pepl or https://github.com/nicos-angelopoulos/pepl

gunzip pepl-*tgz
tar xf pepl-*tar
cd pepl-*
cd examples
yap
[main].
main.

Package information

author
- Nicos Angelopoulos
version
- 2.2, 2022/1/2
- 2.1, 2017/2/25
- 2.0.6, 2014/01/28
See also
- the user guide at pack('pepl/doc/pepl-user_guide.pdf').
- James Cussens. Parameter estimation in stochastic logic programs. Machine Learning, 44(3):245-271, 2001. ftp://ftp.cs.york.ac.uk/pub/aig/Papers/james.cussens/jcslpmlj.pdf
- Nicos Angelopoulos, Notes on the implementation of FAM, 3rd Probabilistic Logic Programming workshop (a ILP 2016 workshop), 03/09/2016, http://ceur-ws.org/Vol-1661/paper-05.pdf
- pepl website http://stoics.org.uk/~nicos/sware/pepl */
license
- This software is distributed under the MIT licence
   91% :- switch_dbg( off ).
   92% :- switch_dbg( on ).
 fam(Opts)
Run the failure adjusted maximisation (FAM) parameter estimation algorithm.

For SLP source file jc_ml_S1.slp

0.5:: s(X,p) :- p(X), p(X).
0.5:: s(X,q) :- q(X).
0.5:: p(a).
0.5:: p(b).
0.5:: q(a).
0.5:: q(b).

and data file jc_ml_S1_data.pl

frequencies([s(a,p)-4,s(a,q)-3,s(b,p)-2,s(b,q)-3]).

the call succeeds with the learned PPs

?- fam( [goal(s(_A,_B)),slp(jc_ml_S1),datafile('jc_ml_S1_data.pl'),final_pps(PPs)] ).
PPs = [0.6602,0.3398,0.5858,0.4142,0.5,0.5]

Options

count(CountMeth=exact)
CountMeth in {exact, store, sample};
times(Tms=1000)
only relevant with CountMeth=sample
termin(TermList)
currently TermList knows about the following terms
interactive
ask user if another iteration should be run
iter(I)
I is the number of iterations
prm_e(E)
parameter difference between iteration, that renders termination due to convergence of all parameters, between two iterations
ll_e(L)
likelihood convergence limit;
goal(Goal)
the top goal, defaults to an all vars version of data;
pregoal(PreGoal)
a goal that called only once, before experiments are run. The intuition is that PreGoal will partially instantiate Goal.
data(Data)
the data to use, overrides datafile/1. Data should be a list of Yield-Times pairs. (All Yields of Goal should be included in Data, even if that means some get Times = 0.)
prior(Prior=none)
the distribution to replace the probability labels with. By default prior is used, so input parameters are used as given in Slp source file. System also knows about uniform and random. Any other distribution should come in Prolog source file named Prior.pl and define Prior/3 predicate. First argument is a list of ranges (Beg-End) for each stochastic predicate in source file. Second argument, is the list of actual probability labels in source file. Finally, third argument should be instantiated to the list of labels according to Prior.
datafile(DataFile=data.pl)
the data file to use, default is data.pl. DataFile should have either a number of atomic formulae or a single formula of the form : frequencies(Data).
complement(Complement=none)
one of : none (with PrbSc = PrbTrue, the default), success (with PrbSc = 1 − PrbF ail), or quotient (with PrbSc = PrbT rue/(PrbT rue + PrbF ail)).
setrand(SetRand=false)
sets random seeds. SetRand = true sets the seeds to some random triplet while the default value false, does not set them. Any other value for SetRand is taken to be of the form rand(S1,S2,S3) as expected by system predicate random of the supported prolog systems.
eps(Eps=0)
the depth Epsilon. Sets the probability limit under which Pepl considers a path as a failed one.
write_iterations(Wrt=all)
indicates which set of parameters to output. Values for Wrt are: all, last and none.
write_ll(Bool==true)
takes a boolean argument, idicating where loglikelihoods should be printed or not.
debug(Dbg=off)
should be set to on or off. If on, various information about intermediate calculations will be printed.
return(RetOpts=[])
a list of return options. The terms RetOpts contain variables. These will be instantiated to the appropriate values signified by the name of each corresponding term. Recognised are, initial pps/1 for the initial parameters, final pps for the final/learned parameters, termin/1 for the terminating reason, ll/1 for the last loglikelyhood calculated, iter/1 for the number of iterations performed, and seeds/1 for the seeds used.
keep_pl(KeepBool==false)
if true, the temporary Prolog file that contains the translated SLP, is not deleted.
exception(Handle=rerun)
identifies the action to be taken if an exception is raised while running Fam. rerun means that the same Fam call is executed repeatedly. Any other value for Handle will cause execution to abort after printing the exception raised.

*/

  208fam( Opts ) :-
  209     option_sel( setrand, Opts, false, RndOpts, Rnd ),
  210     fam_setrand( Rnd, Seeds ),
  211     option_sel( return, RndOpts, [], _RetOpts, RetList ),
  212     ( memberchk(seeds(Seeds),RetList) -> true;true ),
  213     sload_in_fam_options( RndOpts, SldOpts ),
  214     sel_option_actions( slp(Slp), RndOpts, sload_pe(Slp,SldOpts), true ),
  215     sel_option_actions( datafile(Df), RndOpts, true, there_exists_mem_slp_with_datafile(Df) ),
  216     sel_option_actions( write_iterations(WchWhr), RndOpts, true, (WchWhr=all/user_output) ),
  217   ( WchWhr = Wch/Whr -> true; Wch = WchWhr, Whr = user_output ),
  218     bb_put( fam_write_parameters, Wch/Whr ),
  219     sel_option_actions( write_ll(Wll), RndOpts, true, Wll=true ),
  220     bb_put( fam_write_ll, Wll ),
  221     sel_option_actions( debug(Dbg), RndOpts, switch_dbg(Dbg), true ),
  222          % this is a peek:, the real one comes after Data is instantiated.
  223     ( memberchk( goal(Goal), RndOpts ) -> true; true),
  224     sel_option_actions( data(Data), RndOpts, true, 
  225          ( slp_data_file_location( Df, AbsDF ),
  226            datafile_to_frequencies( AbsDF, Goal, Data )
  227            ) ),
  228     check_data( Data ),
  229     FrqC = frequencies_to_top_goal( Data, Goal ),
  230     sel_option_actions( goal(Goal), RndOpts, true, FrqC ),
  231     sel_option_actions( pregoal(PrG), RndOpts, call(PrG), true ),
  232     sel_option_actions( prior(Prior), RndOpts, true, Prior=none ),
  233     set_prior( Prior ),
  234     sel_option_actions( termin(_Termin), RndOpts, PadOpts = RndOpts, PadOpts = [termin([interactive])|Opts] ),
  235     sel_option_actions( count(_Cnt), PadOpts, EsmOpts=PadOpts, EsmOpts = [count(exact)|PadOpts] ),
  236     % setrand/1 is also recognised (in estim/4). 
  237     % either false, true or rand(R1,R2,R3).
  238     % sel_option_actions( setrand(_Cnt), EsmOpts, EsmOpts=FinOpts, EsmOpts = [(exact)|PadOpts] ),
  239     % estim( Goal, Data, EsmOpts, _Params ).
  240     sel_option_actions( exception(Handle), RndOpts, true, Handle=abort ),
  241     % sel_option_actions( exception(Handle), RndOpts, true, Handle=rerun ),
  242     estim( Goal, Data, EsmOpts, _Params ).
  243     % Estim = estim( Goal, Data, EsmOpts, _Params ),
  244     % catch( Estim, All, fam_excp( All, Handle, Estim, Slp, Prior ) ).
  245
  246/*
  247fam_excp( Error, Handle, Estim, Slp, Prior ) :-
  248     write( user_error, 'The following exception was caught while trying to run FAM. Atttempting to rerun fam/1 with same arguments.' ), nl( user_error ),
  249     print_message( error, Error ),
  250     fam_excp1( Handle, Estim, Slp, Prior ).
  251
  252fam_excp1( rerun, Estim, Slp, Prior ) :-
  253     sload_pe( Slp ),
  254     set_prior( Prior ),
  255     catch( Estim, All, fam_excp(All,rerun,Estim,Slp,Prior) ).
  256fam_excp1( _Handle, _Estim, _Slp, _Prior ) :-
  257     abort.
  258*/
 pepl_citation(-Atom, -Bibterm)
This predicate succeeds once for each publication related to this library. Atom is the atom representation suitable for printing while Bibterm is a bibtex(Type,Key,Pairs) term of the same publication. Produces all related publications on backtracking.
  267pepl_citation( Atom, bibtex(Type,Key,Pairs) ) :-
  268     Atom = 'Notes on the implementation of FAM\nNicos Angelopoulos\n3rd Probabilistic Logic Programming workshop (PLP 2016, a workshop of ILP 2016). September 2016, Imperial College London. Pages 46-58.',
  269    Type = inproceedings,
  270    Key  = 'AngelopoulosN+2016',
  271    Pairs = [
  272               author = 'Nicos Angelopoulos',
  273               title  = 'Notes on the implementation of FAM',
  274               booktitle = '3rd Probabilistic Logic Programming Workshop (collocated with ILP 2016)',
  275               year = 2016,
  276               month = 'September',
  277               address = 'Imperial College, London',
  278               publisher = 'CEUR',
  279               volume   = '1661',
  280               url     = 'http://ceur-ws.org/Vol-1661/'
  281     ].
 pepl_version(-Version, -Date)
Pepl's current Version (Maj:Min:Fix) and publication date (date(Year,Month,Day)).
?- pepl_version(V,D).
V = 2:2:0,
D = date(2021, 1, 1).

*/

  296pepl_version( 2:2:0, date(2021,1,1) ).
  297
  298there_exists_mem_slp_with_datafile( DataFile ) :-
  299     ( bb_get( current_slp, Cslp ) -> 
  300          true
  301          ;
  302          pepl_warn( nothing_in_memory )
  303     ),
  304     ( file_name_extension(Stem,slp,Cslp) -> true; Stem=Cslp ),
  305     % fname_stem( Cslp, ".slp", Stem, _FullExt ),
  306     atom_concat( Stem, '_data', DataFile ).
  307     % atom_codes( Stem, StemCs ),
  308     % append( StemCs, "_data", DataFileCs ),
  309     % atom_codes( DataFile, DataFileCs ).
  310     
  311frequencies_to_top_goal( [H-_Hocc|T], Goal ) :-
  312     H =.. [Name|Args], 
  313     mold_vars_list( Args, Vars ),
  314     Goal =.. [Name|Vars],
  315     frequencies_to_top_goal_1( T, Goal ).
  316
  317frequencies_to_top_goal_1( [], _Goal ).
  318frequencies_to_top_goal_1( [H-_Hocc|T], Goal ) :-
  319     ( \+ \+ Goal = H -> 
  320          true
  321          ;
  322          pepl_warn( skipping_datum(Goal,H) )
  323     ),
  324     frequencies_to_top_goal_1( T, Goal ).
  325
  326check_data( Data ) :-
  327     ( is_list(Data) ->
  328          ( Data == [] ->
  329               Inner = empty_list
  330               ;
  331               ( check_data_1(Data) ->
  332                    Inner = []
  333                    ;
  334                    Inner = list_is_not_pairs(Data)
  335               )
  336          )
  337          ;
  338          Inner = not_a_list(Data)
  339     ),
  340     ( Inner == [] ->
  341          true
  342          ;
  343          pepl_warn( data_format_error(Inner) )
  344     ).
  345
  346check_data_1( [] ).
  347check_data_1( [_G-_F|T] ) :-
  348     !,
  349     check_data_1( T ).
  350
  351sload_in_fam_options( [], [] ).
  352sload_in_fam_options( [H|T], SldOpts ) :-
  353     ( \+ memberchk( H, [keep_pl(_)] ) ->
  354          SldOpts = TSldOpts
  355          ;
  356          SldOpts = [H|TSldOpts]
  357     ),
  358     sload_in_fam_options( T, TSldOpts ).
 ssave(+File)
Save the stochastic program currently in memory to a file.
  364ssave( InFile ) :-
  365     ( InFile == user -> 
  366          bb_get( directives, Directvs ),
  367          portray_sdirectives( Directvs ), nl,
  368          bb_get( all_slp_clauses, AllClauses ),
  369          bb_get( pp, PPs ),
  370          portray_sclauses( AllClauses, PPs )
  371          ;
  372          ( file_name_extension(_Base,slp,InFile) ->
  373               File = InFile
  374               ;
  375               file_name_extension( InFile, slp, File )
  376          ),
  377          current_output( Co ),
  378          open( File, write, Stream ),
  379          ( ( set_output( Stream ),
  380               % DefMod = (write( (:- module( slp, [])) ), nl, nl),
  381               % pl( swi(_), DefMod ), 
  382               write( '% Generated by ssave/1.' ), nl, nl,
  383               bb_get( directives, Directvs ),
  384               portray_sdirectives( Directvs ), nl,
  385               bb_get( all_slp_clauses, AllClauses ),
  386               bb_get( pp, PPs ),
  387               portray_sclauses( AllClauses, PPs ),
  388               set_output( Co ),
  389               write( 'program saved in: ' ), write( File ), nl, !
  390               )
  391               ;
  392               set_output( Co ),
  393               write( 'failure while trying to save in: ' ), write( File ), nl
  394          ),
  395          close( Stream ),
  396          set_output( Co )
  397     ).
 sls
Listing of the stochastic program currently in memory.
  403sls :-
  404     % bb_get( all_transformed_clauses, TrsClauses ),
  405     % sprint_list( TrsClauses ).
  406     ssave( user ).
 sload_pe(Files)
 sload_pe(Files, Options)
Load an SLP to memory. If the source file has an slp extension the extension may be omitted. Pepl looks in the following directories and order for the source file(s). ., and ./slp/ while on SWI it also looks in, pack(’pepl/slp/’).
  416sload_pe( File ) :-
  417     sload_pe( File, [] ).
  418
  419sload_pe( Files, InOptions ) :-
  420     % bb_get( cc,  ),
  421     bb_put( pp, [] ),
  422     bb_put( all_slp_clauses, [] ),
  423     bb_put( all_transformed_clauses, [] ),
  424     bb_put( directives, [] ),
  425     sload_options_defaults( DefOpts ),
  426     options_cohesion( InOptions, DefOpts, Options ),
  427     once( select( add_mode(AddMode), Options, Opts1 ) ),
  428     ( AddMode == append ->
  429          true
  430          ;
  431          ( AddMode == write ->
  432               clean_slp_module
  433               ;
  434               pepl_warn( add_mode_opt(AddMode) )
  435          )
  436     ),
  437     sload_pe_1( Files, Opts1, 1, CC ),
  438     bb_put( cc, CC ),
  439     bb_put( current_slp, Files ).
 scall(Goal)
Sample (=call) Goal
?- sload_pe(coin).
?- set_random(seed(101)).
?- scall(coin(Flip)).
Flip = head.

?- scall(coin(Flip)).
Flip = tail.

If you have packs: mlu, b_real and Real.

?- lib(mlu).
?- sload_pe(coin).
?- mlu_sample( scall(coin(Side)), 100, Side, Freqs ), mlu_frequency_plot( Freqs, [interface(barplot),outputs([svg]),las=2] ).

Produces file: real_plot.svg

[[doc/html/images/real_plot.svg]]

To demonstrate the inability of SLPs to operate over arbitrary length objects, check:

?- sload_pe(member3).
?- lib(mlu).
?- set_random(seed(101)).
?- mlu_sample( scall(member3(X,[a,b,c])), 100, X, Freqs ), mlu_frequency_plot( Freqs, [interface(barplot),outputs(png),stem('meb3from3'),las=2] ).

Produces file: meb3from3.png

[[doc/html/images/meb3from3.png]]

...and:

?- sload_pe(member3).
?- lib(mlu).
?- set_random(seed(101)).
?- mlu_sample( scall(member3(X,[a,b,c,d,e,f,g,h])), 100, X, Freqs ), mlu_frequency_plot( Freqs, [interface(barplot),outputs(png),stem('meb3from8'),las=2] ).

Produces file: meb3from8.png

[[doc/html/images/meb3from8.png]]

See also
- scall/6

*/

  490scall( Goal ) :-
  491     Eps is 1E-10,
  492     scall_1( sample, Goal, Eps, _Path, Succ, _Prb ), 
  493     Succ \== fail. % fixme: or false ?
  494
  495scall( Goal, Path, Succ, Prb ) :-
  496     scall_1( all, Goal, 0, Path, Succ, Prb ). 
 scall(Goal, Eps, Meth, Path, Succ, Prb)
This predicate is for people interested in the iternals of pepl. Use at your own peril.

The predicate arguments are as follows.

See predicate main_gen/1, in examples/main_scfg.pl for example usage.

You can use scall/6 to sample from an SLP.

?- sload_pe(coin).
?- set_random(seed(101)).
?- scall(coin(Flip), 0, sample, Path, Succ, Prb ).
Flip = head,
Path = [1],
Prb = 0.5.

... or to backtrack overall paths

?- scall(coin(Flip), 0, all, Path, Succ, Prb ).
Flip = head,
Path = [1],
Prb = 0.5 ;
Flip = tail,
Path = [2],
Prb = 0.5.

*/

  542scall( Goal, Eps, Meth, Path, Succ, Prb ) :-
  543     scall_1( Meth, Goal, Eps, Path, Succ, Prb ).
  544
  545dbg_flag( off ).
  546
  547:- ensure_loaded( library(write_list_with_line_numbers) ).
 switch_dbg(Switch)
Switch debugging of fam/1 to either on or off.
  553switch_dbg( Flag ) :-
  554     ( (Flag == on;Flag == off) -> 
  555          retractall( dbg_flag(_) ),
  556          assert( (dbg_flag(Flag):- !) )
  557          ;
  558          G = switch_dbg( Flag ), T=[off,on],
  559          print_message( error, type_error(G,1,T,Flag) )
  560     ).
 dbg_pepl(+Goal)
Call Goal iff in (pepl) debugging.
  566dbg_pepl( Goal ) :- 
  567     ( dbg_flag(on) -> 
  568          call( Goal )
  569          ; 
  570          true
  571     ).
  572
  573dbg_ls_pepl( Header, List ) :-
  574     ( dbg_flag(on) -> 
  575          write( Header ), nl,
  576          write_list_with_line_numbers( List, 1, 4 ), nl
  577          ;
  578          true
  579     )