https://github.com/RichardMoot/LinearOne
Raw File
Tip revision: 55540e9a91b7a5c7722775f39ffbf50f5b472a7e authored by Richard Moot on 12 November 2020, 17:47:42 UTC
Update .gitignore
Tip revision: 55540e9
mill1.pl
#!/Applications/SWI-Prolog.app/Contents/MacOS//swipl -q -t main -f

:- use_module(dancing_links,       [compute_axioms/4,
				    update_roots_axiom/4,
				    update_roots_contraction/5]).
:- use_module(portray_graph_tikz, [portray_graph/1,graph_header/0,graph_footer/1,latex_graph/1]).
%:- use_module(portray_graph_none,  [portray_graph/1,graph_header/0,graph_footer/1,latex_graph/1]).
:- use_module(translations,        [translate_lambek/3,
				    translate_displacement/3,
				    translate_hybrid/6,
				    exhaustive_test/6]).
:- use_module(proof_generation,    [generate_proof/2,
				    generate_sequent_proof/2,
				    generate_natural_deduction_proof/2,
				    generate_nd_proof/2,
				    generate_hybrid_proof/2,
				    generate_displacement_proof/2]).
:- use_module(latex,               [proof_header/0,
				    proof_footer/0,
				    latex_semantics/1,
				    latex_lexicon/1,
				    latex_lexicon1/0]).
:- use_module(sem_utils,           [substitute_sem/3,
				    reduce_sem/2]).
:- use_module(replace,             [replace_graph/6,
				    replace_proofs_labels/4,
				    replace_formula/5]).
:- use_module(lexicon,             [lookup/5]).
:- use_module(auxiliaries,         [merge_fvs/3,
				    free_vars_n/2,
				    free_vars_p/2]).

:- dynamic '$PROOFS'/2, '$AXIOMS'/1, '$LOOKUP'/1.
:- dynamic node_formula/3.

:- op(400, xfy, \).
:- op(190, yfx, @).

% = adds some likely locations for pdflatex/lualatex to the file search path

user:file_search_path(path, '/usr/texbin/').
user:file_search_path(path, '/opt/local/bin/').

% = allow for simplified atomic_formula/1 declarations; does the completely general
% = translation to atomic_formula/3 automatically

term_expansion(atomic_formula(A), atomic_formula(A, F, Vs)) :-
	A =.. [F|Vs],
	!.

% = some definitions for pretty-printing

portray(neg(F, X, L)) :-
	atom(F),
	Term =.. [F|L],
	format('-~p ~p',[Term,X]).
portray(pos(F, X, L)) :-
	atom(F),
	Term =.. [F|L],
	format('+~p ~p',[Term,X]).
portray(at(X, Vs)) :-
	atom(X),
	Term =.. [X|Vs],
	print(Term).
portray(at(X, I1, I2, Vs)) :-
	atom(X),
	Term =.. [X|Vs],
	format('~w{~w,~w}', [Term,I1,I2]).
portray(impl(A,B)) :-
	format('(~p -o ~p)', [A,B]).
portray(rule(N,A,B,Ps)) :-
	copy_term(A-B, AA-BB),
	numbervars(AA-BB, 0, _),
	Ps \== [],
	format('rule(~p,~p  |-  ~p,...)', [N,AA,BB]).
portray(appl(appl(appl(appl(appl(F,W),V),Z),Y),X)) :-
	atom(F),
	!,
	Term =.. [F,X,Y,Z,V,W],
	print(Term).
portray(appl(appl(appl(appl(F,V),Z),Y),X)) :-
	atom(F),
	!,
	Term =.. [F,X,Y,Z,V],
	print(Term).
portray(appl(appl(appl(F,Z),Y),X)) :-
	atom(F),
	!,
	Term =.. [F,X,Y,Z],
	print(Term).
portray(appl(appl(F,Y),X)) :-
	atom(F),
	!,
	Term =.. [F,X,Y],
	print(Term).
portray(appl(F,X)) :-
	atom(F),
	!,
	Term =.. [F,X],
	print(Term).
portray(appl(M,N)) :-
	format('(~p ~p)', [M,N]).
portray(lambda(X,M)) :-
        format('(~p^~p)', [X,M]).
portray(bool(P,B,Q)) :-
	format('(~p ~p ~p)', [P,B,Q]).

% =======================================
% = Top-level theorem prover predicates =
% =======================================

% command line execution
%
% mill1 GrammarFile GoalFormula Words
%
% loads GrammarFile and passes Words and GoalFormula to parse/2

main :-
	current_prolog_flag(os_argv, Argv),
        append(_, [A|Av], Argv),
	file_base_name(A, 'mill1.pl'),
	!,
        main(Av).

main([silent|Rest]) :-
	!,
	retractall(output_error(_)),
	assert(output_error(0)),
	main(Rest).
main([GrammarFile,Atom|Words]) :-
	!,
	load_grammar(GrammarFile),
	/* allow goal formulas of the form "s\(fin\)", etc. (backslashes are necessary!) */
	read_term_from_atom(Atom, GoalFormula, []),
	parse(Words, GoalFormula),
	halt.
main(_) :-
	format(user_output, 'Usage: mill1 GrammarFileName GoalFormula Words~n', []).


% = load_grammar(File)
%
% compile Prolog grammar file File

load_grammar(File) :-
	absolute_file_name(File, GrammarFile, [access(read),file_type(prolog)]),
	grammar_cleanup,
	compile(GrammarFile).

% = grammar_cleanup
%
% remove lexicon and test sentences from previous grammar

grammar_cleanup :-		
	abolish(lex/3),
	abolish(lex/4),
	abolish(lex/5),
	abolish(atomic_formula/1),
	abolish(atomic_formula/3),
	abolish(test/1).

% = parse_all
%
% parse all test sentences in the currently loaded
% grammar file; test sentences are assumed to be
% of the form
%
% test(N) :-
%       parse(Words, Goal).
%
% or
%
% test(N) :-
%       prove(Antecedent, Goal).

parse_all :-
	findall(N, clause(test(N),_), List),
	parse_all(List, Solutions),
	print_solutions(List, Solutions).

parse_all([], []).
parse_all([N|Ns], [P-SemList|Ps]) :-
        test(N),
        user:'$PROOFS'(P, SemList),
	parse_all(Ns, Ps).

% = parse(+ListOfWords, +GoalFormula)
%
% true if there are lexical entries for each of the words in ListOfWords
% which allow us to derive GoalFormula; lexical entries for the Displacement
% calculus or hybrid type-logical grammars are translated into first-order
% linear logic beforehand.

parse(ListOfWords, Goal0) :-
	initialisation,
	retractall('$LOOKUP'(_)),
	assert('$LOOKUP'(0)),
	lookup(ListOfWords, Formulas, LexSem, Goal0, Goal),
	/* update lookup statistics */
	'$LOOKUP'(N0),
	N is N0 + 1,
	retractall('$LOOKUP'(_)),
	assert('$LOOKUP'(N)),
        format_error('~N= Lookup ~D~n', [N]),
	prove0(Formulas, Goal, LexSem),
	fail.
parse(_, _) :-
        format_error('~N= Done!~2n================~n=  statistics  =~n================~n', []),	
	'$LOOKUP'(L),
	write_lookups(L),
        final_statistics.
% Commented out since this is inconsistent with the behavior of parse_all
%	/* succeed only if at least one proof was found */
%        user:'$PROOFS'(N),
%        N > 0.

% = prove(+Antecedent, +GoalFormula)
%
% true if the sequent Antecedent |- GoalFormula is derivable in
% first-order linear logic.
%
% prove/2 and prove/3 are wrapper predicates: they do initialisation,
% call prove0/3, enumerate solutions by a failure-drive loop and
% outputs some proof statistics.
%
% The LaTeX headers and footers are also output here (when LaTeX
% output is generated).

prove(Antecedent, Goal) :-
	prove(Antecedent, Goal, []).

% = prove(+Antecedent, +GoalFormula, +LexicalSubstitutions)

prove(Antecedent, Goal, LexSem) :-
	initialisation,
	prove0(Antecedent, Goal, LexSem),
	fail.
prove(_, _, _) :-
        format_error('~N= Done!~2n================~n=  statistics  =~n================~n', []),	
	final_statistics.

initialisation :-
	/* LaTeX output */
	graph_header,
	proof_header,
	/* reset counters */
	retractall('$PROOFS'(_, _)),
	assert('$PROOFS'(0, [])),
	retractall('$AXIOMS'(_)),
	assert('$AXIOMS'(0)).

final_statistics :-
	/* print final statistics and generate pdf files */
	'$AXIOMS'(A),
	'$PROOFS'(N, _),
	write_proofs(N),
	write_axioms(A),
	/* LaTeX graphs */
	graph_footer(N),
	/* LaTeX proofs */
	proof_footer.

% prove0/2, prove0/3 is a second wrapper, doing sequent unfolding
% calling the recursive prover predicate prove1/3, doing lexical
% subsitution and outputing semantics
% and proofs.

prove0(Antecedent, Goal) :-
	prove0(Antecedent, Goal, []).

prove0(Antecedent, Goal, LexSem) :-
        unfold_sequent(Antecedent, Goal, Roots, Graph, Sem0, Stats),
	portray_sequent_statistics(Stats),
	format_error('Parsing: ', []),
	/* keep a copy of the initial graph (before any unificiations) for later proof generation */
	copy_term(Graph, GraphCopy),
	portray_graph(Graph),
        prove1(Graph, Roots, Trace),
	/* proof found */
	'$PROOFS'(N0, SemList),
	N is N0 + 1,
	/* generate semantics */
	substitute_sem(LexSem, Sem0, Sem1),
	reduce_sem(Sem1, Sem),
	format_info('~N= Semantics ~w: ~p~n', [N,Sem]),
	latex_semantics(Sem),
	/* update proof statistics */
	retractall('$PROOFS'(_, _)),
	assert('$PROOFS'(N, [Sem|SemList])),
	/* generate a LaTeX proof */
	/* generate_proof/2 outputs Displacement, hybrid, natural deduction and sequent proofs */
	/* if you are only interested in one type of proof, replace generate_proof/2 by one of: */
	/* generate_hybrid_proof/2, (natural deduction proofs in hybrid type-logical grammar) */
        /* generate_displacement_proof/2, (natural deduction proofs for the Displacement calculus) */
	/* generate_sequent_proof/2, (sequent proofs in first-order linear logic) */
	/* generate_natural_deduction_proof/2, (natural deduction proof in first-order linear logic) */
	/* generate_nd_proof/2, (natural deduction proof in first-order linear logic with implicit antecedents) */
%	generate_hybrid_proof(GraphCopy, Trace).
%	generate_displacement_proof(GraphCopy, Trace).
	generate_proof(GraphCopy, Trace).

% = first_proof
%
% a version of the prover predicate which only finds the first proof.

first_proof(Antecedent, Goal, Sem) :-
	first_proof(Antecedent, Goal, [], Sem).

first_proof(Antecedent, Goal, LexSem, Sem) :-
	( is_stream(graph) -> true ; open_null_stream(Stream), set_stream(Stream, alias(graph))),
        unfold_sequent(Antecedent, Goal, Roots, Graph, Sem0, _Stats),
        prove1(Graph, Roots, _Trace),
	!,
	substitute_sem(LexSem, Sem0, Sem1),
	reduce_sem(Sem1, Sem).


% = first_parse(+ListOfWords, +GoalFormula)
%
% a version of parse which finds only the first solution.

first_parse(ListOfWords, Goal0) :-
	initialisation,
	retractall('$LOOKUP'(_)),
	assert('$LOOKUP'(0)),
	lookup(ListOfWords, Formulas, LexSem, Goal0, Goal),
	/* update lookup statistics */
	'$LOOKUP'(N0),
	N is N0 + 1,
	retractall('$LOOKUP'(_)),
	assert('$LOOKUP'(N)),
        format_error('~N= Lookup ~D~n', [N]),
	prove0(Formulas, Goal, LexSem),
	!,
        format_error('~N= Done!~2n================~n=  statistics  =~n================~n', []),	
	'$LOOKUP'(L),
	write_lookups(L),
        final_statistics.

% = prove1(+Graph, +Roots, -Trace)
%
% true if Graph (with root nodes Roots) is a proof net, as justified by
% the axioms/contractions in Trace.
%
% the prove1 predicate operates by identifying, at each recursive step, a pair
% of atomic formulas and contracting the input graph (verifying for acyclicity
% and connectedness)

prove1([vertex(_, [], _, [])], _, []) :-
	/* a single vertex with atoms or links, we have succeeded ! */
        format_error('~N= Proof found!~n', []),
        !.
prove1(Graph0, Roots0, [ax(N0,AtV0,AtO0,N1,AtV1,AtO1)|Rest0]) :-
	/* forced choice for positive atom, using first-found from the best */
	/* choices returned by the dancing links algorithm */
	/* TODO: evaluate different tie-breakers here as a heuristic for selecting */
	/* atoms likely to fail quickly */
	compute_axioms(Roots0, Graph0, _ATree, Axioms),
	select_first(Axioms, Graph0, tuple(AtV1,AtO1,N1,Choices)),
        select(vertex(N1, [A|As0], FVs0, Ps0), Graph0, Graph1),
        select(pos(At,AtV1,AtO1,X,Vars), [A|As0], As),
	!,
	/* enumerate choices for negative atom */
%	select_manual(Choices, Graph1, tuple(AtV0,AtO0,N0)),
	member(tuple(AtV0,AtO0,N0), Choices),
	select(vertex(N0, [B|Bs0], FVs1, Ps1), Graph1, Graph2),
	select(neg(At,AtV0,AtO0,X,Vars), [B|Bs0], Bs),
        /* verify no cycle has been created */
        \+ cyclic(Ps0, Graph2, N0),
        \+ cyclic(Ps1, Graph2, N1),
	'$AXIOMS'(Ax0),
	Ax is Ax0 + 1,
	retractall('$AXIOMS'(_)),
	assert('$AXIOMS'(Ax)),
	/* identify nodes of positive and negative atom */
	/* (and do the necessary replacements) */
	append(As, Bs, Cs),
	append(Ps0, Ps1, Ps),
	merge_fvs(FVs0, FVs1, FVs),
	replace_graph(Graph2, Ps, N0, N1, Graph3, Rs),
	update_roots_axiom(Roots0, N0, N1, Roots1),
	Graph4 = [vertex(N1,Cs,FVs,Rs)|Graph3],
        portray_graph(Graph4),
	/* perform Danos-style graph contractions */
	contract(Graph4, Graph, Rest0, Rest, Roots1, Roots),
	/* verify the result is (at least potentially) connected */
	connected(Graph),
	mark_progress(Graph),
	prove1(Graph, Roots, Rest).

% =======================================
% =              Proof nets             =
% =======================================

% = axiom selection predicates

% default: select first

select_first([Item|_], _, Item).

select_random(List, _, Item) :-
	random_member(Item, List).
	
select_manual(List, Graph, Item) :-
	portray_atoms(List, Graph, 0, Len),
	Len > 0,
	read_integer_max(Len, ItemNo),
	nth1(ItemNo, List, Item).


read_integer_max(Max, Int) :-
	read(Int0),
   (
	integer(Int0),
	Int0 =< Max
   ->
        Int = Int0
   ;
        read_integer_max(Max, Int)
   ).
    
portray_atoms([], _, N, N) :-
	format('Selection?', []).
portray_atoms([A|As], Graph, N0, N) :-
	N1 is N0 + 1,
	portray_atom(A, N1, Graph),
	portray_atoms(As, Graph, N1, N).

portray_atom(tuple(AtV1,AtO1,N1), N, Graph) :-
        select(vertex(N1, [A|As0], _FVs0, _Ps0), Graph, _),
        select(neg(At,AtV1,AtO1,_X,Vars), [A|As0], _),
	!,
	format('[~d] -~w(~@) ~d-~d~n', [N, At, portray_vars(Vars), AtV1, AtO1]).
portray_atom(tuple(AtV1,AtO1,N1,Choices), N, Graph) :-
	length(Choices, Len),
	Len > 0,
        select(vertex(N1, [A|As0], _FVs0, _Ps0), Graph, _),
        select(pos(At,AtV1,AtO1,_X,Vars), [A|As0], _),
	!,
	format('[~d] +~w(~@) ~d-~d [~d]~n', [N, At, portray_vars(Vars), AtV1, AtO1, Len]).

portray_vars([]).
portray_vars([V|Vs]) :-
	portray_vars(Vs, V).
portray_vars([], V) :-
	print(V).
portray_vars([V|Vs], V0) :-
	format('~p, ~@', [V0, portray_vars(Vs, V)]).
	

% = contract(+InGraph,-OutGraph, TraceDL, RootNodeAcc)
%
% perform all valid contractions on InGraph producing OutGraph;
% these are Danos-style contractions, performed in a first-found
% search.
%
% In addition, it returns a Trace of the contractions performed (as
% a difference list) and updates the root nodes of the graph (using
% an accumulator).

contract(Graph0, Graph, L0, L, R0, R) :-
        contract1(Graph0, Graph1, L0, L1, R0, R1),
        portray_graph(Graph1),
        !,
        contract(Graph1, Graph, L1, L, R1, R).
contract(Graph, Graph, L, L, R, R).

% par contraction
contract1(Graph0, [vertex(N1,Cs,FVs,Rs)|Graph], [N0-par(N1)|Rest], Rest, Roots0, Roots) :-
        select(vertex(N0, As, FVsA, Ps0), Graph0, Graph1),
        select(par(N1, N1), Ps0, Ps),
	select(vertex(N1, Bs, FVsB, Qs), Graph1, Graph2),
	\+ cyclic(Qs, Graph2, N0),
	!,
	append(As, Bs, Cs),
	append(Ps, Qs, Rs0),
	merge_fvs(FVsA, FVsB, FVs),
	replace_graph(Graph2, Rs0, N0, N1, Graph, Rs),
        update_roots_contraction(Roots0, Graph, N0, N1, Roots).
% forall contraction
contract1(Graph0, [vertex(N1,Cs,FVs,Rs)|Graph], [N0-univ(U,N1)|Rest], Rest, Roots0, Roots) :-
        select(vertex(N0, As, FVsA, Ps0), Graph0, Graph1),
        select(univ(U, N1), Ps0, Ps),
	select(vertex(N1, Bs, FVsB, Qs), Graph1, Graph2),
	no_occurrences(Bs, Qs, Graph2, U),
	no_occurrences1(FVsA, U),
	no_occurrences(Graph2, U),
	!,
	append(As, Bs, Cs),
	append(Ps, Qs, Rs0),
	merge_fvs(FVsA, FVsB, FVs),
	replace_graph(Graph2, Rs0, N0, N1, Graph, Rs),
        update_roots_contraction(Roots0, Graph, N0, N1, Roots).

% = no_occurrences
%
% verify that the module above the forall link has no remaining occurrences of the eigenvariable
% of the link in atomic formulas
%
% TODO: this can surely be done in a smarter way. For example, it only makes sense to try and
% contract a forall link after all atomic formulas containing its eigenvariable have been
% connected.

no_occurrences([], Ps, G, V) :-
	no_occurrences_pars(Ps, G, V).
no_occurrences([A|As], Ps, G, V) :-
	no_atom_occurrences(A, V),
	no_occurrences(As, Ps, G, V).

no_atom_occurrences(pos(_, _, _, _, Vs), V) :-
	no_varlist_occurrences(Vs, V).
no_atom_occurrences(neg(_, _, _, _, Vs), V) :-
	no_varlist_occurrences(Vs, V).

no_varlist_occurrences([], _).
no_varlist_occurrences([V|Vs], W) :-
	V \== var(W),
	no_varlist_occurrences(Vs, W).

no_occurrences_pars([], _, _).
no_occurrences_pars([P|Ps], G0, V) :-
	no_occurrences_par(P, V, G0, G),
	no_occurrences_pars(Ps, G, V).

no_occurrences_par(univ(_,N1), V, G0, G) :-
    (
	selectchk(vertex(N1,Cs,_,Rs), G0, G)
    ->			 
	no_occurrences(Cs, Rs, G, V)
    ;
        G = G0
    ).
no_occurrences_par(par(N1,N2), V, G0, G) :-
    (
	selectchk(vertex(N1,Cs,_,Rs), G0, G1)
    ->			 
	no_occurrences(Cs, Rs, G1, V)
    ;
        G1 = G0
    ),
    (
	selectchk(vertex(N2,Ds,_,Ss), G1, G)
    ->			 
	no_occurrences(Ds, Ss, G, V)
    ;
        G = G1
    ).


% test for cyclicity
% G2 contains unvisited nodes
% P contains paths from current node
% N is the node to reach for a cycle.

cyclic([P|_], Graph, N) :-
    cyclic1(P, Graph, N).
cyclic([_|Ps], Graph, N) :-
    cyclic(Ps, Graph, N).

cyclic1(par(M,P), Graph0, N) :-
    (
       N =:= M
    ;
       N =:= P
    ;
       select(vertex(M,_,_,Ps), Graph0, Graph),
       cyclic(Ps, Graph, N)
    ;
       P \== M,
       select(vertex(P,_,_,Ps), Graph0, Graph),
       cyclic(Ps, Graph, N)
    ).
cyclic1(univ(_,M), Graph0, N) :-
    (
       N =:= M
     ;
       select(vertex(M,_,_,Ps), Graph0, Graph),
       cyclic(Ps, Graph, N)
    ).        

% = connected(+Graph)
%
% true if Graph is connected or at least can be made connected
% by vertex identifications (corresponding to axioms)
%
% This test presupposes that all possible contractions for the
% current graph have been performed.

connected([V|Vs]) :-
   (
       Vs = []
   ->
       /* a single-node graph is connected */
       true
   ;
       connected1([V|Vs])
   ).

connected1([]).
connected1([vertex(_,As,_,Ps)|Vs]) :-
    (
        As = []
    ->  /* in a multiple-node graph, if a node has no */
        /* atoms, it must have a link */
	/* In other words, in a fully contracted graph, */
	/* a node cannot have both As and Ps empty */    
        Ps = [_|_]
    ;
        true
    ),
    connected1(Vs).

% = no_occurrences(+Graph, +VarNum)
%
% walks through +Graph and checks that none of its vertices
% has the variable +VarNum in their list of free variables
% (this is the condition on the forall contraction)

no_occurrences([], _).
no_occurrences([vertex(_, _, FVs, _)|Rest], U) :-
        no_occurrences1(FVs, U),
        no_occurrences(Rest, U).

no_occurrences1([], _).
no_occurrences1([V|Vs], U) :-
        var(U) \== V,
        no_occurrences1(Vs, U).

% =======================================
% =      Formula/sequent unfolding      =
% =======================================

% transforms sequents, antecedents and (polarized) formulas into graphs

% = unfold_sequent(+ListOfFormulas, +GoalFormula, -RootNodes, -Vertices, -Semantics) 
%
% unfold the sequent ListOfFormulas |- GoalFormula into the graph Vertices, keeping
% track of the RootNodes (of the resulting forest) and the term representing the
% Semantics (after all axiom links have been performed).
%
% In addition, for later proof generation, all nodes in the initial graph will have
% their node number, polarity and the formula corresponding to this node stored by
% means of an asserted
%
% node_formula(NodeNumber, Polarity, Formula)
%
% declaration (with Polarity one of pos/neg).
	    
unfold_sequent(List, Goal, Roots, Vs0, Sem, Stats) :-
        retractall(node_formula(_,_,_)),
	init_stats(Stats0),
	unfold_antecedent(List, 0, _W, 0, N0, 0, M, Roots0, Vs0, [vertex(N0,As,FVsG,Es)|Vs1], Stats0, Stats1),
	N is N0 + 1,
	append(Roots0, [N0], Roots),
	number_subformulas_pos(Goal, N0, N, _, _-NGoal),
        assert(node_formula(N0, pos, NGoal)),
	free_vars_p(Goal, FVsG),
	unfold_pos(NGoal, Sem, M, _, As, [], Es, [], Vs1, [], Stats1, Stats).

unfold_antecedent([], W, W, N, N, M, M, [], Vs, Vs, Stats, Stats).
unfold_antecedent([F|Fs], W0, W, N0, N, M0, M, [N0|Rs], [vertex(N0,As,FVsF,Es)|Vs0], Vs, Stats0, Stats) :-
        N1 is N0 + 1,
        W1 is W0 + 1,
	free_vars_n(F, FVsF),
	number_subformulas_neg(F, N0, N1, N2, _-NF),
        assert(node_formula(N0, neg, NF)),
	unfold_neg(NF, '$VAR'(W0), M0, M1, As, [], Es, [], Vs0, Vs1, Stats0, Stats1),
	unfold_antecedent(Fs, W1, W, N2, N, M1, M, Rs, Vs1, Vs, Stats1, Stats).

%= unfold_neg(+Formula, Sem, VertexNo, VarNoAcc, AtomsDL, AtomsDL, EdgesDL, VerticesDL)

unfold_neg(at(A,C,N,Vars), X, M, M, [neg(A,C,N,X,Vars)|As], As, Es, Es, Vs, Vs, Stats0, Stats) :-
	add_neg_atom(Stats0, Stats).
unfold_neg(forall(_,_-A), X, M0, M, As0, As, Es0, Es, Vs0, Vs, Stats0, Stats) :-
	add_unary_tensor(Stats0, Stats1),
	unfold_neg(A, X, M0, M, As0, As, Es0, Es, Vs0, Vs, Stats1, Stats).
unfold_neg(exists(var(M0),N-A), X, M0, M, As, As, [univ(M0,N)|Es], Es, [vertex(N,Bs,FVsA,Fs)|Vs0], Vs, Stats0, Stats) :-
        assert(node_formula(N, neg, A)),
        free_vars_n(A, FVsA),
	M1 is M0 + 1,
	add_unary_par(Stats0, Stats1),
	unfold_neg(A, X, M1, M, Bs, [], Fs, [], Vs0, Vs, Stats1, Stats).
unfold_neg(p(N0-A,N1-B), X, M0, M, As, As, [par(N0,N1)|Es], Es, [vertex(N0,Bs,FVsA,Fs),vertex(N1,Cs,FVsB,Gs)|Vs0], Vs, Stats0, Stats) :-
        assert(node_formula(N0, neg, A)),
        assert(node_formula(N1, neg, B)),
        free_vars_n(A, FVsA),
        free_vars_n(B, FVsB),
	add_binary_par(Stats0, Stats1),
	unfold_neg(A, pi1(X), M0, M1, Bs, [], Fs, [], Vs0, Vs1, Stats1, Stats2),
	unfold_neg(B, pi2(X), M1, M, Cs, [], Gs, [], Vs1, Vs, Stats2, Stats).
unfold_neg(impl(_-A,_-B), X, M0, M, As0, As, Es0, Es, Vs0, Vs, Stats0, Stats) :-
	add_binary_tensor(Stats0, Stats1),
	unfold_pos(A, Y, M0, M1, As0, As1, Es0, Es1, Vs0, Vs1, Stats1, Stats2),
	unfold_neg(B, appl(X,Y), M1, M, As1, As, Es1, Es, Vs1, Vs, Stats2, Stats).

%= unfold_pos(+Formula, Sem, VertexNo, VarNoAcc, AtomsDL, AtomsDL, EdgesDL, VerticesDL)

unfold_pos(at(A,C,N,Vars), X, M, M, [pos(A,C,N,X,Vars)|As], As, Es, Es, Vs, Vs, Stats0, Stats) :-
	add_pos_atom(Stats0, Stats).
unfold_pos(forall(var(M0),N0-A), X, M0, M, As, As, [univ(M0,N0)|Es], Es, [vertex(N0,Bs,FVsA,Fs)|Vs0], Vs, Stats0, Stats) :-
        assert(node_formula(N0, pos, A)),
        free_vars_p(A, FVsA),
	M1 is M0 + 1,
	add_unary_par(Stats0, Stats1),
	unfold_pos(A, X, M1, M, Bs, [], Fs, [], Vs0, Vs, Stats1, Stats).
unfold_pos(exists(_,_-A), X, M0, M, As0, As, Es0, Es, Vs0, Vs, Stats0, Stats) :-
	add_unary_tensor(Stats0, Stats1),
	unfold_pos(A, X, M0, M, As0, As, Es0, Es, Vs0, Vs, Stats1, Stats).
unfold_pos(p(_-A,_-B), pair(X,Y), M0, M, As0, As, Es0, Es, Vs0, Vs, Stats0, Stats) :-
	add_binary_tensor(Stats0, Stats1),
	unfold_pos(A, X, M0, M1, As0, As1, Es0, Es1, Vs0, Vs1, Stats1, Stats2),
	unfold_pos(B, Y, M1, M, As1, As, Es1, Es, Vs1, Vs, Stats2, Stats).
unfold_pos(impl(N0-A,N1-B), lambda(X,Y), M0, M, As, As, [par(N0,N1)|Es], Es, [vertex(N0,Bs,FVsA,Fs),vertex(N1,Cs,FVsB,Gs)|Vs0], Vs, Stats0, Stats) :-
        assert(node_formula(N0, neg, A)),
        assert(node_formula(N1, pos, B)),
        free_vars_n(A, FVsA),
        free_vars_p(B, FVsB),
	add_binary_par(Stats0, Stats1),
	unfold_neg(A, X, M0, M1, Bs, [], Fs, [], Vs0, Vs1, Stats1, Stats2),
	unfold_pos(B, Y, M1, M, Cs, [], Gs, [], Vs1, Vs, Stats2, Stats).

% = number_subformulas_neg(+Formula, +CurrentNodeNumber, +NextNodeNumberIn, -NextNodeNumberOut, -NumberFormula)
%
% assigns node numbers to all subformulas of Formula, allowing us to designate
% all (sub-)formula occurrences in a sequent by a unique node number, and in the
% case of atomic formulas a combination of node-index (where index is a
% left-to-right numbering of the atomic subformulas at a node).

number_subformulas_neg(F, C, N0, N, NF) :-
        number_subformulas_neg(F, C, N0, N, 1, _, NF).

number_subformulas_neg(at(A,Vars), C, N, N, M0, M, C-at(A,C,M0,Vars)) :-
        M is M0 + 1.
number_subformulas_neg(forall(X,A), C, N0, N, M0, M, C-forall(X,NA)) :-
	number_subformulas_neg(A, C, N0, N, M0, M, NA).
number_subformulas_neg(exists(X,A), C, N0, N, M, M, C-exists(X,NA)) :-
	N1 is N0 + 1,
	number_subformulas_neg(A, N0, N1, N, 1, _, NA).
number_subformulas_neg(p(A,B), C, N0, N, M, M, C-p(NA,NB)) :-
	N1 is N0 + 1,
	N2 is N1 + 1,
	number_subformulas_neg(A, N0, N2, N3, 1, _, NA),
	number_subformulas_neg(B, N1, N3, N, 1, _, NB).
number_subformulas_neg(impl(A,B), C, N0, N, M0, M, C-impl(NA,NB)) :-
	number_subformulas_pos(A, C, N0, N1, M0, M1, NA),
	number_subformulas_neg(B, C, N1, N, M1, M, NB).

number_subformulas_pos(F, C, N0, N, NF) :-
        number_subformulas_pos(F, C, N0, N, 1, _, NF).

number_subformulas_pos(at(A,Vars), C, N, N, M0, M, C-at(A,C,M0,Vars)) :-
	M is M0 + 1.
number_subformulas_pos(forall(X,A), C, N0, N, M, M, C-forall(X,NA)) :-
	N1 is N0 + 1,
	number_subformulas_pos(A, N0, N1, N, 1, _, NA).
number_subformulas_pos(exists(X,A), C, N0, N, M0, M, C-exists(X,NA)) :-
	number_subformulas_pos(A, C, N0, N, M0, M, NA).
number_subformulas_pos(p(A,B), C, N0, N, M0, M, C-p(NA,NB)) :-
	number_subformulas_pos(A, C, N0, N1, M0, M1, NA),
	number_subformulas_pos(B, C, N1, N, M1, M, NB).
number_subformulas_pos(impl(A,B), C, N0, N, M, M, C-impl(NA,NB)) :-
	N1 is N0 + 1,
	N2 is N1 + 1,	
	number_subformulas_neg(A, N0, N2, N3, 1, _, NA),
	number_subformulas_pos(B, N1, N3, N, 1, _, NB).


% =======================================
% =             Input/output            =
% =======================================

mark_progress(G) :-
	length(G, L),
	format_error('~D.', [L]),
        flush_output(user_error).
	

% = sequent statistics predicates

init_stats(stats(0,0,0,0,0,0)).

add_neg_atom(stats(A0,B,C,D,E,F), stats(A,B,C,D,E,F)) :-
	A is A0 + 1.

add_pos_atom(stats(A,B0,C,D,E,F), stats(A,B,C,D,E,F)) :-
	B is B0 + 1.

add_unary_tensor(stats(A,B,C0,D,E,F), stats(A,B,C,D,E,F)) :-
	C is C0 + 1.

add_unary_par(stats(A,B,C,D0,E,F), stats(A,B,C,D,E,F)) :-
	D is D0 + 1.

add_binary_tensor(stats(A,B,C,D,E0,F), stats(A,B,C,D,E,F)) :-
	E is E0 + 1.

add_binary_par(stats(A,B,C,D,E,F0), stats(A,B,C,D,E,F)) :-
	F is F0 + 1.


portray_sequent_statistics(stats(A,B,C,D,E,F)) :-
   (
	A =:= B
    ->
	format_error('~NAtoms:   ~|~t~D~4+~n', [A])
    ;
        format_error('~NAtoms:   ~|~t-~D~4+  ~|~t+~D~4+~n', [A,B])
    ),
        format_error('Unary : T~|~t~D~4+ P~|~t~D~4+~n', [C,D]),
	format_error('Binary: T~|~t~D~4+ P~|~t~D~4+~n', [E,F]).




% = print_trace(+Stream, +List).
%
% output proof trace in List to Stream

print_trace(Stream, [A|As]) :-
        format(Stream, '~n= Proof trace =~n', []),
        print_trace(As, A, Stream).

print_trace([], A, Stream) :-
        format(Stream, '~p~n= End of trace =~2n', [A]).
print_trace([B|Bs], A, Stream) :-
        format(Stream, '~p~n', [A]),
        print_trace(Bs, B, Stream).


% = print_list(+List)
%
% output List to user_error

print_list([]).
print_list([A|As]) :-
	format_error('~p~n', [A]),
	print_list(As).

% = write_proof(P)
%
% output a message stating P proofs have been found

write_proofs(P) :-
   (
       P =:= 0
   ->
       format_error('No proofs found!~n', [])
   ;
       P =:= 1
   ->
       format_error('1 proof found.~n', [])
   ;
       format_error('~D proofs found.~n', [P])
   ).

% = write_axioms(A)
%
% output a message stating A axioms have been performed

write_axioms(A) :-
   (
       A =:= 0
   ->
       format_error('No axioms performed!~n', [])
   ;
       A =:= 1
   ->
       format_error('1 axiom performed.~n', [])
   ;
       format_error('~D axioms performed.~n', [A])
   ).

% = write_lookup(L)
%
% output a message stating L lexical lookups have been done

write_lookups(L) :-
   (
       L =:= 0
   ->
       format_error('No lexical lookups!~n', [])
   ;
       L =:= 1
   ->
       format_error('1 lexical lookup.~n', [])
   ;
       format_error('~D lexical lookups.~n', [L])
   ).


% = print_solutions(+ExampleNos, +NumSolutions)
%
% display for each example in ExampleNo its number of solutions
% in NumSolutions and print an overview of the number of failed,
% succeeded and total examples.

print_solutions(L, NS) :-
	open('parse_log.txt', write, Stream, []), 
	format(Stream, 'SentNo Solutions~n', []),
	format_error('SentNo Solutions~n', []),
	print_solutions(L, Stream, 0, 0, NS),
	close(Stream).
print_solutions([], Stream, S, F, []) :-
	Total is S + F,
	format(Stream, '~nTotal sentences :~|~t~d~4+~nSucceeded       :~|~t~d~4+~nFailed          :~|~t~d~4+~n', [Total, S, F]),
	format_error('~nTotal sentences :~|~t~d~4+~nSucceeded       :~|~t~d~4+~nFailed          :~|~t~d~4+~n', [Total, S, F]).
print_solutions([N|Ns], Stream, S0, F0, [P-SemList|Ps]) :-
	format(Stream, '~|~t~d~6+ ~|~t~d~9+', [N,P]),
	format_error('~|~t~d~6+ ~|~t~d~9+', [N,P]),
    (
	P =:= 0
    ->
        format(Stream, ' * ~p~n', [SemList]),
        format_error(' * ~p~n', [SemList]),
	F is F0 + 1,
	S = S0
    ;
        format(Stream, '   ~p~n', [SemList]),
        format_error('   ~p~n', [SemList]),
        S is S0 + 1,
        F = F0
    ),
	print_solutions(Ns, Stream, S, F, Ps).

% = some test predicates

test :-
        /* this should fail ! */
	prove([forall(X,exists(Y,at(f,[X,Y])))], exists(V,forall(W,at(f,[W,V])))).
