mirror of
https://github.com/Z3Prover/z3
synced 2025-04-15 21:38:44 +00:00
na
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
baeff82e59
commit
55f15b0921
|
@ -361,6 +361,9 @@ public:
|
||||||
app * mk_int(int i) {
|
app * mk_int(int i) {
|
||||||
return mk_numeral(rational(i), true);
|
return mk_numeral(rational(i), true);
|
||||||
}
|
}
|
||||||
|
app * mk_int(rational const& r) {
|
||||||
|
return mk_numeral(r, true);
|
||||||
|
}
|
||||||
app * mk_real(int i) {
|
app * mk_real(int i) {
|
||||||
return mk_numeral(rational(i), false);
|
return mk_numeral(rational(i), false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
void theory_jobscheduler::collect_statistics(::statistics & st) const {
|
||||||
|
@ -117,27 +146,27 @@ namespace smt {
|
||||||
return alloc(theory_jobscheduler, new_ctx->get_manager());
|
return alloc(theory_jobscheduler, new_ctx->get_manager());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t theory_jobscheduler::est(unsigned j) {
|
time_t theory_jobscheduler::est(unsigned j) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t theory_jobscheduler::lst(unsigned j) {
|
time_t theory_jobscheduler::lst(unsigned j) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t theory_jobscheduler::ect(unsigned j) {
|
time_t theory_jobscheduler::ect(unsigned j) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t theory_jobscheduler::lct(unsigned j) {
|
time_t theory_jobscheduler::lct(unsigned j) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t theory_jobscheduler::start(unsigned j) {
|
time_t theory_jobscheduler::start(unsigned j) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t theory_jobscheduler::end(unsigned j) {
|
time_t theory_jobscheduler::end(unsigned j) {
|
||||||
return 0;
|
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) {
|
void theory_jobscheduler::add_job_resource(unsigned j, unsigned r, unsigned cap, unsigned loadpct, time_t end) {
|
||||||
// assert: done at base level
|
SASSERT(get_context().at_base_level());
|
||||||
m_jobs.reserve(j + 1);
|
m_jobs.reserve(j + 1);
|
||||||
m_resources.reserve(r + 1);
|
m_resources.reserve(r + 1);
|
||||||
job_info& ji = m_jobs[j];
|
job_info& ji = m_jobs[j];
|
||||||
|
@ -160,12 +189,189 @@ namespace smt {
|
||||||
m_resources[r].m_jobs.push_back(j);
|
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) {
|
void theory_jobscheduler::add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end) {
|
||||||
// assert: done at base level
|
SASSERT(get_context().at_base_level());
|
||||||
SASSERT(start < end);
|
SASSERT(start < end);
|
||||||
m_resources.reserve(r + 1);
|
m_resources.reserve(r + 1);
|
||||||
m_resources[r].m_available.push_back(res_available(max_loadpct, start, end));
|
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 "smt/smt_theory.h"
|
||||||
#include "ast/jobshop_decl_plugin.h"
|
#include "ast/jobshop_decl_plugin.h"
|
||||||
|
#include "ast/arith_decl_plugin.h"
|
||||||
|
|
||||||
namespace smt {
|
namespace smt {
|
||||||
|
|
||||||
|
typedef uint64_t time_t;
|
||||||
|
|
||||||
class theory_jobscheduler : public theory {
|
class theory_jobscheduler : public theory {
|
||||||
|
|
||||||
struct job_resource {
|
struct job_resource {
|
||||||
unsigned m_resource_id; // id of resource
|
unsigned m_resource_id; // id of resource
|
||||||
unsigned m_capacity; // amount of resource to use
|
unsigned m_capacity; // amount of resource to use
|
||||||
unsigned m_loadpct; // assuming loadpct
|
unsigned m_loadpct; // assuming loadpct
|
||||||
uint64_t m_end; // must run before
|
time_t m_end; // must run before
|
||||||
job_resource(unsigned r, unsigned cap, unsigned loadpct, uint64_t end):
|
job_resource(unsigned r, unsigned cap, unsigned loadpct, time_t end):
|
||||||
m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_end(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 {
|
struct job_info {
|
||||||
vector<job_resource> m_resources; // resources allowed to run job.
|
vector<job_resource> m_resources; // resources allowed to run job.
|
||||||
u_map<unsigned> m_resource2index; // resource to index into vector
|
u_map<unsigned> m_resource2index; // resource to index into vector
|
||||||
|
@ -47,24 +62,31 @@ namespace smt {
|
||||||
|
|
||||||
struct res_available {
|
struct res_available {
|
||||||
unsigned m_loadpct;
|
unsigned m_loadpct;
|
||||||
uint64_t m_start;
|
time_t m_start;
|
||||||
uint64_t m_end;
|
time_t m_end;
|
||||||
res_available(unsigned load_pct, uint64_t start, uint64_t end):
|
res_available(unsigned load_pct, time_t start, time_t end):
|
||||||
m_loadpct(load_pct),
|
m_loadpct(load_pct),
|
||||||
m_start(start),
|
m_start(start),
|
||||||
m_end(end)
|
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 {
|
struct res_info {
|
||||||
unsigned_vector m_jobs; // jobs allocated to run on resource
|
unsigned_vector m_jobs; // jobs allocated to run on resource
|
||||||
vector<res_available> m_available; // time intervals where resource is available
|
vector<res_available> m_available; // time intervals where resource is available
|
||||||
uint64_t m_end; // can't run after
|
time_t m_end; // can't run after
|
||||||
res_info(): m_end(std::numeric_limits<uint64_t>::max()) {}
|
res_info(): m_end(std::numeric_limits<time_t>::max()) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
ast_manager& m;
|
ast_manager& m;
|
||||||
jobshop_util u;
|
jobshop_util u;
|
||||||
|
arith_util a;
|
||||||
unsigned_vector m_var2index;
|
unsigned_vector m_var2index;
|
||||||
vector<job_info> m_jobs;
|
vector<job_info> m_jobs;
|
||||||
vector<res_info> m_resources;
|
vector<res_info> m_resources;
|
||||||
|
@ -113,17 +135,31 @@ namespace smt {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// assignments:
|
// assignments:
|
||||||
uint64_t est(unsigned j); // earliest start time of job j
|
time_t est(unsigned j); // earliest start time of job j
|
||||||
uint64_t lst(unsigned j); // last start time
|
time_t lst(unsigned j); // last start time
|
||||||
uint64_t ect(unsigned j); // earliest completion time
|
time_t ect(unsigned j); // earliest completion time
|
||||||
uint64_t lct(unsigned j); // last completion time
|
time_t lct(unsigned j); // last completion time
|
||||||
uint64_t start(unsigned j); // start time of job j
|
time_t start(unsigned j); // start time of job j
|
||||||
uint64_t end(unsigned j); // end time of job j
|
time_t end(unsigned j); // end time of job j
|
||||||
unsigned resource(unsigned j); // resource of job j
|
unsigned resource(unsigned j); // resource of job j
|
||||||
|
|
||||||
// set up model
|
// set up model
|
||||||
void add_job_resource(unsigned j, unsigned r, unsigned cap, unsigned loadpct, 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, uint64_t start, uint64_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…
Reference in a new issue