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