void _do_grouping(Cudd &cudd, hmap<uint, vector<SetUint>> &groups_by_length, // we modify its values uint cur_group_length, const VecUint& cur_order) { L_INF("fixing groups of size " << cur_group_length << ". The number of groups = " << groups_by_length[cur_group_length].size()); auto cur_groups = groups_by_length[cur_group_length]; for (uint i = 0; i+cur_group_length < cur_order.size(); ++i) { SetUint candidate; for (uint j = 0; j < cur_group_length; ++j) candidate.insert(cur_order[i+j]); if (find(cur_groups.begin(), cur_groups.end(), candidate) != cur_groups.end()) { for (uint l = 2; l < cur_group_length; ++l) { // cout << "rm intersections " << string_set(candidate) << endl; // cout << "before: " << endl; // for (auto const v : groups_by_length[l]) // cout << string_set(v) << endl; remove_intersecting(candidate, groups_by_length[l]); //remove from smaller groups // cout << "after: " << endl; // for (auto const v : groups_by_length[l]) // cout << string_set(v) << endl; } introduce_group_into_cudd(cudd, candidate); } } }
void do_grouping(Cudd& cudd, const vector<VecUint>& orders) { L_INF("trying to group vars.."); if (orders[0].size() < 5) // window size is too big return; hmap<uint, vector<SetUint>> groups_by_length; groups_by_length[2] = get_group_candidates(orders, 2); groups_by_length[3] = get_group_candidates(orders, 3); groups_by_length[4] = get_group_candidates(orders, 4); groups_by_length[5] = get_group_candidates(orders, 5); L_INF("# of group candidates: of size 2 -- " << groups_by_length[2].size()); for (auto const& g : groups_by_length[2]) { L_INF(string_set(g)); } L_INF("# of group candidates: of size 3 -- " << groups_by_length[3].size()); for (auto const& g : groups_by_length[3]) { L_INF(string_set(g)); } L_INF("# of group candidates: of size 4 -- " << groups_by_length[4].size()); L_INF("# of group candidates: of size 5 -- " << groups_by_length[5].size()); auto cur_order = orders.back(); // we fix only groups present in the current order (because that is easier to implement) for (uint i = 5; i>=2; --i) // decreasing order! if (!groups_by_length[i].empty()) _do_grouping(cudd, groups_by_length, i, cur_order); }
void Synth::compose_init_state_bdd() { // Initial state is 'all latches are zero' L_INF("compose_init_state_bdd.."); init = cudd.bddOne(); for (uint i = 0; i < aiger_spec->num_latches; i++) { BDD latch_var = get_bdd_for_sign_lit(aiger_spec->latches[i].lit); init = init & ~latch_var; } }
BDD Synth::calc_win_region() { /** Calculate a winning region for the safety game: win = greatest_fix_point.X [not_error & pre_sys(X)] :return: BDD representing the winning region **/ BDD new_ = cudd.bddOne(); for (uint i = 1; ; ++i) { L_INF("calc_win_region: iteration " << i << ": node count " << cudd.ReadNodeCount()); BDD curr = new_; new_ = pre_sys(curr); if ((init & new_) == cudd.bddZero()) return cudd.bddZero(); if (new_ == curr) return new_; } }
DdCiAdapter::DdCiAdapter( cDevice *dev, int ca_fd, int ci_fdw, int ci_fdr, cString &devNameCa, cString &devNameCi ) : device( dev ) , fd( ca_fd ) , caDevName( devNameCa ) , ciSend( *this, ci_fdw, devNameCi ) , ciRecv( *this, ci_fdr, devNameCi ) , camSlot( 0 ) { LOG_FUNCTION_ENTER; if (!dev) { L_ERROR_STR( "dev=NULL!" ); return; } SetDescription( "DDCI adapter on device %d (%s)", device->DeviceNumber(), *caDevName ); ca_caps_t Caps; if (ioctl( fd, CA_GET_CAP, &Caps ) == 0) { if ((Caps.slot_type & CA_CI_LINK) != 0) { int NumSlots = Caps.slot_num; if (NumSlots > 0) { for (int i = 0; i < NumSlots; i++) { if (!camSlot) { camSlot = new DdCiCamSlot( *this, ciSend ); } else { L_ERR( "Currently only ONE CAM slot supported" ); } } L_DBG( "DdCiAdapter(%s) for device %d created", *caDevName, device->DeviceNumber() ); Start(); } else L_ERR( "no CAM slots found on device %d", device->DeviceNumber() ); } else L_INF( "device %d doesn't support CI link layer interface", device->DeviceNumber() ); } else L_ERR( "can't get CA capabilities on device %d", device->DeviceNumber() ); LOG_FUNCTION_EXIT; }
BDD Synth::get_nondet_strategy() { /** Get non-deterministic strategy from the winning region. If the system outputs controllable values that satisfy this non-deterministic strategy, then the system wins. I.e., a non-deterministic strategy describes for each state all possible plausible output values (below is assuming W excludes error states) strategy(t,u,c) = ∃t' W(t) & T(t,i,c,t') & W(t') But since t' <-> bdd(t,i,o), (and since we use error=error(t,u,c)), we use: strategy(t,u,c) = ~error(t,u,c) & W(t) & W(t)[t <- bdd_next_t(t,u,c)] :return: non-deterministic strategy bdd :note: The strategy is non-deterministic -- determinization is done later. **/ L_INF("get_nondet_strategy.."); // TODO: do we need win_region? return ~error & win_region & win_region.VectorCompose(get_substitution()); }
bool Synth::run() { init_cudd(cudd); aiger_spec = aiger_init(); const char *err = aiger_open_and_read_from_file(aiger_spec, aiger_file_name.c_str()); MASSERT(err == NULL, err); Cleaner cleaner(aiger_spec); // main part L_INF("synthesize.. number of vars = " << aiger_spec->num_inputs + aiger_spec->num_latches); // grapher = new Grapher(); timer.sec_restart(); // grapher->compute_deps(aiger_spec); L_INF("calculating deps graph took (sec): " << timer.sec_restart()); //grapher.dump_dot(); //print_set(grapher.deps[STRIP_LIT(aiger_spec->outputs[0].lit)], aiger_spec); // Create all variables. _tmp ensures that BDD have positive refs. vector<BDD> _tmp; for (uint i = 0; i < aiger_spec->num_inputs + aiger_spec->num_latches; ++i) _tmp.push_back(cudd.bddVar(i)); for (uint i = 0; i < aiger_spec->num_inputs; ++i) { auto aiger_strip_lit = aiger_spec->inputs[i].lit; cudd_by_aiger[aiger_strip_lit] = i; aiger_by_cudd[i] = aiger_strip_lit; } for (uint i = 0; i < aiger_spec->num_latches; ++i) { auto aiger_strip_lit = aiger_spec->latches[i].lit; auto cudd_idx = i + aiger_spec->num_inputs; cudd_by_aiger[aiger_strip_lit] = cudd_idx; aiger_by_cudd[cudd_idx] = aiger_strip_lit; } // vector<int> permutation = compute_permutation(grapher, cudd, aiger_spec); // MASSERT(permutation.size() == (uint) cudd.ReadSize(), ""); /* L_INF("frequencies of latches"); for (uint i = 0; i < aiger_spec->num_latches; ++i) { auto lit = aiger_spec->latches[i].lit; cout << "latch lit " << lit << " : " << grapher.freq_map[lit] << endl; } L_INF("frequencies of inputs"); for (uint i = 0; i < aiger_spec->num_inputs; ++i) { auto lit = aiger_spec->inputs[i].lit; cout << "input lit " << lit << " : " << grapher.freq_map[lit] << endl; } */ /* vector<BDD> nodes; nodes.push_back(error); vector<string> names; vector<const char*> names_; names.push_back(string("weird0")); names_.push_back(names[0].data()); for (uint i = 1; i < aiger_spec->num_latches + aiger_spec->num_inputs+1; ++i) { names.push_back(to_string(i)); if (aiger_is_input(aiger_spec, i*2)) { auto s = aiger_is_input(aiger_spec, i*2); if (s->name) names.push_back(string(s->name)); else names.push_back(to_string(i*2)); } if (aiger_is_latch(aiger_spec, i*2)) { auto s = aiger_is_latch(aiger_spec, i*2); if (s->name) names.push_back(string(s->name)); else names.push_back(to_string(i*2)); } names_.push_back(names[i].data()); } cudd.DumpDot(nodes, names_.data(), NULL); cout << cudd.OrderString() << endl; exit(0); */ compose_init_state_bdd(); timer.sec_restart(); compose_transition_vector(); L_INF("calc_trans_rel took (sec): " << timer.sec_restart()); introduce_error_bdd(); L_INF("introduce_error_bdd took (sec): " << timer.sec_restart()); // cout << "before comput: nof_vars = " << cudd.ReadSize() << endl; // reachable = compute_reachable(aiger_spec, init, transition_func, error, cudd); // cout << "after comput: nof_vars = " << cudd.ReadSize() << endl; // no need for cache bdd_by_aiger_unlit.clear(); // reorder_opt(cudd); // print_aiger_like_order(cudd); timer.sec_restart(); win_region = calc_win_region(); L_INF("calc_win_region took (sec): " << timer.sec_restart()); // print_aiger_like_order(cudd); // Cudd_MakeTreeNode(cudd.getManager(), 5, 8, MTR_FIXED); // reorder_opt(cudd); // cout << "optimal order after calc_win_region" << endl; // print_aiger_like_order(cudd); // cout << cudd.ReadNodeCount() << endl; if (win_region.IsZero()) { cout << "UNREALIZABLE" << endl; return 0; } cout << "REALIZABLE" << endl; non_det_strategy = get_nondet_strategy(); //cleaning non-used bdds win_region = cudd.bddZero(); transition_func.clear(); init = cudd.bddZero(); error = cudd.bddZero(); // // TODO: set time limit on reordering? or even disable it if no time? hmap<uint, BDD> model_by_cuddidx = extract_output_funcs(); L_INF("extract_output_funcs took (sec): " << timer.sec_restart()); //cleaning non-used bdds non_det_strategy = cudd.bddZero(); // auto elapsed_sec = time_limit_sec - timer.sec_from_origin(); if (elapsed_sec > 100) { // leave 100sec just in case auto spare_time_sec = elapsed_sec - 100; cudd.ResetStartTime(); cudd.IncreaseTimeLimit((unsigned long) (spare_time_sec * 1000)); cudd.ReduceHeap(CUDD_REORDER_SIFT_CONVERGE); cudd.UnsetTimeLimit(); cudd.AutodynDisable(); // just in case -- cudd hangs on timeout } for (auto const it : model_by_cuddidx) model_to_aiger(cudd.ReadVars((int)it.first), it.second); L_INF("model_to_aiger took (sec): " << timer.sec_restart()); L_INF("circuit size: " << (aiger_spec->num_ands + aiger_spec->num_latches)); int res = 1; if (output_file_name == "stdout") res = aiger_write_to_file(aiger_spec, aiger_ascii_mode, stdout); else if (!output_file_name.empty()) { L_INF("writing a model to " << output_file_name); res = aiger_open_and_write_to_file(aiger_spec, output_file_name.c_str()); } MASSERT(res, "Could not write result file"); return 1; }
hmap<uint,BDD> Synth::extract_output_funcs() { /** The result vector respects the order of the controllable variables **/ L_INF("extract_output_funcs.."); cudd.FreeTree(); // ordering that worked for win region computation might not work here hmap<uint,BDD> model_by_cuddidx; vector<BDD> controls = get_controllable_vars_bdds(); while (!controls.empty()) { BDD c = controls.back(); controls.pop_back(); aiger_symbol *aiger_input = aiger_is_input(aiger_spec, aiger_by_cudd[c.NodeReadIndex()]); L_INF("getting output function for " << aiger_input->name); BDD c_arena; if (controls.size() > 0) { BDD cube = cudd.bddComputeCube(controls.data(), NULL, (int)controls.size()); c_arena = non_det_strategy.ExistAbstract(cube); } else { //no other signals left c_arena = non_det_strategy; } // Now we have: c_arena(t,u,c) = ∃c_others: nondet(t,u,c) // (i.e., c_arena talks about this particular c, about t and u) BDD c_can_be_true = c_arena.Cofactor(c); BDD c_can_be_false = c_arena.Cofactor(~c); BDD c_must_be_true = ~c_can_be_false & c_can_be_true; BDD c_must_be_false = c_can_be_false & ~c_can_be_true; // Note that we cannot use `c_must_be_true = ~c_can_be_false`, // since the negation can cause including tuples (t,i,o) that violate non_det_strategy. auto support_indices = cudd.SupportIndices(vector<BDD>({c_must_be_false, c_must_be_true})); for (auto const var_cudd_idx : support_indices) { auto v = cudd.ReadVars(var_cudd_idx); auto new_c_must_be_false = c_must_be_false.ExistAbstract(v); auto new_c_must_be_true = c_must_be_true.ExistAbstract(v); if ((new_c_must_be_false & new_c_must_be_true) == cudd.bddZero()) { c_must_be_false = new_c_must_be_false; c_must_be_true = new_c_must_be_true; } } // We use 'restrict' operation, but we could also just do: // c_model = care_set -> must_be_true // but this is (presumably) less efficient (in time? in size?). // (intuitively, because we always set c_model to 1 if !care_set, but we could set it to 0) // // The result of restrict operation satisfies: // on c_care_set: c_must_be_true <-> must_be_true.Restrict(c_care_set) BDD c_model = c_must_be_true.Restrict(c_must_be_true | c_must_be_false); model_by_cuddidx[c.NodeReadIndex()] = c_model; //killing node refs c_must_be_false = c_must_be_true = c_can_be_false = c_can_be_true = c_arena = cudd.bddZero(); //TODO: ak: strange -- the python version for the example amba_02_9n produces a smaller circuit (~5-10 times)! non_det_strategy = non_det_strategy.Compose(c_model, c.NodeReadIndex()); //non_det_strategy = non_det_strategy & ((c & c_model) | (~c & ~c_model)); } return model_by_cuddidx; }
void introduce_group_into_cudd(Cudd &cudd, const SetUint& group) { L_INF("adding variable group to cudd: " << string_set(group)); auto first_var_pos = get_var_of_min_order_position(cudd, group); cudd.MakeTreeNode(first_var_pos, (uint) group.size(), MTR_FIXED); }
void Synth::compose_transition_vector() { L_INF("compose_transition_vector.."); for (uint i = 0; i < aiger_spec->num_latches; ++i) transition_func[aiger_spec->latches[i].lit] = get_bdd_for_sign_lit(aiger_spec->latches[i].next); }