mirror of
https://github.com/Z3Prover/z3
synced 2025-06-23 06:13:40 +00:00
add job/resource axioms on demand
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
2839f64f0d
commit
40a79694ea
4 changed files with 133 additions and 58 deletions
|
@ -241,6 +241,10 @@ bool csp_util::is_job(expr* e, unsigned& j) {
|
||||||
return is_app_of(e, m_fid, OP_JS_JOB) && (j = job2id(e), true);
|
return is_app_of(e, m_fid, OP_JS_JOB) && (j = job2id(e), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool csp_util::is_job2resource(expr* e, unsigned& j) {
|
||||||
|
return is_app_of(e, m_fid, OP_JS_JOB2RESOURCE) && (j = job2id(e), true);
|
||||||
|
}
|
||||||
|
|
||||||
bool csp_util::is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, uint64_t& start, uint64_t& end) {
|
bool csp_util::is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, uint64_t& start, uint64_t& end) {
|
||||||
if (!is_app_of(e, m_fid, OP_JS_RESOURCE_AVAILABLE)) return false;
|
if (!is_app_of(e, m_fid, OP_JS_RESOURCE_AVAILABLE)) return false;
|
||||||
res = to_app(e)->get_arg(0);
|
res = to_app(e)->get_arg(0);
|
||||||
|
|
|
@ -124,6 +124,7 @@ public:
|
||||||
|
|
||||||
app* mk_job(unsigned j);
|
app* mk_job(unsigned j);
|
||||||
bool is_job(expr* e, unsigned& j);
|
bool is_job(expr* e, unsigned& j);
|
||||||
|
bool is_job2resource(expr* e, unsigned& j);
|
||||||
unsigned job2id(expr* j);
|
unsigned job2id(expr* j);
|
||||||
|
|
||||||
app* mk_resource(unsigned r);
|
app* mk_resource(unsigned r);
|
||||||
|
|
|
@ -114,15 +114,57 @@ namespace smt {
|
||||||
throw default_exception(strm.str());
|
throw default_exception(strm.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void theory_jobscheduler::new_eq_eh(theory_var v1, theory_var v2) {
|
||||||
|
enode* e1 = get_enode(v1);
|
||||||
|
enode* e2 = get_enode(v2);
|
||||||
|
enode* root = e1->get_root();
|
||||||
|
unsigned r;
|
||||||
|
if (u.is_resource(root->get_owner(), r)) {
|
||||||
|
enode* next = e1;
|
||||||
|
do {
|
||||||
|
unsigned j;
|
||||||
|
if (u.is_job2resource(next->get_owner(), j) && !m_jobs[j].m_is_bound) {
|
||||||
|
m_bound_jobs.push_back(j);
|
||||||
|
m_jobs[j].m_is_bound = true;
|
||||||
|
}
|
||||||
|
next = next->get_next();
|
||||||
|
}
|
||||||
|
while (e1 != next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void theory_jobscheduler::new_diseq_eh(theory_var v1, theory_var v2) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void theory_jobscheduler::push_scope_eh() {
|
void theory_jobscheduler::push_scope_eh() {
|
||||||
|
scope s;
|
||||||
|
s.m_bound_jobs_lim = m_bound_jobs.size();
|
||||||
|
s.m_bound_qhead = m_bound_qhead;
|
||||||
|
m_scopes.push_back(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void theory_jobscheduler::pop_scope_eh(unsigned num_scopes) {
|
void theory_jobscheduler::pop_scope_eh(unsigned num_scopes) {
|
||||||
|
unsigned new_lvl = m_scopes.size() - num_scopes;
|
||||||
|
scope const& s = m_scopes[new_lvl];
|
||||||
|
for (unsigned i = s.m_bound_jobs_lim; i < m_bound_jobs.size(); ++i) {
|
||||||
|
unsigned j = m_bound_jobs[i];
|
||||||
|
m_jobs[j].m_is_bound = false;
|
||||||
|
}
|
||||||
|
m_bound_jobs.shrink(s.m_bound_jobs_lim);
|
||||||
|
m_bound_qhead = s.m_bound_qhead;
|
||||||
|
m_scopes.shrink(new_lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
final_check_status theory_jobscheduler::final_check_eh() {
|
final_check_status theory_jobscheduler::final_check_eh() {
|
||||||
TRACE("csp", tout << "\n";);
|
TRACE("csp", tout << "\n";);
|
||||||
bool blocked = false;
|
bool blocked = false;
|
||||||
|
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
||||||
|
if (split_job2resource(j)) {
|
||||||
|
return FC_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
||||||
if (constrain_resource_energy(r)) {
|
if (constrain_resource_energy(r)) {
|
||||||
blocked = true;
|
blocked = true;
|
||||||
|
@ -139,7 +181,7 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool theory_jobscheduler::can_propagate() {
|
bool theory_jobscheduler::can_propagate() {
|
||||||
return false;
|
return m_bound_qhead < m_bound_jobs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
literal theory_jobscheduler::mk_literal(expr * e) {
|
literal theory_jobscheduler::mk_literal(expr * e) {
|
||||||
|
@ -387,19 +429,40 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void theory_jobscheduler::propagate() {
|
void theory_jobscheduler::propagate() {
|
||||||
return;
|
while (m_bound_qhead < m_bound_jobs.size()) {
|
||||||
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
unsigned j = m_bound_jobs[m_bound_qhead++];
|
||||||
|
unsigned r = 0;
|
||||||
job_info const& ji = m_jobs[j];
|
job_info const& ji = m_jobs[j];
|
||||||
unsigned r = resource(j);
|
VERIFY(u.is_resource(ji.m_job2resource->get_root()->get_owner(), r));
|
||||||
propagate_end_time(j, r);
|
TRACE("csp", tout << "job: " << j << " resource: " << r << "\n";);
|
||||||
|
propagate_job2resource(j, r);
|
||||||
}
|
}
|
||||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
}
|
||||||
// TBD: check energy constraints on resources.
|
|
||||||
|
void theory_jobscheduler::propagate_job2resource(unsigned j, unsigned r) {
|
||||||
|
job_info const& ji = m_jobs[j];
|
||||||
|
res_info const& ri = m_resources[r];
|
||||||
|
job_resource const& jr = get_job_resource(j, r);
|
||||||
|
literal eq = mk_eq_lit(ji.m_job2resource, ri.m_resource);
|
||||||
|
assert_last_end_time(j, r, jr, eq);
|
||||||
|
assert_last_start_time(j, r, eq);
|
||||||
|
assert_first_start_time(j, r, eq);
|
||||||
|
vector<res_available> const& available = ri.m_available;
|
||||||
|
for (unsigned i = 0; i + 1 < available.size(); ++i) {
|
||||||
|
SASSERT(available[i].m_end < available[i + 1].m_start);
|
||||||
|
assert_job_not_in_gap(j, r, i, eq);
|
||||||
|
if (!ji.m_is_preemptable && available[i].m_end + 1 < available[i+1].m_start) {
|
||||||
|
assert_job_non_preemptable(j, r, i, eq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
theory_jobscheduler::theory_jobscheduler(ast_manager& m):
|
theory_jobscheduler::theory_jobscheduler(ast_manager& m):
|
||||||
theory(m.get_family_id("csp")), m(m), u(m), a(m) {
|
theory(m.get_family_id("csp")),
|
||||||
|
m(m),
|
||||||
|
u(m),
|
||||||
|
a(m),
|
||||||
|
m_bound_qhead(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& theory_jobscheduler::display(std::ostream & out, job_resource const& jr) const {
|
std::ostream& theory_jobscheduler::display(std::ostream & out, job_resource const& jr) const {
|
||||||
|
@ -593,8 +656,7 @@ namespace smt {
|
||||||
context & ctx = get_context();
|
context & ctx = get_context();
|
||||||
|
|
||||||
// sort availability intervals
|
// sort availability intervals
|
||||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
for (res_info& ri : m_resources) {
|
||||||
res_info& ri = m_resources[r];
|
|
||||||
vector<res_available>& available = ri.m_available;
|
vector<res_available>& available = ri.m_available;
|
||||||
res_available::compare cmp;
|
res_available::compare cmp;
|
||||||
std::sort(available.begin(), available.end(), cmp);
|
std::sort(available.begin(), available.end(), cmp);
|
||||||
|
@ -603,9 +665,7 @@ namespace smt {
|
||||||
expr_ref fml(m);
|
expr_ref fml(m);
|
||||||
literal lit, l1, l2, l3;
|
literal lit, l1, l2, l3;
|
||||||
|
|
||||||
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
for (job_info const& ji : m_jobs) {
|
||||||
job_info const& ji = m_jobs[j];
|
|
||||||
literal_vector disj;
|
|
||||||
if (ji.m_resources.empty()) {
|
if (ji.m_resources.empty()) {
|
||||||
throw default_exception("every job should be associated with at least one resource");
|
throw default_exception("every job should be associated with at least one resource");
|
||||||
}
|
}
|
||||||
|
@ -617,21 +677,15 @@ namespace smt {
|
||||||
time_t start_lb = std::numeric_limits<time_t>::max();
|
time_t start_lb = std::numeric_limits<time_t>::max();
|
||||||
time_t end_ub = 0;
|
time_t end_ub = 0;
|
||||||
for (job_resource const& jr : ji.m_resources) {
|
for (job_resource const& jr : ji.m_resources) {
|
||||||
// resource(j) = r => end(j) <= end(j, r)
|
|
||||||
// resource(j) = r => start(j) <= lst(j, r, end(j, r))
|
|
||||||
unsigned r = jr.m_resource_id;
|
unsigned r = jr.m_resource_id;
|
||||||
res_info const& ri = m_resources[r];
|
res_info const& ri = m_resources[r];
|
||||||
enode* j2r = m_jobs[j].m_job2resource;
|
// literal eq = mk_eq_lit(ji.m_job2resource, ri.m_resource);
|
||||||
literal eq = mk_eq_lit(j2r, ri.m_resource);
|
// disj.push_back(eq);
|
||||||
assert_last_end_time(j, r, jr, eq);
|
|
||||||
assert_last_start_time(j, r, eq);
|
|
||||||
disj.push_back(eq);
|
|
||||||
start_lb = std::min(start_lb, ri.m_available[0].m_start);
|
start_lb = std::min(start_lb, ri.m_available[0].m_start);
|
||||||
end_ub = std::max(end_ub, ri.m_available.back().m_end);
|
end_ub = std::max(end_ub, ri.m_available.back().m_end);
|
||||||
|
|
||||||
}
|
}
|
||||||
// resource(j) = r1 || ... || resource(j) = r_n
|
// resource(j) = r1 || ... || resource(j) = r_n
|
||||||
ctx.mk_th_axiom(get_id(), disj.size(), disj.c_ptr());
|
// ctx.mk_th_axiom(get_id(), disj.size(), disj.c_ptr());
|
||||||
|
|
||||||
// start(j) >= start_lb
|
// start(j) >= start_lb
|
||||||
lit = mk_ge(ji.m_start, start_lb);
|
lit = mk_ge(ji.m_start, start_lb);
|
||||||
|
@ -641,44 +695,18 @@ namespace smt {
|
||||||
lit = mk_le(ji.m_end, end_ub);
|
lit = mk_le(ji.m_end, end_ub);
|
||||||
ctx.mk_th_axiom(get_id(), 1, &lit);
|
ctx.mk_th_axiom(get_id(), 1, &lit);
|
||||||
}
|
}
|
||||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
|
||||||
res_info& ri = m_resources[r];
|
|
||||||
vector<res_available>& available = ri.m_available;
|
|
||||||
if (available.empty()) continue;
|
|
||||||
enode* res = m_resources[r].m_resource;
|
|
||||||
for (unsigned j : ri.m_jobs) {
|
|
||||||
// resource(j) == r => start(j) >= available[0].m_start;
|
|
||||||
enode* j2r = m_jobs[j].m_job2resource;
|
|
||||||
assert_first_start_time(j, r, mk_eq_lit(j2r, res));
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i + 1 < available.size(); ++i) {
|
|
||||||
if (available[i].m_end > available[i + 1].m_start) {
|
|
||||||
throw default_exception("availability intervals should be disjoint");
|
|
||||||
}
|
|
||||||
for (unsigned j : ri.m_jobs) {
|
|
||||||
// jobs start within an interval.
|
|
||||||
// resource(j) == r => start(j) <= available[i].m_end || start(j) >= available[i + 1].m_start;
|
|
||||||
enode* j2r = m_jobs[j].m_job2resource;
|
|
||||||
literal eq = mk_eq_lit(j2r, res);
|
|
||||||
assert_job_not_in_gap(j, r, i, eq);
|
|
||||||
|
|
||||||
// if job is not pre-emptable, start and end have to align within contiguous interval.
|
|
||||||
// resource(j) == r => end(j) <= available[i].m_end || start(j) >= available[i + 1].m_start
|
|
||||||
if (!m_jobs[j].m_is_preemptable && available[i].m_end + 1 < available[i+1].m_start) {
|
|
||||||
assert_job_non_preemptable(j, r, i, eq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TRACE("csp", tout << "add-done end\n";);
|
TRACE("csp", tout << "add-done end\n";);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resource(j) = r => end(j) <= end(j, r)
|
||||||
void theory_jobscheduler::assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq) {
|
void theory_jobscheduler::assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq) {
|
||||||
job_info const& ji = m_jobs[j];
|
job_info const& ji = m_jobs[j];
|
||||||
literal l2 = mk_le(ji.m_end, jr.m_end);
|
literal l2 = mk_le(ji.m_end, jr.m_end);
|
||||||
get_context().mk_th_axiom(get_id(), ~eq, l2);
|
get_context().mk_th_axiom(get_id(), ~eq, l2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resource(j) = r => start(j) <= lst(j, r, end(j, r))
|
||||||
void theory_jobscheduler::assert_last_start_time(unsigned j, unsigned r, literal eq) {
|
void theory_jobscheduler::assert_last_start_time(unsigned j, unsigned r, literal eq) {
|
||||||
context& ctx = get_context();
|
context& ctx = get_context();
|
||||||
time_t t;
|
time_t t;
|
||||||
|
@ -691,12 +719,14 @@ namespace smt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resource(j) = r => start(j) >= avaialble[0].m_start
|
||||||
void theory_jobscheduler::assert_first_start_time(unsigned j, unsigned r, literal eq) {
|
void theory_jobscheduler::assert_first_start_time(unsigned j, unsigned r, literal eq) {
|
||||||
vector<res_available>& available = m_resources[r].m_available;
|
vector<res_available>& available = m_resources[r].m_available;
|
||||||
literal l2 = mk_ge(m_jobs[j].m_start, available[0].m_start);
|
literal l2 = mk_ge(m_jobs[j].m_start, available[0].m_start);
|
||||||
get_context().mk_th_axiom(get_id(), ~eq, l2);
|
get_context().mk_th_axiom(get_id(), ~eq, l2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resource(j) = r => start[idx] <= end(j) || start(j) <= start[idx+1];
|
||||||
void theory_jobscheduler::assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, literal eq) {
|
void theory_jobscheduler::assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, literal eq) {
|
||||||
vector<res_available>& available = m_resources[r].m_available;
|
vector<res_available>& available = m_resources[r].m_available;
|
||||||
literal l2 = mk_ge(m_jobs[j].m_start, available[idx + 1].m_start);
|
literal l2 = mk_ge(m_jobs[j].m_start, available[idx + 1].m_start);
|
||||||
|
@ -704,14 +734,45 @@ namespace smt {
|
||||||
get_context().mk_th_axiom(get_id(), ~eq, l2, l3);
|
get_context().mk_th_axiom(get_id(), ~eq, l2, l3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resource(j) = r => end(j) <= end[idx] || start(j) >= start[idx+1];
|
||||||
void theory_jobscheduler::assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, literal eq) {
|
void theory_jobscheduler::assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, literal eq) {
|
||||||
vector<res_available>& available = m_resources[r].m_available;
|
vector<res_available>& available = m_resources[r].m_available;
|
||||||
literal l2 = mk_le(m_jobs[j].m_end, available[idx].m_end);
|
literal l2 = mk_le(m_jobs[j].m_end, available[idx].m_end);
|
||||||
literal l3 = mk_ge(m_jobs[j].m_start, available[idx + 1].m_start);
|
literal l3 = mk_ge(m_jobs[j].m_start, available[idx + 1].m_start);
|
||||||
get_context().mk_th_axiom(get_id(), ~eq, l2, l3);
|
get_context().mk_th_axiom(get_id(), ~eq, l2, l3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool theory_jobscheduler::split_job2resource(unsigned j) {
|
||||||
|
job_info const& ji = m_jobs[j];
|
||||||
|
context& ctx = get_context();
|
||||||
|
if (ji.m_is_bound) return false;
|
||||||
|
auto const& jrs = ji.m_resources;
|
||||||
|
for (job_resource const& jr : jrs) {
|
||||||
|
unsigned r = jr.m_resource_id;
|
||||||
|
res_info const& ri = m_resources[r];
|
||||||
|
enode* e1 = ji.m_job2resource;
|
||||||
|
enode* e2 = ri.m_resource;
|
||||||
|
if (ctx.is_diseq(e1, e2))
|
||||||
|
continue;
|
||||||
|
literal eq = mk_eq_lit(e1, e2);
|
||||||
|
if (ctx.get_assignment(eq) != l_false) {
|
||||||
|
ctx.mark_as_relevant(eq);
|
||||||
|
if (assume_eq(e1, e2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal_vector lits;
|
||||||
|
for (job_resource const& jr : jrs) {
|
||||||
|
unsigned r = jr.m_resource_id;
|
||||||
|
res_info const& ri = m_resources[r];
|
||||||
|
enode* e1 = ji.m_job2resource;
|
||||||
|
enode* e2 = ri.m_resource;
|
||||||
|
lits.push_back(mk_eq_lit(e1, e2));
|
||||||
|
}
|
||||||
|
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check that each job is run on some resource according to
|
* check that each job is run on some resource according to
|
||||||
|
@ -883,7 +944,6 @@ namespace smt {
|
||||||
cap -= delta;
|
cap -= delta;
|
||||||
}
|
}
|
||||||
if (cap == 0) {
|
if (cap == 0) {
|
||||||
std::cout << "start " << start << "\n";
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,8 @@ namespace smt {
|
||||||
enode* m_start;
|
enode* m_start;
|
||||||
enode* m_end;
|
enode* m_end;
|
||||||
enode* m_job2resource;
|
enode* m_job2resource;
|
||||||
job_info(): m_is_preemptable(false), m_job(nullptr), m_start(nullptr), m_end(nullptr), m_job2resource(nullptr) {}
|
bool m_is_bound;
|
||||||
|
job_info(): m_is_preemptable(false), m_job(nullptr), m_start(nullptr), m_end(nullptr), m_job2resource(nullptr), m_is_bound(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct res_available {
|
struct res_available {
|
||||||
|
@ -98,6 +99,13 @@ namespace smt {
|
||||||
arith_util a;
|
arith_util a;
|
||||||
vector<job_info> m_jobs;
|
vector<job_info> m_jobs;
|
||||||
vector<res_info> m_resources;
|
vector<res_info> m_resources;
|
||||||
|
unsigned_vector m_bound_jobs;
|
||||||
|
unsigned m_bound_qhead;
|
||||||
|
struct scope {
|
||||||
|
unsigned m_bound_jobs_lim;
|
||||||
|
unsigned m_bound_qhead;
|
||||||
|
};
|
||||||
|
svector<scope> m_scopes;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -109,9 +117,9 @@ namespace smt {
|
||||||
|
|
||||||
void assign_eh(bool_var v, bool is_true) override {}
|
void assign_eh(bool_var v, bool is_true) override {}
|
||||||
|
|
||||||
void new_eq_eh(theory_var v1, theory_var v2) override {}
|
void new_eq_eh(theory_var v1, theory_var v2) override;
|
||||||
|
|
||||||
void new_diseq_eh(theory_var v1, theory_var v2) override {}
|
void new_diseq_eh(theory_var v1, theory_var v2) override;
|
||||||
|
|
||||||
void push_scope_eh() override;
|
void push_scope_eh() override;
|
||||||
|
|
||||||
|
@ -180,10 +188,12 @@ namespace smt {
|
||||||
// propagation
|
// propagation
|
||||||
void propagate_end_time(unsigned j, unsigned r);
|
void propagate_end_time(unsigned j, unsigned r);
|
||||||
void propagate_resource_energy(unsigned r);
|
void propagate_resource_energy(unsigned r);
|
||||||
|
void propagate_job2resource(unsigned j, unsigned r);
|
||||||
|
|
||||||
// final check constraints
|
// final check constraints
|
||||||
bool constrain_end_time_interval(unsigned j, unsigned r);
|
bool constrain_end_time_interval(unsigned j, unsigned r);
|
||||||
bool constrain_resource_energy(unsigned r);
|
bool constrain_resource_energy(unsigned r);
|
||||||
|
bool split_job2resource(unsigned j);
|
||||||
|
|
||||||
void assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq);
|
void assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq);
|
||||||
void assert_last_start_time(unsigned j, unsigned r, literal eq);
|
void assert_last_start_time(unsigned j, unsigned r, literal eq);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue