1 /****************************************************************\
2 * *
3 * C4 dynamic programming library - DP layout code *
4 * *
5 * Guy St.C. Slater.. mailto:guy@ebi.ac.uk *
6 * Copyright (C) 2000-2009. All Rights Reserved. *
7 * *
8 * This source code is distributed under the terms of the *
9 * GNU General Public License, version 3. See the file COPYING *
10 * or http://www.gnu.org/licenses/gpl.txt for details *
11 * *
12 * If you use this code, please keep this notice intact. *
13 * *
14 \****************************************************************/
15
16 #include "layout.h"
17 #include "matrix.h"
18
19 /**/
20
Layout_model_has_state_active(C4_Model * model,C4_State * state,gint query_pos,gint target_pos,gint query_length,gint target_length)21 static gboolean Layout_model_has_state_active(C4_Model *model,
22 C4_State *state,
23 gint query_pos, gint target_pos,
24 gint query_length, gint target_length){
25 if(query_pos < 0)
26 return FALSE;
27 if(target_pos < 0)
28 return FALSE;
29 if(query_pos > query_length)
30 return FALSE;
31 if(target_pos > target_length)
32 return FALSE;
33 /**/
34 if(state == model->start_state->state){
35 switch(model->start_state->scope){
36 case C4_Scope_ANYWHERE:
37 break;
38 case C4_Scope_EDGE:
39 if((query_pos != 0) && (target_pos != 0))
40 return FALSE;
41 break;
42 case C4_Scope_QUERY:
43 if(query_pos != 0)
44 return FALSE;
45 break;
46 case C4_Scope_TARGET:
47 if(target_pos != 0)
48 return FALSE;
49 break;
50 case C4_Scope_CORNER:
51 if((query_pos != 0) || (target_pos != 0))
52 return FALSE;
53 break;
54 default:
55 g_assert(!"Bad start scope type");
56 break;
57 }
58 }
59 /**/
60 if(state == model->end_state->state){
61 switch(model->end_state->scope){
62 case C4_Scope_ANYWHERE:
63 break;
64 case C4_Scope_EDGE:
65 if((query_pos != query_length)
66 && (target_pos != target_length))
67 return FALSE;
68 break;
69 case C4_Scope_QUERY:
70 if(query_pos != query_length)
71 return FALSE;
72 break;
73 case C4_Scope_TARGET:
74 if(target_pos != target_length)
75 return FALSE;
76 break;
77 case C4_Scope_CORNER:
78 if((query_pos != query_length)
79 || (target_pos != target_length))
80 return FALSE;
81 break;
82 default:
83 g_assert(!"Bad end scope type");
84 break;
85 }
86 }
87 return TRUE;
88 }
89
Layout_model_state_is_valid_input(C4_Model * model,C4_State * state,gint query_pos,gint target_pos,gint query_length,gint target_length)90 static gboolean Layout_model_state_is_valid_input(C4_Model *model,
91 C4_State *state,
92 gint query_pos, gint target_pos,
93 gint query_length, gint target_length){
94 register gint i;
95 register C4_Transition *transition;
96 if(state == model->start_state->state)
97 return TRUE;
98 for(i = 0; i < state->input_transition_list->len; i++){
99 transition = state->input_transition_list->pdata[i];
100 if((transition->advance_query == 0)
101 && (transition->advance_target == 0)){ /* If silent */
102 if(Layout_model_state_is_valid_input(model,
103 transition->input,
104 query_pos, target_pos,
105 query_length, target_length))
106 return TRUE;
107 } else { /* Emits */
108 if(Layout_model_has_state_active(model, transition->input,
109 query_pos-transition->advance_query,
110 target_pos-transition->advance_target,
111 query_length, target_length))
112 return TRUE;
113 }
114 }
115 return FALSE;
116 }
117 /* The input state must be in scope,
118 * and be traceable to a non-silent transition
119 * from an state which is also in scope.
120 */
121
Layout_transition_is_valid(C4_Model * model,C4_Transition * transition,gint dst_query_pos,gint dst_target_pos,gint query_length,gint target_length)122 static gboolean Layout_transition_is_valid(C4_Model *model,
123 C4_Transition *transition,
124 gint dst_query_pos, gint dst_target_pos,
125 gint query_length, gint target_length){
126 /* Check transition input is valid */
127 if(!Layout_model_has_state_active(model, transition->input,
128 dst_query_pos-transition->advance_query,
129 dst_target_pos-transition->advance_target,
130 query_length, target_length)){
131 return FALSE;
132 }
133 /* Check transition output is valid */
134 if(!Layout_model_has_state_active(model, transition->output,
135 dst_query_pos, dst_target_pos,
136 query_length, target_length)){
137 return FALSE;
138 }
139 /* Check input score will be present.
140 *
141 * This must be disabled for continuation DP to work properly.
142 * also generated code is smaller without it,
143 * and the DP seems to be just as fast.
144 */
145 if(FALSE){ /* DISABLED */
146 if(!Layout_model_state_is_valid_input(model, transition->input,
147 dst_query_pos-transition->advance_query,
148 dst_target_pos-transition->advance_target,
149 query_length, target_length)){
150 return FALSE;
151 }
152 }
153 return TRUE;
154 }
155
Layout_is_transition_valid(Layout * layout,C4_Model * model,C4_Transition * transition,gint dst_query_pos,gint dst_target_pos,gint query_length,gint target_length)156 gboolean Layout_is_transition_valid(Layout *layout, C4_Model *model,
157 C4_Transition *transition,
158 gint dst_query_pos, gint dst_target_pos,
159 gint query_length, gint target_length){
160 register Layout_Row *row;
161 register Layout_Cell *cell;
162 register Layout_Mask *mask;
163 g_assert(layout);
164 row = layout->row_list->pdata[MIN(dst_target_pos,
165 (layout->row_list->len-1))];
166 g_assert(row);
167 cell = row->cell_list->pdata[MIN(dst_query_pos,
168 (row->cell_list->len-1))];
169 g_assert(cell);
170 if(dst_query_pos == query_length){
171 if(dst_target_pos == target_length){ /* QT */
172 mask = cell->corner;
173 if(!mask)
174 mask = cell->end_target;
175 if(!mask)
176 mask = cell->normal;
177 } else { /* Q */
178 mask = cell->end_query;
179 if(!mask)
180 mask = cell->normal;
181 }
182 } else {
183 if(dst_target_pos == target_length){ /* T */
184 mask = cell->end_target;
185 if(!mask)
186 mask = cell->normal;
187 } else { /* - */
188 mask = cell->normal;
189 }
190 }
191 g_assert(mask);
192 g_assert(Layout_transition_is_valid(model, transition,
193 dst_query_pos, dst_target_pos,
194 query_length, target_length)
195 == g_array_index(mask->transition_mask, gboolean,
196 transition->id));
197 return g_array_index(mask->transition_mask, gboolean,
198 transition->id);
199 }
200 /*
201 We could just call Layout_transition_is_valid(),
202 but this way is slightly faster.
203
204 FIXME: should change Layout to allow for faster lookup here.
205 (currently makes code generation fast, but interp slow ...)
206 */
207
208 /**/
209
Layout_Mask_create(C4_Model * model,gint query_pos,gint target_pos,gint query_length,gint target_length)210 static Layout_Mask *Layout_Mask_create(C4_Model *model,
211 gint query_pos, gint target_pos,
212 gint query_length, gint target_length){
213 register Layout_Mask *mask = g_new(Layout_Mask, 1);
214 register gint i;
215 register C4_Transition *transition;
216 gboolean is_valid;
217 mask->transition_mask = g_array_new(FALSE, TRUE, sizeof(gboolean));
218 for(i = 0; i < model->transition_list->len; i++){
219 transition = model->transition_list->pdata[i];
220 is_valid = Layout_transition_is_valid(model, transition,
221 query_pos, target_pos, query_length, target_length);
222 g_array_append_val(mask->transition_mask, is_valid);
223 }
224 return mask;
225 }
226
Layout_Mask_destroy(Layout_Mask * mask)227 static void Layout_Mask_destroy(Layout_Mask *mask){
228 g_array_free(mask->transition_mask, TRUE);
229 g_free(mask);
230 return;
231 }
232
Layout_Mask_is_same(Layout_Mask * a,Layout_Mask * b)233 static gboolean Layout_Mask_is_same(Layout_Mask *a, Layout_Mask *b){
234 register gint i;
235 register gboolean value_a, value_b;
236 if(!(a || b))
237 return TRUE;
238 if(!(a && b))
239 return FALSE;
240 if(a->transition_mask->len != b->transition_mask->len)
241 return FALSE;
242 for(i = 0; i < a->transition_mask->len; i++){
243 value_a = g_array_index(a->transition_mask, gboolean, i);
244 value_b = g_array_index(b->transition_mask, gboolean, i);
245 if(value_a != value_b)
246 return FALSE;
247 }
248 return TRUE;
249 }
250
Layout_Mask_info(Layout_Mask * mask,C4_Model * model,gchar * name)251 static void Layout_Mask_info(Layout_Mask *mask, C4_Model *model,
252 gchar *name){
253 register gint i, count = 0;
254 register gboolean is_valid;
255 if(!mask)
256 return;
257 g_assert(mask->transition_mask->len == model->transition_list->len);
258 g_print("[");
259 for(i = 0; i < mask->transition_mask->len; i++){
260 is_valid = g_array_index(mask->transition_mask, gboolean, i);
261 if(is_valid){
262 count++;
263 g_print("#");
264 } else {
265 g_print(" ");
266 }
267 }
268 g_print("] [%s] [%d]\n", name, count);
269 return;
270 }
271
272 /**/
273
Layout_Cell_create(C4_Model * model,gint query_pos,gint target_pos,gint query_length,gint target_length)274 static Layout_Cell *Layout_Cell_create(C4_Model *model,
275 gint query_pos, gint target_pos,
276 gint query_length, gint target_length){
277 register Layout_Cell *cell = g_new(Layout_Cell, 1);
278 cell->normal = Layout_Mask_create(model,
279 query_pos, target_pos,
280 query_length, target_length);
281 cell->end_query = Layout_Mask_create(model,
282 query_pos, target_pos,
283 query_pos, target_length);
284 cell->end_target = Layout_Mask_create(model,
285 query_pos, target_pos,
286 query_length, target_pos);
287 cell->corner = Layout_Mask_create(model,
288 query_pos, target_pos,
289 query_pos, target_pos);
290 /* Skip EndQuery if same as Normal */
291 if(Layout_Mask_is_same(cell->normal, cell->end_query)){
292 Layout_Mask_destroy(cell->end_query);
293 cell->end_query = NULL;
294 }
295 /* Skip Corner if same as EndTarget */
296 if(Layout_Mask_is_same(cell->corner, cell->end_target)){
297 Layout_Mask_destroy(cell->corner);
298 cell->corner = NULL;
299 /* Also skip EndTarget if same as Normal */
300 if(Layout_Mask_is_same(cell->normal, cell->end_target)){
301 Layout_Mask_destroy(cell->end_target);
302 cell->end_target = NULL;
303 }
304 }
305 /* FIXME: disabled as this was incorrect for some non-local models */
306 #if 0
307 /* Skip EndTarget (and Corner if present) if same as Normal
308 * and Corner?Corner:Normal is same as EndQuery?EndQuery:Normal
309 */
310 if(Layout_Mask_is_same(cell->normal, cell->end_target)
311 && Layout_Mask_is_same(cell->corner ? cell->corner
312 : cell->end_target,
313 cell->end_query ? cell->end_query
314 : cell->normal)){
315 Layout_Mask_destroy(cell->end_target);
316 cell->end_target = NULL;
317 if(cell->corner){
318 Layout_Mask_destroy(cell->corner);
319 cell->corner = NULL;
320 }
321 }
322 #endif /* 0 */
323 return cell;
324 }
325
Layout_Cell_destroy(Layout_Cell * cell)326 static void Layout_Cell_destroy(Layout_Cell *cell){
327 g_assert(cell);
328 g_assert(cell->normal);
329 Layout_Mask_destroy(cell->normal);
330 if(cell->end_query)
331 Layout_Mask_destroy(cell->end_query);
332 if(cell->end_target)
333 Layout_Mask_destroy(cell->end_target);
334 if(cell->corner)
335 Layout_Mask_destroy(cell->corner);
336 g_free(cell);
337 return;
338 }
339
Layout_Cell_is_same(Layout_Cell * a,Layout_Cell * b)340 static gboolean Layout_Cell_is_same(Layout_Cell *a, Layout_Cell *b){
341 if(!Layout_Mask_is_same(a->normal, b->normal))
342 return FALSE;
343 if(!Layout_Mask_is_same(a->end_query, b->end_query))
344 return FALSE;
345 if(!Layout_Mask_is_same(a->end_target, b->end_target))
346 return FALSE;
347 if(!Layout_Mask_is_same(a->corner, b->corner))
348 return FALSE;
349 return TRUE;
350 }
351
Layout_Cell_info(Layout_Cell * cell,C4_Model * model)352 static void Layout_Cell_info(Layout_Cell *cell, C4_Model *model){
353 Layout_Mask_info(cell->normal, model, "normal");
354 Layout_Mask_info(cell->end_query, model, "end_query");
355 Layout_Mask_info(cell->end_target, model, "end_target");
356 Layout_Mask_info(cell->corner, model, "corner");
357 return;
358 }
359
360 /**/
361
Layout_Row_create(C4_Model * model,gint row_number,gint query_length,gint target_length)362 static Layout_Row *Layout_Row_create(C4_Model *model,
363 gint row_number, gint query_length, gint target_length){
364 register Layout_Row *row = g_new(Layout_Row, 1);
365 register Layout_Cell *cell;
366 row->cell_list = g_ptr_array_new();
367 do {
368 cell = Layout_Cell_create(model, row->cell_list->len,
369 row_number, query_length, target_length);
370 if(row->cell_list->len >= model->max_query_advance)
371 if(Layout_Cell_is_same(cell,
372 row->cell_list->pdata[row->cell_list->len-1])){
373 Layout_Cell_destroy(cell);
374 break;
375 }
376 g_ptr_array_add(row->cell_list, cell);
377 g_assert(row->cell_list->len < 1024); /* safety check */
378 } while(TRUE);
379 return row;
380 }
381
Layout_Row_destroy(Layout_Row * row)382 static void Layout_Row_destroy(Layout_Row *row){
383 register gint i;
384 for(i = 0; i < row->cell_list->len; i++)
385 Layout_Cell_destroy(row->cell_list->pdata[i]);
386 g_ptr_array_free(row->cell_list, TRUE);
387 g_free(row);
388 return;
389 }
390
Layout_Row_is_same(Layout_Row * a,Layout_Row * b)391 static gboolean Layout_Row_is_same(Layout_Row *a, Layout_Row *b){
392 register gint i;
393 register Layout_Cell *cell_a, *cell_b;
394 g_assert(a);
395 g_assert(b);
396 if(a->cell_list->len != b->cell_list->len)
397 return FALSE;
398 for(i = 0; i < a->cell_list->len; i++){
399 cell_a = a->cell_list->pdata[i];
400 cell_b = b->cell_list->pdata[i];
401 if(!Layout_Cell_is_same(cell_a, cell_b))
402 return FALSE;
403 }
404 return TRUE;
405 }
406
Layout_Row_info(Layout_Row * row,C4_Model * model)407 static void Layout_Row_info(Layout_Row *row, C4_Model *model){
408 register gint i;
409 g_print("Layout_Cell count: [%d]\n", row->cell_list->len);
410 for(i = 0; i < row->cell_list->len; i++){
411 g_print("Layout_Cell [%d]:\n", i);
412 Layout_Cell_info(row->cell_list->pdata[i], model);
413 }
414 return;
415 }
416
417 /**/
418
Layout_create(C4_Model * model)419 Layout *Layout_create(C4_Model *model){
420 register Layout *layout = g_new(Layout, 1);
421 register Layout_Row *row;
422 g_assert(model);
423 g_assert(!model->is_open);
424 layout->row_list = g_ptr_array_new();
425 do {
426 row = Layout_Row_create(model, layout->row_list->len,
427 1024, 1024);
428 if(layout->row_list->len >= model->max_target_advance)
429 if(Layout_Row_is_same(row,
430 layout->row_list->pdata[layout->row_list->len-1])){
431 Layout_Row_destroy(row);
432 break;
433 }
434 g_ptr_array_add(layout->row_list, row);
435 g_assert(layout->row_list->len < 1024); /* safety check */
436 } while(TRUE);
437 return layout;
438 }
439 /* FIXME: optimisation
440 * may be possible to trim the pattern for some models
441 * where dimensions are <= layout->max_{query,target}_advance
442 */
443
Layout_destroy(Layout * layout)444 void Layout_destroy(Layout *layout){
445 register gint i;
446 for(i = 0; i < layout->row_list->len; i++)
447 Layout_Row_destroy(layout->row_list->pdata[i]);
448 g_ptr_array_free(layout->row_list, TRUE);
449 g_free(layout);
450 return;
451 }
452
Layout_info(Layout * layout,C4_Model * model)453 void Layout_info(Layout *layout, C4_Model *model){
454 register gint i;
455 register C4_Transition *transition;
456 g_print("Layout for model [%s]\n", model->name);
457 for(i = 0; i < model->transition_list->len; i++){
458 transition = model->transition_list->pdata[i];
459 g_message("transition [%d] [%s]", i, transition->name);
460 }
461 g_print("Layout_Row count: [%d]\n", layout->row_list->len);
462 for(i = 0; i < layout->row_list->len; i++){
463 g_print("Layout_Row [%d]:\n", i);
464 Layout_Row_info(layout->row_list->pdata[i], model);
465 }
466 return;
467 }
468
469 /**/
470
471