
% This is the code for the Trial Selector component of ASE-Progol.

% The code consults:

% 1/ The file of matrix_cell(H, Compression, Trial, Cell_value)
% facts which record how each trial is classified by each
% hypothesis.
% 2/ The file of static knowledge as it requires cost(Trial, Cost)
% definitions for domain.

% The code outputs ...
% 1/ A table of statistics on the hypotheses.
% 2/ A table of statistics on the trials.
% 3/ The trial which minimises the expected cost of experimentation.

% The statistics on the hypotheses are computed prior to the
% calculation of the estimated cost of each trial and they are
% asserted to memory as predicate hyp_stats/6 ground clauses. This is
% done because some of these statistics are repeatably required in the
% calculations of the estimated costs of each of the trials.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- set(r,10000)?
:- set(h,10000)?

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

select_trial(Classifications_file, Static_know_file, Current_trial_file):-
	consult(Classifications_file),
	consult(Static_know_file),
	compute_two_to_power_compression_of_hyps,
	bagof(Two_to_power_compression, hyp_compression(H, Compression, Two_to_power_compression), List),
	sum_elements_in_list(List, Sum),
	compute_other_stats_on_hyps(Sum),
	setof(E, matrix_cell(_, _, E, _), L),
	trial_with_lowest_EC(L, Trial),
	nl,
	write('Instruct robot to perform the trial:'),
	nl,
	write('trial('),
	write(Trial),
	write(').'),
	nl,
	nl,
	tell(Current_trial_file),
	write('trial('),
	write(Trial),
	write(').'),
	told.

trial_with_lowest_EC([Trial], Trial):-
	nl,
	write('There is only one candidate trial.'),
	nl.	
				
% If there is only one trial to choose from then there is no need
% to compute cost estimates.

trial_with_lowest_EC(_, Trial):- 
        compute_cost_estimates,
	setof(EC, trial_stats(_, EC), L),
	qsort(L, [Lowest_EC | Higher_ECs]),
	trial_stats(Trial, Lowest_EC).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

compute_two_to_power_compression_of_hyps:-
	matrix_cell(H, Compression, _, _),
	Two_to_power_compression is 2^(Compression),
	asserta(hyp_compression(H, Compression, Two_to_power_compression)),
	fail.

compute_two_to_power_compression_of_hyps.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sum_elements_in_list([], 0).
sum_elements_in_list([Head | Tail], Sum):-
	sum_elements_in_list(Tail, Sum_tail),
	Sum is Head + Sum_tail.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

compute_other_stats_on_hyps(Sum):-
	nl,
	write('Statistics on hypotheses'),
	nl,
	write('--------------------------------------------------'),
	nl,
	write_column('Compress',10),
	write_column('2^Compre',10),
	write_column('Norm_prob',10),
	write_column('Log(2)np',10),
	write_column('Product',10),
	write('Hypothesis'),
	nl,
	write_column(' ',10),
	write_column(' ',10),
	write_column('*1000',10),
	write_column('(floor)',10),
	write_column('*1000',10),
	nl,
	write('--------------------------------------------------'),
	nl,
	hyp_compression(H, Compression, Two_to_power_compression),
	Norm_prob is Two_to_power_compression / Sum,
	Factor is (1 / (log 2)),
	Log_norm_prob is floor(Factor * (log  Norm_prob)),
						% multiply by 1/ln(e)2
						% to convert loge to
						% log2. 
						% floor, rather than
						% ceil, since log of
						% probability will be
						% negative. 
	Product is Norm_prob * Log_norm_prob,
	asserta(hyp_stats(H, 
                          Compression, 
                          Two_to_power_compression, 
			  Norm_prob,
			  Log_norm_prob,
			  Product)),

	write_column(Compression,10),
	write_column(Two_to_power_compression,10),
%	write_column(Norm_prob,10),
	Norm_prob1000 is Norm_prob * 1000,
	write_column(Norm_prob1000,10),
	write_column(Log_norm_prob,10),
%	Log_norm_prob1000 is Log_norm_prob * 1000,
%	write_column(Log_norm_prob1000,10),
%	write_column(Product,10),
	Product1000 is Product * 1000,
	write_column(Product1000,10),
	write(H),
	nl,
	fail.

compute_other_stats_on_hyps(_):-
	write('--------------------------------------------------'),
	nl.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

