9 #include <vaucanson/int_boolean_automaton.hh>
13 #include "dependence_graph.h"
14 #include "eqv2_options.h"
19 #include <isl/constraint.h>
21 #ifdef HAVE_OMEGA_PLUS
24 using namespace omega
;
26 typedef std::vector
<Variable_ID
> varvector
;
27 typedef std::vector
<Global_Var_Decl
*> globalvector
;
29 static void create_globals(globalvector
&globals
, __isl_keep isl_space
*dim
)
31 unsigned nparam
= isl_space_dim(dim
, isl_dim_param
);
32 for (int i
= 0; i
< nparam
; ++i
) {
34 name
= isl_space_get_dim_name(dim
, isl_dim_param
, i
);
35 globals
.push_back(new Global_Var_Decl(name
));
48 static isl_stat
add_constraint(__isl_take isl_constraint
*c
, void *user
)
50 struct map2rel_data
*data
= (struct map2rel_data
*) user
;
54 if (isl_constraint_is_equality(c
))
55 h
= data
->base
->add_EQ();
57 h
= data
->base
->add_GEQ();
59 for (int i
= 0; i
< data
->in
.size(); ++i
) {
60 v
= isl_constraint_get_coefficient_val(c
, isl_dim_in
, i
);
61 h
.update_coef(data
->in
[i
], isl_val_get_num_si(v
));
64 for (int i
= 0; i
< data
->out
.size(); ++i
) {
65 v
= isl_constraint_get_coefficient_val(c
, isl_dim_out
, i
);
66 h
.update_coef(data
->out
[i
], isl_val_get_num_si(v
));
69 for (int i
= 0; i
< data
->params
.size(); ++i
) {
70 v
= isl_constraint_get_coefficient_val(c
, isl_dim_param
, i
);
71 h
.update_coef(data
->params
[i
], isl_val_get_num_si(v
));
74 for (int i
= 0; i
< data
->exists
.size(); ++i
) {
75 v
= isl_constraint_get_coefficient_val(c
, isl_dim_div
, i
);
76 h
.update_coef(data
->exists
[i
], isl_val_get_num_si(v
));
79 v
= isl_constraint_get_constant_val(c
);
80 h
.update_const(isl_val_get_num_si(v
));
83 isl_constraint_free(c
);
88 static Relation
basic_map2relation(isl_basic_map
*bmap
,
89 const Variable_ID_Tuple
*globals
)
91 struct map2rel_data data
;
93 Relation
r(isl_basic_map_dim(bmap
, isl_dim_in
),
94 isl_basic_map_dim(bmap
, isl_dim_out
));
95 F_Exists
*e
= r
.add_exists();
96 data
.base
= e
->add_and();
98 for (int j
= 1; j
<= r
.n_inp(); ++j
)
99 data
.in
.push_back(r
.input_var(j
));
100 for (int j
= 1; j
<= r
.n_out(); ++j
)
101 data
.out
.push_back(r
.output_var(j
));
102 for (int i
= 0; i
< isl_basic_map_dim(bmap
, isl_dim_div
); ++i
)
103 data
.exists
.push_back(e
->declare());
104 for (int i
= 0; i
< globals
->size(); ++i
)
105 data
.params
.push_back(r
.get_local((*globals
)[i
+1]));
107 isl_basic_map_foreach_constraint(bmap
, &add_constraint
, &data
);
113 static isl_stat
bm2r(__isl_take isl_basic_map
*bmap
, void *user
)
115 Relation
*r
= (Relation
*)user
;
117 *r
= Union(*r
, basic_map2relation(bmap
, r
->global_decls()));
119 isl_basic_map_free(bmap
);
124 static Relation
map2relation(__isl_keep isl_map
*map
,
125 const globalvector
&globals
)
127 Relation
r(isl_map_dim(map
, isl_dim_in
), isl_map_dim(map
, isl_dim_out
));
128 for (int i
= 0; i
< globals
.size(); ++i
)
129 r
.get_local(globals
[i
]);
133 isl_map_foreach_basic_map(map
, &bm2r
, &r
);
138 static void max_index(Constraint_Handle c
, varvector
& vv
, varvector
& params
)
140 for (Constr_Vars_Iter
cvi(c
); cvi
; ++cvi
) {
141 Variable_ID var
= (*cvi
).var
;
142 if (find(vv
.begin(), vv
.end(), var
) == vv
.end() &&
143 find(params
.begin(), params
.end(), var
) == params
.end())
148 static __isl_give isl_constraint
*set_constraint(__isl_take isl_constraint
*c
,
149 Constraint_Handle h
, varvector
&vv
, varvector
¶ms
)
153 for (int i
= 0; i
< vv
.size(); ++i
) {
154 v
= h
.get_coef(vv
[i
]);
155 c
= isl_constraint_set_coefficient_si(c
, isl_dim_set
, i
, v
);
157 for (int i
= 0; i
< params
.size(); ++i
) {
158 v
= h
.get_coef(params
[i
]);
159 c
= isl_constraint_set_coefficient_si(c
, isl_dim_param
, i
, v
);
162 c
= isl_constraint_set_constant_si(c
, v
);
167 static isl_basic_set
*disjunct2basic_set(DNF_Iterator
&di
, isl_space
*dim
,
168 varvector
&vv
, varvector
¶ms
)
175 for (EQ_Iterator ei
= (*di
)->EQs(); ei
; ++ei
)
176 max_index((*ei
), vv
, params
);
177 for (GEQ_Iterator gi
= (*di
)->GEQs(); gi
; ++gi
)
178 max_index((*gi
), vv
, params
);
180 dim
= isl_space_add_dims(dim
, isl_dim_set
, vv
.size() - d
);
181 bset
= isl_basic_set_universe(isl_space_copy(dim
));
182 ls
= isl_local_space_from_space(dim
);
184 for (EQ_Iterator ei
= (*di
)->EQs(); ei
; ++ei
) {
185 c
= isl_constraint_alloc_equality(isl_local_space_copy(ls
));
186 c
= set_constraint(c
, (*ei
), vv
, params
);
187 bset
= isl_basic_set_add_constraint(bset
, c
);
189 for (GEQ_Iterator gi
= (*di
)->GEQs(); gi
; ++gi
) {
190 c
= isl_constraint_alloc_inequality(isl_local_space_copy(ls
));
191 c
= set_constraint(c
, (*gi
), vv
, params
);
192 bset
= isl_basic_set_add_constraint(bset
, c
);
195 isl_local_space_free(ls
);
197 bset
= isl_basic_set_project_out(bset
, isl_dim_set
, d
, vv
.size() - d
);
203 static __isl_give isl_map
*relation2map(__isl_take isl_space
*dim
, Relation
&r
)
211 for (int j
= 1; j
<= r
.n_inp(); ++j
)
212 vv
.push_back(r
.input_var(j
));
213 for (int j
= 1; j
<= r
.n_out(); ++j
)
214 vv
.push_back(r
.output_var(j
));
216 const Variable_ID_Tuple
*globals
= r
.global_decls();
217 for (int i
= 0; i
< globals
->size(); ++i
)
218 params
.push_back(r
.get_local((*globals
)[i
+1]));
220 ctx
= isl_space_get_ctx(dim
);
221 dim_set
= isl_space_set_alloc(ctx
, params
.size(), vv
.size());
222 map
= isl_map_empty(isl_space_copy(dim
));
224 for (DNF_Iterator
di(r
.query_DNF()); di
; ++di
) {
228 bset
= disjunct2basic_set(di
, isl_space_copy(dim_set
), vv
, params
);
229 bmap
= isl_basic_map_from_basic_set(bset
, isl_space_copy(dim
));
230 map
= isl_map_union(map
, isl_map_from_basic_map(bmap
));
233 isl_space_free(dim_set
);
239 /* Compute transitive closure using Omega+.
240 * Note that the result computed by Omega also contains the identity
241 * mapping on the intersection of domain and range, but we will include
242 * the entire identity mapping later, so this shouldn't be a problem.
244 static __isl_give isl_map
*omega_closure(__isl_take isl_map
*map
)
246 isl_ctx
*ctx
= isl_map_get_ctx(map
);
247 struct options
*options
= isl_ctx_peek_eqv2_options(ctx
);
249 globalvector globals
;
251 dim
= isl_map_get_space(map
);
252 create_globals(globals
, dim
);
254 Relation R
= map2relation(map
, globals
);
258 if (options
->try_lower
) {
260 TC
= TransitiveClosure(copy(R
));
261 Relation test
= Composition(copy(TC
), copy(R
));
263 exact
= TC
.is_exact() &&
264 Must_Be_Subset(copy(R
), copy(TC
)) &&
265 Must_Be_Subset(test
, copy(TC
));
267 TC
= Upper_Bound(ApproxClosure(R
));
269 TC
= Upper_Bound(ApproxClosure(R
));
271 map
= relation2map(dim
, TC
);
277 /* Compute transitive closure */
278 static __isl_give isl_map
*closure(__isl_take isl_map
*map
)
280 isl_ctx
*ctx
= isl_map_get_ctx(map
);
281 struct options
*options
= isl_ctx_peek_eqv2_options(ctx
);
283 #ifdef HAVE_OMEGA_PLUS
285 return omega_closure(map
);
288 return isl_map_transitive_closure(map
, NULL
);
291 using namespace vcsn::int_boolean_automaton
;
292 using namespace vcsn
;
294 template <class Exp_
, class Dispatch_
>
295 class acc_visitor
: public algebra::KRatExpMatcher
<
296 acc_visitor
<Exp_
, Dispatch_
>, Exp_
, isl_map
*, Dispatch_
>
299 typedef typename
Exp_::monoid_elt_value_t monoid_elt_value_t
;
300 typedef acc_visitor
<Exp_
, Dispatch_
> this_class
;
301 typedef isl_map
* return_type
;
307 acc_visitor(const MSA
&msa
) : msa(msa
) {}
309 INHERIT_CONSTRUCTORS(this_class
, Exp_
, isl_map
*, Dispatch_
);
311 MATCH__(Product
, lhs
, rhs
) {
312 return isl_map_apply_range(this->match(lhs
), this->match(rhs
));
315 MATCH__(Sum
, lhs
, rhs
) {
316 return isl_map_union(this->match(lhs
), this->match(rhs
));
322 map
= closure(this->match(node
));
323 dim
= isl_map_get_space(map
);
324 return isl_map_union(map
, isl_map_identity(dim
));
327 MATCH__(LeftWeight
, w
, node
) {
332 MATCH__(RightWeight
, node
, w
) {
337 MATCH_(Constant
, m
) {
339 typename
monoid_elt_value_t::const_iterator i
= m
.begin();
340 assert(i
!= m
.end());
341 map
= isl_map_copy(msa
.firing
[*i
]);
343 assert(i
== m
.end());
348 /* By construction, there should always be a path */
354 /* There can be no zero-length path */
361 template<typename Exp
>
362 isl_map
*accessibility(const MSA
&msa
, const Exp
&kexp
)
364 acc_visitor
<Exp
, algebra::DispatchFunction
<Exp
> > m(msa
);
365 return m
.match(kexp
);
368 static unsigned update_out_dim(pdg::PDG
*pdg
, unsigned out_dim
)
370 for (int i
= 0; i
< pdg
->arrays
.size(); ++i
) {
371 pdg::array
*array
= pdg
->arrays
[i
];
372 if (array
->type
!= pdg::array::output
)
374 if (array
->dims
.size() + 1 > out_dim
)
375 out_dim
= array
->dims
.size() + 1;
381 /* The input arrays of the two programs are supposed to be the same,
382 * so they should at least have the same dimension. Make sure
383 * this is true, because we depend on it later on.
385 static int check_input_arrays(dependence_graph
*dg1
, dependence_graph
*dg2
)
387 for (int i
= 0; i
< dg1
->input_computations
.size(); ++i
)
388 for (int j
= 0; j
< dg2
->input_computations
.size(); ++j
) {
389 if (strcmp(dg1
->input_computations
[i
]->operation
,
390 dg2
->input_computations
[j
]->operation
))
392 if (dg1
->input_computations
[i
]->dim
==
393 dg2
->input_computations
[j
]->dim
)
396 "input arrays \"%s\" do not have the same dimension\n",
397 dg1
->input_computations
[i
]->operation
);
404 /* Compute a map between the domains of the output nodes of
405 * the dependence graphs that expresses that array1 in dg1
406 * and array2 in dg2 are equal.
407 * This map is then returned as a set.
409 static __isl_give isl_set
*compute_equal_array(
410 dependence_graph
*dg1
, dependence_graph
*dg2
, int array1
, int array2
)
412 const char *array_name
= dg1
->output_arrays
[array1
];
413 isl_set
*out1
= isl_set_copy(dg1
->out
->domain
);
414 isl_set
*out2
= isl_set_copy(dg2
->out
->domain
);
415 unsigned dim1
= isl_set_n_dim(out1
);
416 unsigned dim2
= isl_set_n_dim(out2
);
418 out1
= isl_set_fix_dim_si(out1
, dim1
- 1, array1
);
419 out2
= isl_set_fix_dim_si(out2
, dim2
- 1, array2
);
420 out1
= isl_set_remove_dims(out1
, isl_dim_set
, dim1
- 1, 1);
421 out2
= isl_set_remove_dims(out2
, isl_dim_set
, dim2
- 1, 1);
422 int equal
= isl_set_is_equal(out1
, out2
);
427 fprintf(stderr
, "different output domains for array %s\n",
432 out1
= isl_set_copy(dg1
->out
->domain
);
433 out2
= isl_set_copy(dg2
->out
->domain
);
434 out1
= isl_set_fix_dim_si(out1
, dim1
- 1, array1
);
435 out2
= isl_set_fix_dim_si(out2
, dim2
- 1, array2
);
436 out1
= isl_set_coalesce(out1
);
437 out2
= isl_set_coalesce(out2
);
438 isl_space
*dim
= isl_space_map_from_set(isl_set_get_space(out2
));
439 isl_map
*id
= isl_map_identity(dim
);
440 id
= isl_map_remove_dims(id
, isl_dim_in
, isl_map_n_in(id
) - 1, 1);
441 id
= isl_map_apply_domain(id
, isl_map_copy(id
));
442 id
= isl_map_intersect_domain(id
, out1
);
443 id
= isl_map_intersect_range(id
, out2
);
445 return isl_map_wrap(id
);
448 /* Compute a map between the domains of the output nodes of
449 * the dependence graphs that expresses that arrays with
450 * the same names in both graphs are equal.
451 * The map is returned as a set.
453 static __isl_give isl_set
*compute_equal_output(
454 dependence_graph
*dg1
, dependence_graph
*dg2
)
459 isl_set
*equal_output
;
462 dim
= isl_set_get_space(dg1
->out
->domain
);
463 d
= isl_space_dim(dim
, isl_dim_set
);
464 map
= isl_map_empty(isl_space_map_from_set(dim
));
465 equal_output
= isl_map_wrap(map
);
467 while (i1
< dg1
->output_arrays
.size() || i2
< dg2
->output_arrays
.size()) {
469 cmp
= i1
== dg1
->output_arrays
.size() ? 1 :
470 i2
== dg2
->output_arrays
.size() ? -1 :
471 strcmp(dg1
->output_arrays
[i1
], dg2
->output_arrays
[i2
]);
474 "Array \"%s\" only in program 1; not checked\n",
475 dg1
->output_arrays
[i1
]);
477 } else if (cmp
> 0) {
479 "Array \"%s\" only in program 2; not checked\n",
480 dg2
->output_arrays
[i2
]);
484 eq_array
= compute_equal_array(dg1
, dg2
, i1
, i2
);
485 equal_output
= isl_set_union(equal_output
, eq_array
);
491 equal_output
= isl_set_set_tuple_name(equal_output
, "s0");
496 static int check_equivalence(isl_ctx
*ctx
,
497 dependence_graph
*dg1
, dependence_graph
*dg2
)
505 if (check_input_arrays(dg1
, dg2
))
508 p
= isl_printer_to_file(ctx
, stdout
);
510 root
= msa
.add(dg1
->out
, dg2
->out
);
513 for (int i
= 0; i
< msa
.firing
.size(); ++i
)
516 automaton_t a
= make_automaton(alpha
);
517 std::vector
<hstate_t
> state
;
518 for (int i
= 0; i
< msa
.state
.size(); ++i
)
519 state
.push_back(a
.add_state());
521 for (int i
= 0; i
< msa
.transition
.size(); ++i
) {
522 int s1
= msa
.transition
[i
].first
.first
;
523 int s2
= msa
.transition
[i
].first
.second
;
524 int f
= msa
.transition
[i
].second
;
525 a
.add_letter_transition(state
[s1
], state
[s2
], f
);
528 a
.set_initial(state
[root
]);
530 isl_set
*equal_output
;
531 equal_output
= compute_equal_output(dg1
, dg2
);
533 for (int i
= 0; i
< msa
.input_state
.size(); ++i
) {
539 a
.set_final(state
[msa
.input_state
[i
]]);
540 rat_exp_t ratexp
= aut_to_exp(a
);
543 acc
= accessibility(msa
, ratexp
.value());
544 set
= isl_set_apply(isl_set_copy(equal_output
),
546 map
= isl_set_unwrap(set
);
548 id
= isl_map_identity(isl_map_get_space(map
));
549 map
= isl_map_subtract(map
, id
);
551 set
= isl_map_wrap(map
);
552 set
= isl_set_set_tuple_name(set
,
553 isl_map_get_tuple_name(acc
, isl_dim_out
));
554 set
= isl_set_apply(set
, isl_map_reverse(acc
));
555 d
= isl_set_dim(set
, isl_dim_set
) / 2;
556 set
= isl_set_remove_dims(set
, isl_dim_set
, d
, d
);
557 if (!isl_set_is_empty(set
)) {
559 isl_printer_print_str(p
, "NOT equivalent");
560 isl_printer_end_line(p
);
561 set
= isl_set_coalesce(set
);
562 isl_printer_print_set(p
, set
);
563 isl_printer_end_line(p
);
569 /* Consider each fail state in turn as the corresponding
570 * domains may have different dimensions.
572 for (int i
= 0; i
< msa
.fail_state
.size(); ++i
) {
576 a
.set_final(state
[msa
.fail_state
[i
]]);
577 rat_exp_t ratexp
= aut_to_exp(a
);
580 acc
= accessibility(msa
, ratexp
.value());
581 acc
= isl_map_intersect_domain(acc
, isl_set_copy(equal_output
));
583 set
= isl_map_domain(acc
);
584 d
= isl_set_dim(set
, isl_dim_set
) / 2;
585 set
= isl_set_remove_dims(set
, isl_dim_set
, d
, d
);
586 if (!isl_set_is_empty(set
)) {
588 isl_printer_print_str(p
, "NOT equivalent");
589 isl_printer_end_line(p
);
590 set
= isl_set_coalesce(set
);
591 isl_printer_print_set(p
, set
);
592 isl_printer_end_line(p
);
598 isl_set_free(equal_output
);
601 isl_printer_print_str(p
, "NOT ");
602 isl_printer_print_str(p
, "OK");
603 isl_printer_end_line(p
);
610 int main(int argc
, char *argv
[])
613 struct options
*options
= eqv2_options_new_with_defaults();
614 isl_set
*context
= NULL
;
615 dependence_graph
*dg
[2];
617 argc
= eqv2_options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
619 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
621 fprintf(stderr
, "Unable to allocate ctx\n");
625 if (options
->context
)
626 context
= isl_set_read_from_str(ctx
, options
->context
);
629 unsigned out_dim
= 0;
630 for (int i
= 0; i
< 2; ++i
) {
632 in
= fopen(options
->program
[i
], "r");
634 fprintf(stderr
, "Unable to open %s\n", options
->program
[i
]);
637 pdg
[i
] = yaml::Load
<pdg::PDG
>(in
, ctx
);
640 fprintf(stderr
, "Unable to read %s\n", options
->program
[i
]);
643 out_dim
= update_out_dim(pdg
[i
], out_dim
);
646 pdg
[i
]->params
.size() != isl_set_dim(context
, isl_dim_param
)) {
648 "Parameter dimension mismatch; context ignored\n");
649 isl_set_free(context
);
654 for (int i
= 0; i
< 2; ++i
) {
655 dg
[i
] = pdg_to_dg(pdg
[i
], out_dim
, isl_set_copy(context
));
660 int res
= check_equivalence(ctx
, dg
[0], dg
[1]);
662 for (int i
= 0; i
< 2; ++i
)
664 isl_set_free(context
);