test0 :-
	prove([exists(X,forall(Y,at(f,[X,Y])))], forall(V,exists(W,at(f,[W,V])))).

test1 :-
        translate_lambek(p(dr(at(np),at(n)),at(n)), [0,1], F),
        prove([F], at(np, [0,1])).

test2 :-
	prove([at(np,[0,1]),forall(X,impl(at(np,[X,1]),at(s,[X,2])))], at(s,[0,2])).

test3 :-
	prove([forall(Y,forall(Z,impl(impl(at(np,[0,1]),at(s,[Y,Z])),at(s,[Y,Z])))),forall(X,impl(at(np,[X,1]),at(s,[X,2])))], at(s,[0,2])).

test4 :-
        translate_hybrid(at(np), lambda(X,appl(john,X)), john, 0, 1, John),
	translate_hybrid(h(h(at(s),at(np)),at(np)), lambda(P,lambda(Q,lambda(Z,appl(Q,appl(loves,appl(P,Z)))))), loves, 1, 2, Loves),
        translate_hybrid(at(np), lambda(V,appl(mary,V)), mary, 2, 3, Mary),
	prove([John, Loves, Mary], at(s, [0,3])).

test5 :-
        translate_hybrid(at(np), lambda(X,appl(john,X)), john, 0, 1, John),
	translate_hybrid(h(h(at(s),at(np)),at(s)), lambda(P,lambda(Q,lambda(Z,appl(Q,appl(believes,appl(P,Z)))))), believes, 1, 2, Believes),
	translate_hybrid(h(at(s),h(at(s),at(np))), lambda(VP,lambda(Z,appl(appl(VP,someone),Z))), someone, 2, 3, Someone),
	translate_hybrid(h(at(s),at(np)), lambda(S,lambda(Z1,appl(S,appl(left,Z1)))), left, 3, 4, Left),
	prove([John, Believes, Someone, Left], at(s, [0,4])).

% = I need a better axiom selection strategy

% succeeds, but proof generation fails
test6 :-
	translate_displacement(at(np), [0,1], John),
	translate_displacement(dl(at(np),at(s)), [1,2], Left),
	translate_displacement(dr(dl(dl(at(np),at(s)),dl(at(np),at(s))),at(s)), [2,3], Before),
	translate_displacement(at(np), [3,4], Mary),
	translate_displacement(dl(dr(dr(>,dl(at(np),at(s)),dl(at(np),at(s))),dl(at(np),at(s))),dr(>,dl(at(np),at(s)),dl(at(np),at(s)))), [4,5], Did),
	prove([John,Left,Before,Mary,Did], at(s,[0,5])).

% fails (verify!)
test7 :-
        translate_hybrid(at(np), lambda(X,appl(john,X)), john, 0, 1, John),
	translate_hybrid(dr(dl(at(np),at(s)),at(np)), lambda(Y,appl(studies,Y)), studies, 1, 2, Studies),
        translate_hybrid(at(np), lambda(Z,appl(logic,Z)), logic, 2, 3, Logic),
	translate_hybrid(h(h(h(at(s),dr(dl(at(np),at(s)),at(np))),h(at(s),dr(dl(at(np),at(s)),at(np)))),h(at(s),dr(dl(at(np),at(s)),at(np)))),
			 lambda(STV2,lambda(STV1,lambda(TV,lambda(V,appl(appl(STV1,TV),appl(and,appl(appl(STV2,lambda(W,W)),V))))))), and, 3, 4, And),
        translate_hybrid(at(np), lambda(X1,appl(charles,X1)), charles, 4, 5, Charles),
        translate_hybrid(at(np), lambda(Z1,appl(phonetics,Z1)), phonetics, 5, 6, Phonetics),
	prove([John, Studies, Logic, And, Charles, Phonetics], at(s,[0,6])).

%

test_eta :-
	prove([exists(Y,p(at(a,[Y]),at(b,[Y])))], exists(Y,p(at(a,[Y]),at(b,[Y])))).
	
% = test translations

test_d1(F) :-
	/* generalized quantifier */
	translate_displacement(dl(>,dr(>,at(s),at(np)),at(s)), [1,2], F).
test_d2(F) :-
	/* did */
	translate_displacement(dl(dr(dr(>,at(vp),at(vp)),at(vp)),dr(>,at(vp),at(vp))), [4,5], F).
test_d3(F) :-
	/* himself */
	translate_displacement(dl(<,dr(<,dr(>,at(vp),at(np)),at(np)),dr(>,at(vp),at(np))), [3,4], F).

test_h1(F) :-
	translate_hybrid(h(at(s),at(np)), lambda(P,lambda(Z,appl(P,appl(walks,Z)))), walks, 1, 2, F).
test_h2(F) :-
	translate_hybrid(h(at(s),h(at(s),at(np))), lambda(P,lambda(Z,appl(appl(P,everyone),Z))), everyone, 0, 1, F).

% = generate exhaustive test file	


% = control over messages
%
% use output_error(0) to suppress diagnostic messages

:- dynamic output_error/1, output_info/1.

% output_error(0) % suppress diagnostic messages
output_error(1). % print diagnostic messages
output_info(1).

format_error(_, _) :-
	output_error(0),
	!.
format_error(X, Y) :-
	format(user_error, X, Y).


format_info(_, _) :-
	output_info(0),
	!.
format_info(X, Y) :-
	format(user_output, X, Y).

back to top