% --------------------------------------------------------
% DCS 533 - Matthew Stone - Spring 2002 - HW 3
% Due February 20, 2002 - in class.
% 
% The point of this homework is to explore a model of language
% interpretation as (probabilistic) constraint-satisfaction.
% All the ALGORITHMS that are required for the exercises are
% provided for you, and comments below explain the algorithms.  
% The job of the exercise is then to create REPRESENTATIONS
% that summarize the linguistic information and the contextual
% information that go into interpreting some sample utterances.
% You will then be able to show that language and context 
% suffice to identify the intended interpretation of the
% utterances.
%
% THIS FILE is organized as 
% - OVERVIEW of REPRESENTATIONS
% - PROGRAM CODE
% - PROBLEMS
%
% --------------------------------------------------------
%
% OVERVIEW OF REPRESENTATIONS
%
% The central representation used here is a prolog representation 
% of an inference that's supported by the context.  These 
% representations are constructed as follows.
% THERE ARE TWO KINDS OF FORMULAS:
% - an atomic formula : Fact 
% - a rule : if(Fact, Antecedents)
%     where Fact is an atomic formula and 
%           Antecedents is a list of atomic formulas
%     meaning that whenever an instance of Antecedents is true,
%                  then the corresponding instance of Fact is true.
% WE BUILD PROOFS FROM THESE IN TWO WAYS:
% - axiom(Fact) is a proof
%      when Fact is an atomic formula supported by the context.
% - chain(Fact, if(Fact, Antecedents), Subproofs) is a proof
%      when Fact is an atomic formula, Subproofs is a list of proofs
%           (of the same length as Antecedents), and (for each i) 
%           the ith member of Subproof is a proof of the ith member
%           of antecedents.
% AN INTERPRETATION OF AN UTTERANCE 
% is just a proof that draws on the fact that the utterance 
% has occurred to show that some desired effect for 
% the conversation can be achieved in context.
%
% The predicate PROOF(Proof, Fact) checks recursively
% that a structure has the right form (but does not 
% access the context to verify axioms).

proof(axiom(Fact), Fact).
proof(chain(Head, if(Head, Antecedent), Subproofs), Head) :-
	proofs(Subproofs, Antecedent).

proofs([],[]).
proofs([Proof|Proofs], [Fact|Facts]) :-
	proof(Proof, Fact),
	proofs(Proofs, Facts).

% The predicate RESULT(Proof, Fact) just accesses 
% the proof structure to see what the conclusion is.

result(axiom(Fact), Fact).
result(chain(Fact, _, _), Fact).

% In addition to proofs, we need to be able to represent
% information in the context.  We do this using two PREDICATES,
% context and goal.
%
%    context(Fact, Weight) is true if
%       Fact is available in the context, and
%       the log probability of an utterance 
%       requiring Fact as part of its interpretation
%       is proportional to Weight.
%
%    goal(Fact, Weight) is true if
%       Fact represents an outcome that 
%       contributes to the ongoing projects, and
%       the log probability of an utterance
%       contributing Fact at this stage of
%       the conversation is proportional to Weight.
% 
% Further, in order to encapsulate the linguistic knowledge
% that you access in understanding an utterance, we will
% use a PREDICATE schema.
%
%    schema(ProofWithVariables) is true
%       if any instance of ProofWithVariables
%       represents a possible intepretation of an 
%       utterance.
%
% In your answers to this exercise, you will have to define
% appropriate facts for the CONTEXT, GOAL and SCHEMA relations,
% in a file SOLUTION.PL.  It gets loaded in here.

:- consult('solution.pl').

% --------------------------------------------------------
%
% PROGRAM CODE
%
% The main work is to define a predicate
%
%     interpret(Schema, Instances) true if
%        Schema is a template proof encapsulating the possible
%           interpretations of an utterance, and
%        Instances is the set of structures that describe
%           specific ways this schema could be interpreted
%           at this point in the conversation.
%
% Instances is represented as a list of structures of the form
%    i(Proof, Weight)
% where Proof is an instance of Schema and Weight is proportional
% to the log probability of the interpretation (on the assumption
% that probability just reflects the likelihood of the links to the
% context and goal, construed as independent).
%
% This is implemented as a simple constraint-satisfaction procedure.
% It requires helper routines:
% - member(E,L) true if element E is in list L.

member(Element, [Element |_Rest]).
member(Element, [_Head | Rest]) :-
	member(Element, Rest).

flatten([], [], []) :- !.
flatten([], [H|T], Result) :- !,
	flatten(H, T, Result).
flatten([H|T], Later, Result) :- !,
	flatten(H, [T|Later], Result).
flatten(E, [], [E]).
flatten(E, [H|T], [E|Result]) :-
	flatten(H, T, Result).

% - one_match(Pattern, Values, Instance, Weight) true
%      if Instance an instance of Pattern  
%         drawn from Values with Weight

one_match([], [], [], 0).

one_match([Head | Tail], [HeadAnswers | TailAnswers], 
	  [HeadInstance | TailInstances], Weight) :-
	member(i(HeadInstance, HeadWeight), HeadAnswers),
	result(HeadInstance, Head),
	one_match(Tail, TailAnswers, TailInstances, TailWeight),
	Weight is HeadWeight + TailWeight.

% - interpret_link[s](Inference[s], Instance[s]) true when
%      Instance[s] hold[s] the instances of Inference[s] that
%      fit the context.

interpret_link(axiom(Fact), Answers) :-
	findall(i(axiom(Fact),Weight), 
	        context(Fact,Weight), 
		Answers).

interpret_link(chain(Head, if(Head, Antecedents), Inferences), Answers) :-
	interpret_links(Inferences, InfAnswers),
	findall(i(chain(Head, if(Head, Antecedents), Instance), Weight),
	        one_match(Antecedents, InfAnswers, Instance, Weight),
		Answers).

interpret_links([Inference | Inferences], [Answer | Answers]) :-
	interpret_link(Inference, Answer),
	interpret_links(Inferences, Answers).

interpret_links([], []).

% - interpret_goal(Instance, Instance')
%      Reweight Instance to Instance' to reflect how
%      plausible its contribution is.

interpret_goal(i(Template, Weight), i(Template, NewWeight)) :-
	result(Template, Info),
	goal(Info, W),
	NewWeight is Weight + W.

% INTERPRET itself.

interpret(Proof, FinalAnswers) :-
	interpret_link(Proof, Answers),
	findall(Answer, 
	        X^(member(X, Answers), 
		   interpret_goal(X, Answer)),
		FinalAnswers).

% --------------------------------------------------------
%
% UNDERSTAND 
%
% A wrapper around interpret that makes life easier.
% 
%  understand(Utterance, MLI) is true
%    if MLI is the list of most likely interpretations
%    derived from a schema whose top inference requires
%    as its first premise the assumption in context
%
%         say(Utterance)
%
%    understand adds this to the context while it works, 
%    by the way, so you don't need the say() fact...
%
% it requires a helper function to find 
% maximal elements in a list...

utterance(chain(_, if(_, [ say(Utterance) | _]), _), Utterance).

highest_weight(X, [i(Head,W) | Tail]) :-
	aux_highest_weight(Head, W, Tail, X).

aux_highest_weight(Best, BestW, [i(_Head, W) | Tail ], X) :-
	BestW >= W,
	aux_highest_weight(Best, BestW, Tail, X).

aux_highest_weight(_Best, BestW, [i(Head, W) | Tail ], X) :-
	W > BestW,
	aux_highest_weight(Head, W, Tail, X).

aux_highest_weight(_Best, BestW, [i(Head, W) | Tail ], Head) :-
	W = BestW,
	aux_highest_weight(Head, W, Tail, Head).

aux_highest_weight(Best, _BestW, [], Best).

understand(Utt, Answers) :-
	assert(context(say(Utt),0)),
	findall(PL, Schema^(schema(Schema),
	                    utterance(Schema, Utt),
	                    interpret(Schema, PL)), 
		PLL),
	flatten(PLL, [], Possibilities),
	findall(Answer, highest_weight(Answer, Possibilities), Answers),
	retract(context(say(Utt),0)),
	!.

understand(Utt, _Answers) :-
	retract(context(say(Utt),0)),
	!,
	fail.

% --------------------------------------------------------
%
% GENERATE
%
% Another wrapper around interpret.


generate(Effect, Utterance, Interpretation) :-
	schema(Schema),
	Pattern = Utterance,
	result(Schema, Effect),
	utterance(Schema, Pattern),

	assert(context(say(Pattern), 0)),
	interpret(Schema, Possibilities),
	retract(context(say(Pattern), 0)),

	highest_weight(Interpretation, Possibilities),
	result(Interpretation, Effect),
	utterance(Interpretation, Utterance),

	understand(Utterance, [Interpretation]).

% --------------------------------------------------------
%
% PROBLEMS
%
% For this assignment, you should hand in a
% SOLUTION.PL file, with definitions of the
% relations CONTEXT, GOAL and SCHEMA.  You will
% develop this file through a series of steps,
% that gradually introduce more aspects of
% Clark's drugstore interaction.  For now,
% we'll assume that the utterance is:
%
%    this bottle here, pointing (along path p).
%
% Represent this as the utterance term
%
%    simult(bottle(this, here), point(p))
%
% So the assumption that this utterance occurs is
%
%    say(simult(bottle(this, here), point(p)))
%
% The argument to formalize its interpretation is:
%
%    b1 is a bottle
%    b1 is located at l1
%    l1 is proximate (ie., it could be "here")
%    l1 is located along p (ie., it could be "this")
%    what_to_buy is a salient question.
%
%    When someone says this bottle here, pointing along P, and
%         Q is a salient question, 
%         B is a bottle, and
%         B is located at proximate place L along P,
%    then it means the answer to Q is B.
%
%    the answer to what_to_buy is b1.
%
% 1. WRITE A FACT of the form schema(Term)
%    where Term gives the form of this argument,
%    leaving open the individuals referred to.
%
% 2. WRITE FACTS of the form context(F,W) 
%    to specify all the premises for this argument
%    as used in this context.  
%    (Weights won't matter much.)
%
% 3. WRITE a FACT of the form goal(G,W)
%    to link the conclusion of the argument 
%    to projects in the context.
%
% Check your work: make sure that these two calls succeed:
%   understand(<the utterance>, Interpretation)
%   generate(<the effect>, Utterance, Interpretation)
%
% 4. Now, repeat steps 1-3, to create a complete
%    alternative interpretation: a second bottle b2
%    located at a second designated proximate location l2
%    to answer some other question (e.g., what_i_just_drank)
%    that's contextually-provided and contextually-relevant.
%    Make these facts as salient as those you chose before.
%
% Check your work: 
%    what does interpret give now?  what does generate give?
%
% Finally, play:
%    Try removing any of the facts in 4.  What happens?
%    Try changing their likelihood.  What happens?

summarize([], []).

summarize([chain(X,_,_)|Is], [X|Ss]) :-
    summarize(Is, Ss).