compute_cost_estimates:-
	nl,
	write('Statistics on trials'),
	nl,
	write('------------------------------------------------------------'),
	nl,
	write_column('Cost(e)',10),
	write_column('Mean(E-e)',10),
	write_column('p(e)',10),
	write_column('EC_con',10),
	write_column('EC_incon',10),
	write_column('EC(H,E,e)',10),
	write('Trial'),
	nl,
	write('------------------------------------------------------------'),
	nl,
	setof(E, matrix_cell(_, _, E, _), L),
	element(Trial, L),
	expected_cost(Trial, Cost_of_trial, Mean_cost, Prob_of_trial, EC_con, EC_incon, Expected_cost),
	asserta(trial_stats(Trial, Expected_cost)),
	write_column(Cost_of_trial,10),
	write_column(Mean_cost,10),
	write_column(Prob_of_trial,10),
	write_column(EC_con,10),
	write_column(EC_incon,10),
	write_column(Expected_cost,10),
	write(Trial),
	nl,
	fail.

compute_cost_estimates:-
	write('------------------------------------------------------------'),
	nl.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

expected_cost(Trial, Cost_of_trial, Mean_cost, Prob_of_trial, EC_con, EC_incon, Expected_cost):-
	!,
	cost(Trial, Cost_of_trial),
	setof(E, matrix_cell(_, _, E, _), Set_of_all_trials), 
	delete(Trial, Set_of_all_trials, Set2),
	mean_cost(Set2, Mean_cost),
	expected_cost(Trial, Mean_cost, 1, EC_con),
	expected_cost(Trial, Mean_cost, 0, EC_incon),
	bagof(H, matrix_cell(H, _, Trial, 1), L),
	sum_probabilities(L, Prob_of_trial),
	Expected_cost is ( Cost_of_trial + 
                           (Prob_of_trial * EC_con) + 
		           ((1 - Prob_of_trial) * EC_incon) ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

mean_cost(Set_of_trials, Mean_cost):-
	sum_costs(Set_of_trials, Sum_of_costs),
	length(Set_of_trials, N),
	Mean_cost is Sum_of_costs / N.

sum_costs([], 0).
sum_costs([H|T], Sum_of_costs):-
	sum_costs(T, Tail_sum),
	cost(H, Cost),
	Sum_of_costs is Cost + Tail_sum.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

expected_cost(Trial, Mean_cost, Cell_value, Expected_cost):-
	bagof(H, matrix_cell(H, _, Trial, Cell_value), L),
	sum_products(L, Sum_of_products),
	Expected_cost is (- Mean_cost) * Sum_of_products.

sum_products([], 0).

sum_products([H|T], Sum_of_products):-
	sum_products(T, Tail_sum),
	hyp_stats(H, _, _, _, _, Product),
	Sum_of_products is Product + Tail_sum.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sum_probabilities([], 0).

sum_probabilities([H|T], Sum_of_probabilities):-
	sum_probabilities(T, Tail_sum),
	hyp_stats(H, _, _, Prob, _, _),
	Sum_of_probabilities is Prob + Tail_sum.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

write_column(Column_entry, Width):-
	size(Column_entry, Size),
 	Size > Width,
	!,
	write('ERROR: Column entry longer than width of column.'), 
	nl,
	write('Size of entry = '),
	write(Size),
	write(' but Width = '),
	write(Width),
 	nl.

write_column(Column_entry, Width):-
        size(Column_entry, Size),
        Space is Width-Size,
        write(Column_entry), 
        tab(Space).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

size([],2):-!.
size(List,Size) :-
        List = [_|_],
        !,
        length(List, L),
        Size is 2*L + 1.

% The write predicate of CProgol displays real numers in the range
% 0.01 >= x < 10000 as real numbers with 3 decimal places. Real numers
% outside this range are displayed in floating point form with 3
% significant places; such numbers require 9 characters. Hence cannot
% use name/2 predicate for real numbers outside the range because name
% does not operate on the form used in the column.

size(Column_entry, 9):-
	float(Column_entry),
	( (Column_entry < 0.01) ; (Column_entry >= 10000) ),
	!.

size(Column_entry, Size) :-
        name(Column_entry,L),
        length(L,Size), 
	!.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% delete(X, Y, Z) deletes all occurrences of X from Y to give Z.

delete(_, [], []).

delete(H, [H|T], R):-
	delete(H, T, R).

delete(A, [H|T], [H|R]):-
	not(A = H),
	delete(A, T, R).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% qsort(List1, List2)
%
% qsort([1,2,5,4,9,1],Sorted_list)?
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

qsort( [], [] ).

qsort( [H | T], Slist ):-
    partition( H, T, L1, L2 ),
    qsort( L1, Slist1 ),
    qsort( L2, Slist2 ),
    append( Slist1, [H | Slist2], Slist ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% partition(Divider, List1, List2, List3)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

partition(Divider, [H | T], [H | S1], S2):-
    H =< Divider,
    partition(Divider, T, S1, S2).

partition(Divider, [H | T], S1, [H | S2]):-
    H > Divider,
    partition(Divider, T, S1, S2).

partition(_, [], [], []).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

append([],L,L).
append([H|T],L,[H|Z]) :- append(T,L,Z).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
