/*==============================================================================
 *	LPAD and CP-Logic reasoning suite
 *	File: montecarlo.pl
 *	Solves LPADs with Monte Carlo (main predicate: solve(Goals, Prob, Samples, ResTime, BddTime)
 *	Copyright (c) 2009, Stefano Bragaglia
 *============================================================================*/
 
/* EXTERNAL FILE
 * -------------
 * The following libraries are required by the program to work fine.
 */

:- dynamic rule/4, def_rule/2, randx/1, randy/1, randz/1.

:- use_module(library(lists)).
:- use_module(library(random)).
:- use_module(library(ugraphs)).
:- use_module(params).

% :- source.
% :- yap_flag(single_var_warnings, on).





/* SOLVING PREDICATES
 * ------------------
 * The predicates in this section solve any given problem with several class of 
 * algorithms.
 *
 * Note: the original predicates (no more need and eligible to be deleted) have 
 *       been moved to the end of the file.
 */

/* Starting seed. */
randx(1).
randy(1).
randz(1).

/* newsample
 * ---------
 * This predicate programmatically generates and sets a new seed for the 
 * algorithm.
 */
newsample :-
	retract(randx(X)),
	randy(Y),
	randz(Z),
	(X =< 30269 ->
		SuccX is X + 1,
		assert(randx(SuccX));
		assert(randx(1)),
		retract(randy(_)),
		(Y =< 30307 ->
			SuccY is Y + 1,
			assert(randy(SuccY));
			assert(randy(1)),
			retract(randz(_)),
			(Z =< 30323 ->
				SuccZ is Z + 1,
				assert(randz(SuccZ));
				assert(randz(1))))),
	setrand(rand(X, Y, Z)).


/* solve(Goals, Samples, Time, Lower, Prob, Upper)
 * -----------------------------------------------
 * This predicate calls the Monte Carlo solving method for the current problem.
 * It requires the Goals to fulfil and returns the number of Samples considered,
 * the Time required, the extimated Probability and the Lower and Upper bounds.
 *
 * INPUT
 *  - Goals: given list of goals to fulfil.
 *
 * OUTPUT
 *  - Samples: number of samples considered to solve the problem.
 *  - Time: time required to solve the problem.
 *  - Lower: lower end of the confidence interval.
 *  - Prob: extimated probability.
 *  - Upper: upper end of the confidence interval.
 */
solve(Goals, Samples, Time, Lower, Prob, Upper) :-
	% Retrieving functional parameters...
	setting(k, K),
	setting(min_error, MinError),
	% Resetting the clocks...
	statistics(walltime, [_, _]),	
	% Performing resolution...
	montecarlo(0, 0, Goals, K, MinError, Samples, Lower, Prob, Upper),	
	% Taking elapsed times...
	statistics(walltime, [_, ElapTime]),
	% Setting values...
	Time is ElapTime/1000.



/* montecarlo(Count, Success, Goals, K, MinError, Samples, Lower, Prob, Upper)
 * ---------------------------------------------------------------------------
 * This tail recursive predicate solves the problem currently in memory with a
 * Monte Carlo approach. 
 * It requires the number of samples and successes so far (Count and Success),
 * the desired list of Goals to fulfil, the number K of samples to consider at
 * once and the threshold MinError for the binomial proportion confidence 
 * interval.
 * It returns the total number of Samples considered, the Lower and Upper ends 
 * of the the binomial proportion confidence interval and the extimated Prob.
 * 
 * INPUT
 *  - Count: number of samples considered so far.
 *  - Success: number of successfull samples considered so far.
 *  - Goals: list of goals to fulfil.
 *  - K: number of samples to consider at once.
 *  - MinError: threshold for the binomial proportion confidence interval.
 * 
 * OUTPUT
 *  - Samples: total number of samples considered.
 *  - Lower: lower end of the the binomial proportion confidence interval.
 *  - Prob: extimated probability.
 *  - Upper: upper end of the the binomial proportion confidence interval.
 *
 * NB: This method is based on the binomial proportion confidence interval and
 *     the Brown's rule of thumb to avoid the case the sample proportion is 
 *     exactly 0.0 or 1.0 and doesn't make use of BDDs.
 */
montecarlo(Count, Success, Goals, K, MinError, Samples, Lower, Prob, Upper) :-
	/* Decomment the following line if you want to test the algorithm with an
	   incremental seed for each sample.
	newsample,
	*/
	main(Goals, [], _Explan, Valid),
	N is Count + 1,
	S is Success + Valid,
	(N mod K =:= 0 ->
%		format("Advancement: ~t~d/~d~30+~n", [S, N]),
		P is S / N,
		D is N - S,
		Semi is 2 * sqrt(P * (1 - P) / N),
		Int is 2 * Semi,
	/*   N * P > 5;   N * S / N > 5;   S > 5
	 *   N (1 - P) > 5;   N (1 - S / N) > 5;   N (N - S) / N > 5;   N - S > 5
	 */
		((S > 5, D > 5, (Int < MinError; Int =:= 0)) ->
			Samples is N,
			Lower is P - Semi,
			Prob is P,
			Upper is P + Semi;
			montecarlo(N, S, Goals, K, MinError, Samples, Lower, Prob, Upper));
		montecarlo(N, S, Goals, K, MinError, Samples, Lower, Prob, Upper)).



/* null
 * ----
 * This is dummy predicate to use sparingly when needed. 
 * Typical uses are as spying predicate during tracing or as dead branch in 
 * ( -> ; ) predicate.
 */	
null.



/* main(Goals, Explan0, Explan1, Valid)
 * ------------------------------------
 * This tail recursive predicate looks for a solution to the given Goals
 * starting from the given Explan0 and returns the final Explan and 1 (0 otherwise) if it is a 
 * Valid sample for Montecarlo.
 */
main([], Explan, Explan, 1). 

main([\+ Goal|Tail], Explan0, Explan1, Valid) :-
	builtin(Goal), !,
	(call((\+ Goal)) ->
		main(Tail, Explan0, Explan1, Valid);
		Explan1 = Explan0,
		Valid = 0).
	
main([Goal|Tail], Explan0, Explan1, Valid) :-
	builtin(Goal), !,
	(call(Goal) ->
		main(Tail, Explan0, Explan1, Valid);
		Explan1 = Explan0,
		Valid = 0).

main([Goal|Tail], Explan0, Explan1, Valid) :-
	findall((IsSample, Goals, Step), explore([Goal|Tail], Explan0, IsSample, Goals, Step), List), 
	cycle(List, Explan0, Explan1, Valid).

	

/* explore([Goal|Tail], Explan, Valid, Goals, Step)
 * ------------------------------------------------
 * This predicate looks for a Body and the Step to reach it from the given Goal 
 * and Explan and returns 1 (0 otherwise) if they are a Valid sample for
 * Montecarlo. 
 * Please note that Body and Step are meaningfull only when Valid is 1.
 *
 * This comment has to be fixed.
 */
explore([Goal|Tail], _Explan, 1, Goals, []) :-
	def_rule(Goal, Body),
	append(Body, Tail, Goals).

explore([Goal|Tail], Explan, Valid, Goals, Step) :- 
	findrule(Goal, Explan, Valid, Body, (HeadID, RuleID, Subst)),
	append(Body, Tail, Goals),
	(member_eq((HeadID, RuleID, Subst), Explan) ->
		Step = [];
		Step = [(HeadID, RuleID, Subst)]).



/* findrule(Goal, Explan, Valid, Body, (HeadID, RuleID, Subst))
 * ---------------------------------------------------------------
 * This predicate finds a rule that matches with the given Goal and Explan and
 * returns 1 (0 otherwise) if it is a Valid sample for Montecarlo.
 * If the sample is Valid, the other return parameters are also meaningfull and
 * are the Body and (RuleID, Subst, HeadIS) of the rule that matches with the 
 * given Goal and Explan.
 *
 * This comment has to be fixed.
 */
findrule(Goal, Explan, Valid, Body, (HeadId, RuleId, Subst)) :-
	rule(Goal, _Prob, Required, RuleId, Subst, _Heads, HeadsList, Body),
	sample(HeadsList, HeadId),
	not_already_present_with_a_different_head(HeadId, RuleId, Subst, Explan),
	(HeadId =:= Required ->
		Valid = 1;
		Valid = 0).
	


/* sample(Heads, RuleId, HeadId, Subst)
 * ------------------------------------
 * This tail recursive predicate samples a random head among the given Heads of
 * the given RuleId and returns its HeadId and Subst.
 */
sample(HeadList, HeadId) :-
	random(Prob), 
	sample(HeadList, 0, 0, Prob, HeadId), !.

sample([_HeadTerm:HeadProb|Tail], Index, Prev, Prob, HeadId) :-
	Succ is Index + 1,
	Next is Prev + HeadProb,
	(Prob =< Next ->
		HeadId = Index;
		sample(Tail, Succ, Next, Prob, HeadId)).



/* cycle([(IsSample, Body, [Step])|Tail], Explan0, Explan1, Found)
 * -----------------------------------------------------------------
 * This tail recursive predicate analyzes the given Body and Step to reach it
 * and returns 0 as it's not a Valid sample for Montecarlo. 
 * If it is Valid, it looks for a solution to the Body and the given Goals 
 * starting from the Step and the given Explan and returns 1 if it finds a
 * Valid one.
 * If it does not find it, it considers the next Body and Step and returns their
 * Valid value.
 *
 * NB: This comment needs to be updated.
 */
cycle([], Explan, Explan, 0).

cycle([(0, _Goals, Step)|Tail], Explan0, Explan1, IsSample) :- !,
	append(Step, Explan0, Explan2),
	cycle(Tail, Explan2, Explan1, IsSample).

cycle([(1, Goals, Step)|Tail], Explan0, Explan1, IsSample) :- 
	append(Step, Explan0, Explan),
	main(Goals, Explan, Explan2, Valid),
	(Valid == 1 ->
		Explan1 = Explan2,
		IsSample = 1;
		cycle(Tail, Explan2, Explan1, IsSample)).
