236 lines
6.1 KiB
Plaintext
236 lines
6.1 KiB
Plaintext
|
|
||
|
%:- style_check(all).
|
||
|
|
||
|
:- module(viterbi, [viterbi/4]).
|
||
|
|
||
|
:- use_module(library(lists),
|
||
|
[nth/3,
|
||
|
member/2]).
|
||
|
|
||
|
:- use_module(library(assoc)).
|
||
|
|
||
|
:- use_module(library(dgraphs)).
|
||
|
|
||
|
:- use_module(library(matrix)).
|
||
|
|
||
|
:- use_module(library(clpbn), []).
|
||
|
|
||
|
:- ensure_loaded(library('clpbn/hmm')).
|
||
|
|
||
|
:- use_module(library('clpbn/dists'), [
|
||
|
get_dist_params/2]).
|
||
|
|
||
|
:- meta_predicate viterbi(:,:,+,-).
|
||
|
|
||
|
|
||
|
viterbi(Start,End,String,Trace) :-
|
||
|
init_hmm,
|
||
|
Start,
|
||
|
mk_graph(NOfNodes, Map, ViterbiCode),
|
||
|
compile_trace(String, Emissions),
|
||
|
get_id(Start, Map, SI),
|
||
|
get_id(End, Map, EI),
|
||
|
% add a random symbol in front (for the c/1 state).
|
||
|
compiled_viterbi(NOfNodes, SI, ViterbiCode, Emissions, Dump, L),
|
||
|
backtrace(Dump, EI, Map, L, Trace).
|
||
|
|
||
|
state_from_goal(_:Start,S) :-
|
||
|
state_from_goal(Start,S).
|
||
|
state_from_goal(Start,S) :-
|
||
|
functor(Start, N, Ar),
|
||
|
% get rid of position and random var
|
||
|
NAr is Ar-2,
|
||
|
functor(S, N, NAr).
|
||
|
|
||
|
|
||
|
mk_graph(NOfNodes, Map, ViterbiCode) :-
|
||
|
attributes:all_attvars(Vars0),
|
||
|
empty_assoc(KeyMap0),
|
||
|
get_graph(Vars0, Nodes, Edges, KeyMap0, KeyMap),
|
||
|
dgraph_new(G0),
|
||
|
dgraph_add_vertices(G0, Nodes, G1),
|
||
|
dgraph_add_edges(G1, Edges, G2),
|
||
|
dgraph_top_sort(G2, SortedNodes),
|
||
|
compile_viterbi(SortedNodes, KeyMap, NOfNodes, Map, ViterbiCode).
|
||
|
|
||
|
get_graph([V|Vs], [NKey|Keys], EdgesF, KeyMap0, KeyMap) :-
|
||
|
clpbn:get_atts(V,[key(Key), dist(Id,Parents)]),
|
||
|
( Key =.. [N,2|More] ; Key = s(0), N=s, More=[] ), !,
|
||
|
NKey =.. [N|More],
|
||
|
fetch_edges(Parents, NKey, EdgesF, Edges0, PKeys),
|
||
|
get_emission(V, Key, EmissionProb),
|
||
|
put_assoc(NKey,KeyMap0,nodeinfo(_,Id,EmissionProb,PKeys),KeyMapI),
|
||
|
get_graph(Vs, Keys, Edges0, KeyMapI, KeyMap).
|
||
|
get_graph([_|Vs], Keys, Edges, KeyMap0, KeyMap) :-
|
||
|
get_graph(Vs, Keys, Edges, KeyMap0, KeyMap).
|
||
|
get_graph([], [], [], KeyMap, KeyMap).
|
||
|
|
||
|
get_emission(V, Key, EmissionProbs) :-
|
||
|
hmm:get_atts(V,[emission(_)]), !,
|
||
|
user:emission_cpt(Key, EmissionProbs).
|
||
|
get_emission(_, _, []).
|
||
|
|
||
|
fetch_edges([V|Parents], Key0, EdgesF, Edges0, [Slice-AKey|PKeys]) :-
|
||
|
var(V), !,
|
||
|
clpbn:get_atts(V,[key(Key)]),
|
||
|
abstract_key(Key, AKey, Slice),
|
||
|
(
|
||
|
Slice < 3
|
||
|
->
|
||
|
EdgesF = [Key0-AKey|EdgesI]
|
||
|
;
|
||
|
EdgesF = EdgesI
|
||
|
),
|
||
|
fetch_edges(Parents, Key0, EdgesI, Edges0, PKeys).
|
||
|
fetch_edges([Key|Parents], Key0, EdgesF, Edges0, [Slice-AKey|PKeys]) :-
|
||
|
abstract_key(Key, AKey, Slice),
|
||
|
(
|
||
|
Slice < 3
|
||
|
->
|
||
|
EdgesF = [Key0-AKey|EdgesI]
|
||
|
;
|
||
|
EdgesF = EdgesI
|
||
|
),
|
||
|
fetch_edges(Parents, Key0, EdgesI, Edges0, PKeys).
|
||
|
fetch_edges([], _, Edges, Edges, []).
|
||
|
|
||
|
abstract_key(Key, NKey, Slice) :-
|
||
|
Key =.. [N,Slice|More],
|
||
|
NKey =.. [N|More].
|
||
|
|
||
|
|
||
|
compile_viterbi(Keys, KeyMap, Nodes, Map, ViterbiCode) :-
|
||
|
enum_keys(Keys, KeyMap, 0, Nodes, Map),
|
||
|
compile_keys(Keys, KeyMap, ViterbiCode).
|
||
|
|
||
|
% just enumerate keys
|
||
|
enum_keys([], _, I, I, []).
|
||
|
enum_keys([Key|Keys], KeyMap, I0, Nodes, [I0-Key|Map]) :-
|
||
|
get_assoc(Key,KeyMap,nodeinfo(I0,_,_,_)),
|
||
|
I is I0+1,
|
||
|
enum_keys(Keys, KeyMap, I, Nodes, Map).
|
||
|
|
||
|
compile_keys([Key|Keys], KeyMap, ViterbiCodeF) :-
|
||
|
get_assoc(Key,KeyMap,nodeinfo(IKey,Id,Emission,PKeys)),
|
||
|
compile_emission(Emission,IKey,ViterbiCodeF,ViterbiCodeI),
|
||
|
get_dist_params(Id,Probs),
|
||
|
compile_propagation(PKeys,Probs,IKey,KeyMap,ViterbiCodeI,ViterbiCode0),
|
||
|
compile_keys(Keys, KeyMap, ViterbiCode0).
|
||
|
compile_keys([], _, []).
|
||
|
|
||
|
|
||
|
% add a random symbol to the end.
|
||
|
compile_emission([],_) --> !, [].
|
||
|
compile_emission(EmissionTerm,IKey) --> [emit(IKey,EmissionTerm)].
|
||
|
|
||
|
compile_propagation([],[],_,_) --> [].
|
||
|
compile_propagation([0-PKey|Ps], [Prob|Probs], IKey, KeyMap) -->
|
||
|
[prop_same(IKey,Parent,Prob)],
|
||
|
{ get_assoc(PKey,KeyMap,nodeinfo(Parent,_,_,_)) },
|
||
|
compile_propagation(Ps, Probs, IKey, KeyMap).
|
||
|
compile_propagation([2-PKey|Ps], [Prob|Probs], IKey, KeyMap) -->
|
||
|
[prop_same(IKey,Parent,Prob)],
|
||
|
{ get_assoc(PKey,KeyMap,nodeinfo(Parent,_,_,_)) },
|
||
|
compile_propagation(Ps, Probs, IKey, KeyMap).
|
||
|
compile_propagation([3-PKey|Ps], [Prob|Probs], IKey, KeyMap) -->
|
||
|
[prop_next(IKey,Parent,Prob)],
|
||
|
{ get_assoc(PKey,KeyMap,nodeinfo(Parent,_,_,_)) },
|
||
|
compile_propagation(Ps, Probs, IKey, KeyMap).
|
||
|
|
||
|
get_id(_:S, Map, SI) :- !,
|
||
|
get_id(S, Map, SI).
|
||
|
get_id(S, Map, SI) :-
|
||
|
functor(S,N,A),
|
||
|
A2 is A-2,
|
||
|
functor(S2,N,A2),
|
||
|
once(member(SI-S2,Map)).
|
||
|
|
||
|
compile_trace(Trace, Emissions) :-
|
||
|
user:hmm_domain(Domain),
|
||
|
(atom(Domain) ->
|
||
|
hmm:cvt_vals(Domain, Vals)
|
||
|
;
|
||
|
Vals = Domain
|
||
|
),
|
||
|
compile_trace(Trace, Vals, Emissions).
|
||
|
|
||
|
compile_trace([], _, []).
|
||
|
compile_trace([El|Trace], Vals, [N|Emissions]) :-
|
||
|
once(nth(N, Vals, El)),
|
||
|
compile_trace(Trace, Vals, Emissions).
|
||
|
|
||
|
compiled_viterbi(Nodes, S, Commands, Input, Trace, L) :-
|
||
|
length(Input,L),
|
||
|
prolog_flag(min_tagged_integer, Min),
|
||
|
matrix_new_set(ints,[Nodes], Min, Current),
|
||
|
matrix_new_set(ints,[Nodes], Min, Next),
|
||
|
L1 is L+1,
|
||
|
matrix_new(ints,[L1,Nodes], Trace),
|
||
|
matrix_set(Current, [S], 0),
|
||
|
run_commands(Input, Commands, 0, Current, Next, Trace, Min).
|
||
|
|
||
|
|
||
|
run_commands([], _, _, _, _, _, _).
|
||
|
run_commands([E|Input], Commands, I, Current, Next, Trace, Min) :-
|
||
|
run_code(Commands, E, I, Current, Next, Trace),
|
||
|
matrix_get(Current, [32], M10),
|
||
|
matrix_get(Current, [34], C),
|
||
|
matrix_set_all(Current,Min),
|
||
|
I1 is I+1,
|
||
|
run_commands(Input, Commands, I1, Next, Current, Trace, Min).
|
||
|
|
||
|
run_code([], _, _, _, _, Trace).
|
||
|
run_code([Inst|Input], E, I, Current, Next, Trace) :-
|
||
|
run_inst(Inst, E, I, Current, Next, Trace) ,
|
||
|
run_code(Input, E, I, Current, Next, Trace).
|
||
|
|
||
|
run_inst(emit(Id,T), E, _SP, Current, _, Trace) :-
|
||
|
arg(E,T,P),
|
||
|
matrix_add(Current, [Id], P).
|
||
|
run_inst(prop_same(I,P,Prob), _, SP, Current, _, Trace) :-
|
||
|
matrix_get(Current, [I], PI),
|
||
|
NP is PI+Prob,
|
||
|
matrix_get(Current, [P], P0),
|
||
|
(NP > P0 ->
|
||
|
matrix_set(Current, [P], NP),
|
||
|
matrix_set(Trace, [SP,P], I)
|
||
|
;
|
||
|
true
|
||
|
).
|
||
|
run_inst(prop_next(I,P,Prob), _, SP, Current, Next, Trace) :-
|
||
|
matrix_get(Current, [I], PI),
|
||
|
NP is PI+Prob,
|
||
|
matrix_get(Next, [P], P0),
|
||
|
(NP > P0 ->
|
||
|
matrix_set(Next, [P], NP),
|
||
|
SP1 is SP+1,
|
||
|
IN is -I,
|
||
|
matrix_set(Trace, [SP1,P], IN)
|
||
|
;
|
||
|
true
|
||
|
).
|
||
|
|
||
|
backtrace(Dump, EI, Map, L, Trace) :-
|
||
|
L1 is L-1,
|
||
|
Pos = [L1,EI],
|
||
|
matrix_get(Dump,Pos,Next),
|
||
|
trace(L1,Next,Dump,Map,[],Trace).
|
||
|
|
||
|
trace(0,0,_,_,Trace,Trace) :- !.
|
||
|
trace(L1,Next,Dump,Map,Trace0,Trace) :-
|
||
|
(Next < 0 ->
|
||
|
NL is L1-1,
|
||
|
P is -Next
|
||
|
;
|
||
|
NL = L1,
|
||
|
P = Next
|
||
|
),
|
||
|
once(member(P-AKey,Map)),
|
||
|
AKey=..[N|Args],
|
||
|
Key=..[N,NL|Args],
|
||
|
matrix_get(Dump,[NL,P],New),
|
||
|
trace(NL,New,Dump,Map,[Key|Trace0],Trace).
|
||
|
|
||
|
|
||
|
|