mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
na
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
baeff82e59
commit
55f15b0921
3 changed files with 273 additions and 28 deletions
|
@ -89,12 +89,41 @@ namespace smt {
|
|||
|
||||
}
|
||||
|
||||
theory_jobscheduler::theory_jobscheduler(ast_manager& m): theory(m.get_family_id("jobshop")), m(m), u(m) {
|
||||
theory_jobscheduler::theory_jobscheduler(ast_manager& m):
|
||||
theory(m.get_family_id("jobshop")), m(m), u(m), a(m) {
|
||||
|
||||
}
|
||||
|
||||
void theory_jobscheduler::display(std::ostream & out) const {
|
||||
|
||||
std::ostream& theory_jobscheduler::display(std::ostream & out, job_resource const& jr) const {
|
||||
return out << " r:" << jr.m_resource_id << " cap:" << jr.m_capacity << " load:" << jr.m_loadpct << " end:" << jr.m_end << "\n";
|
||||
}
|
||||
|
||||
std::ostream& theory_jobscheduler::display(std::ostream & out, job_info const& j) const {
|
||||
for (job_resource const& jr : j.m_resources) {
|
||||
display(out, jr);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& theory_jobscheduler::display(std::ostream & out, res_available const& r) const {
|
||||
out << "[" << r.m_start << ":" << r.m_end << "] @ " << r.m_loadpct << "%%\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& theory_jobscheduler::display(std::ostream & out, res_info const& r) const {
|
||||
for (res_available const& ra : r.m_available) {
|
||||
display(out, ra);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void theory_jobscheduler::display(std::ostream & out) const {
|
||||
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
||||
display(out << "job " << j << ":\n", m_jobs[j]);
|
||||
}
|
||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
||||
display(out << "resource " << r << ":\n", m_resources[r]);
|
||||
}
|
||||
}
|
||||
|
||||
void theory_jobscheduler::collect_statistics(::statistics & st) const {
|
||||
|
@ -117,27 +146,27 @@ namespace smt {
|
|||
return alloc(theory_jobscheduler, new_ctx->get_manager());
|
||||
}
|
||||
|
||||
uint64_t theory_jobscheduler::est(unsigned j) {
|
||||
time_t theory_jobscheduler::est(unsigned j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t theory_jobscheduler::lst(unsigned j) {
|
||||
time_t theory_jobscheduler::lst(unsigned j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t theory_jobscheduler::ect(unsigned j) {
|
||||
time_t theory_jobscheduler::ect(unsigned j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t theory_jobscheduler::lct(unsigned j) {
|
||||
time_t theory_jobscheduler::lct(unsigned j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t theory_jobscheduler::start(unsigned j) {
|
||||
time_t theory_jobscheduler::start(unsigned j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t theory_jobscheduler::end(unsigned j) {
|
||||
time_t theory_jobscheduler::end(unsigned j) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -146,8 +175,8 @@ namespace smt {
|
|||
}
|
||||
|
||||
|
||||
void theory_jobscheduler::add_job_resource(unsigned j, unsigned r, unsigned cap, unsigned loadpct, uint64_t end) {
|
||||
// assert: done at base level
|
||||
void theory_jobscheduler::add_job_resource(unsigned j, unsigned r, unsigned cap, unsigned loadpct, time_t end) {
|
||||
SASSERT(get_context().at_base_level());
|
||||
m_jobs.reserve(j + 1);
|
||||
m_resources.reserve(r + 1);
|
||||
job_info& ji = m_jobs[j];
|
||||
|
@ -160,12 +189,189 @@ namespace smt {
|
|||
m_resources[r].m_jobs.push_back(j);
|
||||
}
|
||||
|
||||
void theory_jobscheduler::add_resource_available(unsigned r, unsigned max_loadpct, uint64_t start, uint64_t end) {
|
||||
// assert: done at base level
|
||||
void theory_jobscheduler::add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end) {
|
||||
SASSERT(get_context().at_base_level());
|
||||
SASSERT(start < end);
|
||||
m_resources.reserve(r + 1);
|
||||
m_resources[r].m_available.push_back(res_available(max_loadpct, start, end));
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialze the state based on the set of jobs and resources added.
|
||||
* For each job j, with possible resources r1, ..., r_n assert
|
||||
* resource(j) = r_1 || resource(j) = r_2 || ... || resource(j) = r_n
|
||||
* For each job and resource r with deadline end(j,r) assert
|
||||
* resource(j) = r => end(j) <= end(j,r)
|
||||
*
|
||||
* Ensure that the availability slots for each resource is sorted by time.
|
||||
*/
|
||||
void theory_jobscheduler::add_done() {
|
||||
context & ctx = get_context();
|
||||
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
||||
job_info const& ji = m_jobs[j];
|
||||
expr_ref_vector disj(m);
|
||||
app_ref job(u.mk_job(j), m);
|
||||
if (ji.m_resources.empty()) {
|
||||
throw default_exception("every job should be associated with at least one resource");
|
||||
}
|
||||
// resource(j) = r => end(j) <= end(j, r)
|
||||
for (job_resource const& jr : ji.m_resources) {
|
||||
app_ref res(u.mk_resource(jr.m_resource_id), m);
|
||||
expr_ref eq(m.mk_eq(job, res), m);
|
||||
expr_ref imp(m.mk_implies(eq, a.mk_le(ji.m_start->get_owner(), a.mk_int(rational(jr.m_end, rational::ui64())))), m);
|
||||
ctx.assert_expr(imp);
|
||||
disj.push_back(eq);
|
||||
}
|
||||
// resource(j) = r1 || ... || resource(j) = r_n
|
||||
expr_ref fml = mk_or(disj);
|
||||
ctx.assert_expr(fml);
|
||||
}
|
||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
||||
vector<res_available>& available = m_resources[r].m_available;
|
||||
res_available::compare cmp;
|
||||
std::sort(available.begin(), available.end(), cmp);
|
||||
for (unsigned i = 0; i < available.size(); ++i) {
|
||||
if (i + 1 < available.size() &&
|
||||
available[i].m_end > available[i + 1].m_start) {
|
||||
throw default_exception("availability intervals should be disjoint");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check that each job is run on some resource according to
|
||||
* requested capacity.
|
||||
*
|
||||
* Check that the sum of jobs run in each time instance
|
||||
* does not exceed capacity.
|
||||
*/
|
||||
void theory_jobscheduler::validate_assignment() {
|
||||
vector<vector<job_time>> start_times, end_times;
|
||||
start_times.reserve(m_resources.size());
|
||||
end_times.reserve(m_resources.size());
|
||||
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
||||
unsigned r = resource(j);
|
||||
start_times[r].push_back(job_time(j, start(j)));
|
||||
end_times[r].push_back(job_time(j, end(j)));
|
||||
time_t cap = capacity_used(j, r, start(j), end(j));
|
||||
job_resource const& jr = get_job_resource(j, r);
|
||||
if (jr.m_capacity > cap) {
|
||||
throw default_exception("job not assigned full capacity");
|
||||
}
|
||||
}
|
||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
||||
unsigned load_pct = 0;
|
||||
unsigned idx;
|
||||
time_t next = 0, start = 0;
|
||||
|
||||
// order jobs running on r by start, end-time intervals
|
||||
// then consume ordered list to find jobs in scope.
|
||||
vector<job_time>& starts = start_times[r];
|
||||
vector<job_time>& ends = end_times[r];
|
||||
job_time::compare cmp;
|
||||
std::sort(starts.begin(), starts.end(), cmp);
|
||||
std::sort(ends.begin(), ends.end(), cmp);
|
||||
unsigned s_idx = 0, e_idx = 0;
|
||||
|
||||
uint_set jobs; // set of jobs currently in scope.
|
||||
while (resource_available(r, start, load_pct, next, idx)) {
|
||||
if (load_pct == 0) {
|
||||
start = next + 1;
|
||||
continue;
|
||||
}
|
||||
// add jobs that begin at or before start.
|
||||
while (s_idx < starts.size() && starts[s_idx].m_time <= start) {
|
||||
jobs.insert(starts[s_idx].m_job);
|
||||
++s_idx;
|
||||
}
|
||||
// remove jobs that end before start.
|
||||
while (e_idx < ends.size() && ends[s_idx].m_time < start) {
|
||||
jobs.remove(ends[e_idx].m_job);
|
||||
++e_idx;
|
||||
}
|
||||
|
||||
// check that sum of job loads does not exceed 100%
|
||||
unsigned cap = 0;
|
||||
for (auto j : jobs) {
|
||||
cap += get_job_resource(j, r).m_loadpct;
|
||||
}
|
||||
if (cap > 100) {
|
||||
throw default_exception("capacity on resource exceeded");
|
||||
}
|
||||
if (s_idx < starts.size()) {
|
||||
// start time of the next unprocessed job.
|
||||
start = starts[s_idx].m_time;
|
||||
}
|
||||
else {
|
||||
// done checking.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
theory_jobscheduler::job_resource const& theory_jobscheduler::get_job_resource(unsigned j, unsigned r) const {
|
||||
job_info const& ji = m_jobs[j];
|
||||
return ji.m_resources[ji.m_resource2index[r]];
|
||||
}
|
||||
|
||||
bool theory_jobscheduler::resource_available(unsigned r, time_t t, unsigned& load_pct, time_t& end, unsigned& idx) {
|
||||
vector<res_available>& available = m_resources[r].m_available;
|
||||
unsigned lo = 0, hi = available.size(), mid = hi / 2;
|
||||
while (lo < hi) {
|
||||
res_available const& ra = available[mid];
|
||||
if (ra.m_start <= t && t <= ra.m_end) {
|
||||
end = ra.m_end;
|
||||
load_pct = ra.m_loadpct;
|
||||
idx = mid;
|
||||
return true;
|
||||
}
|
||||
else if (ra.m_start > t && mid > 0) {
|
||||
hi = mid - 1;
|
||||
mid = lo + (mid - lo) / 2;
|
||||
}
|
||||
else if (ra.m_end < t) {
|
||||
lo = mid + 1;
|
||||
mid += (hi - mid) / 2;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* compute the capacity used by job j on resource r between start and end.
|
||||
* The resource r partitions time intervals into segments where a fraction of
|
||||
* the full capacity of the resource is available. The resource can use up to the
|
||||
* available fraction.
|
||||
*/
|
||||
time_t theory_jobscheduler::capacity_used(unsigned j, unsigned r, time_t start, time_t end) {
|
||||
time_t cap = 0;
|
||||
unsigned j_load_pct = get_job_resource(j, r).m_loadpct;
|
||||
vector<res_available>& available = m_resources[r].m_available;
|
||||
unsigned load_pct = 0;
|
||||
time_t next = 0;
|
||||
unsigned idx = 0;
|
||||
if (!resource_available(r, start, load_pct, next, idx)) {
|
||||
return cap;
|
||||
}
|
||||
while (start < end) {
|
||||
next = std::min(end, next);
|
||||
SASSERT(start < next);
|
||||
cap += (std::min(j_load_pct, load_pct) / j_load_pct) * (next - start - 1);
|
||||
++idx;
|
||||
if (idx == available.size()) {
|
||||
break;
|
||||
}
|
||||
start = available[idx].m_start;
|
||||
next = available[idx].m_end;
|
||||
load_pct = available[idx].m_loadpct;
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -22,20 +22,35 @@ Revision History:
|
|||
|
||||
#include "smt/smt_theory.h"
|
||||
#include "ast/jobshop_decl_plugin.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
typedef uint64_t time_t;
|
||||
|
||||
class theory_jobscheduler : public theory {
|
||||
|
||||
struct job_resource {
|
||||
unsigned m_resource_id; // id of resource
|
||||
unsigned m_capacity; // amount of resource to use
|
||||
unsigned m_loadpct; // assuming loadpct
|
||||
uint64_t m_end; // must run before
|
||||
job_resource(unsigned r, unsigned cap, unsigned loadpct, uint64_t end):
|
||||
time_t m_end; // must run before
|
||||
job_resource(unsigned r, unsigned cap, unsigned loadpct, time_t end):
|
||||
m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_end(end) {}
|
||||
};
|
||||
|
||||
struct job_time {
|
||||
unsigned m_job;
|
||||
time_t m_time;
|
||||
job_time(unsigned j, time_t time): m_job(j), m_time(time) {}
|
||||
|
||||
struct compare {
|
||||
bool operator()(job_time const& jt1, job_time const& jt2) const {
|
||||
return jt1.m_time < jt2.m_time;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct job_info {
|
||||
vector<job_resource> m_resources; // resources allowed to run job.
|
||||
u_map<unsigned> m_resource2index; // resource to index into vector
|
||||
|
@ -47,24 +62,31 @@ namespace smt {
|
|||
|
||||
struct res_available {
|
||||
unsigned m_loadpct;
|
||||
uint64_t m_start;
|
||||
uint64_t m_end;
|
||||
res_available(unsigned load_pct, uint64_t start, uint64_t end):
|
||||
time_t m_start;
|
||||
time_t m_end;
|
||||
res_available(unsigned load_pct, time_t start, time_t end):
|
||||
m_loadpct(load_pct),
|
||||
m_start(start),
|
||||
m_end(end)
|
||||
{}
|
||||
struct compare {
|
||||
bool operator()(res_available const& ra1, res_available const& ra2) const {
|
||||
return ra1.m_start < ra2.m_start;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
struct res_info {
|
||||
unsigned_vector m_jobs; // jobs allocated to run on resource
|
||||
vector<res_available> m_available; // time intervals where resource is available
|
||||
uint64_t m_end; // can't run after
|
||||
res_info(): m_end(std::numeric_limits<uint64_t>::max()) {}
|
||||
time_t m_end; // can't run after
|
||||
res_info(): m_end(std::numeric_limits<time_t>::max()) {}
|
||||
};
|
||||
|
||||
ast_manager& m;
|
||||
jobshop_util u;
|
||||
arith_util a;
|
||||
unsigned_vector m_var2index;
|
||||
vector<job_info> m_jobs;
|
||||
vector<res_info> m_resources;
|
||||
|
@ -113,17 +135,31 @@ namespace smt {
|
|||
|
||||
public:
|
||||
// assignments:
|
||||
uint64_t est(unsigned j); // earliest start time of job j
|
||||
uint64_t lst(unsigned j); // last start time
|
||||
uint64_t ect(unsigned j); // earliest completion time
|
||||
uint64_t lct(unsigned j); // last completion time
|
||||
uint64_t start(unsigned j); // start time of job j
|
||||
uint64_t end(unsigned j); // end time of job j
|
||||
time_t est(unsigned j); // earliest start time of job j
|
||||
time_t lst(unsigned j); // last start time
|
||||
time_t ect(unsigned j); // earliest completion time
|
||||
time_t lct(unsigned j); // last completion time
|
||||
time_t start(unsigned j); // start time of job j
|
||||
time_t end(unsigned j); // end time of job j
|
||||
unsigned resource(unsigned j); // resource of job j
|
||||
|
||||
// set up model
|
||||
void add_job_resource(unsigned j, unsigned r, unsigned cap, unsigned loadpct, uint64_t end);
|
||||
void add_resource_available(unsigned r, unsigned max_loadpct, uint64_t start, uint64_t end);
|
||||
void add_job_resource(unsigned j, unsigned r, unsigned cap, unsigned loadpct, time_t end);
|
||||
void add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end);
|
||||
void add_done();
|
||||
|
||||
// validate assignment
|
||||
void validate_assignment();
|
||||
bool resource_available(unsigned r, time_t t, unsigned& load_pct, time_t& end, unsigned& idx); // load available on resource r at time t.
|
||||
time_t capacity_used(unsigned j, unsigned r, time_t start, time_t end); // capacity used between start and end
|
||||
|
||||
job_resource const& get_job_resource(unsigned j, unsigned r) const;
|
||||
|
||||
std::ostream& display(std::ostream & out, res_info const& r) const;
|
||||
std::ostream& display(std::ostream & out, res_available const& r) const;
|
||||
std::ostream& display(std::ostream & out, job_info const& r) const;
|
||||
std::ostream& display(std::ostream & out, job_resource const& r) const;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue