mirror of
https://github.com/Z3Prover/z3
synced 2025-06-17 03:16:17 +00:00
na
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
3478b8b924
commit
540baa88f4
4 changed files with 106 additions and 53 deletions
|
@ -35,6 +35,7 @@ Revision History:
|
||||||
#include "smt/theory_pb.h"
|
#include "smt/theory_pb.h"
|
||||||
#include "smt/theory_fpa.h"
|
#include "smt/theory_fpa.h"
|
||||||
#include "smt/theory_str.h"
|
#include "smt/theory_str.h"
|
||||||
|
#include "smt/theory_jobscheduler.h"
|
||||||
|
|
||||||
namespace smt {
|
namespace smt {
|
||||||
|
|
||||||
|
@ -119,6 +120,8 @@ namespace smt {
|
||||||
setup_UFLRA();
|
setup_UFLRA();
|
||||||
else if (m_logic == "LRA")
|
else if (m_logic == "LRA")
|
||||||
setup_LRA();
|
setup_LRA();
|
||||||
|
else if (m_logic == "CSP")
|
||||||
|
setup_CSP();
|
||||||
else if (m_logic == "QF_FP")
|
else if (m_logic == "QF_FP")
|
||||||
setup_QF_FP();
|
setup_QF_FP();
|
||||||
else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP")
|
else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP")
|
||||||
|
@ -196,6 +199,8 @@ namespace smt {
|
||||||
setup_QF_DT();
|
setup_QF_DT();
|
||||||
else if (m_logic == "LRA")
|
else if (m_logic == "LRA")
|
||||||
setup_LRA();
|
setup_LRA();
|
||||||
|
else if (m_logic == "CSP")
|
||||||
|
setup_CSP();
|
||||||
else
|
else
|
||||||
setup_unknown(st);
|
setup_unknown(st);
|
||||||
}
|
}
|
||||||
|
@ -916,6 +921,11 @@ namespace smt {
|
||||||
m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params));
|
m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup::setup_CSP() {
|
||||||
|
setup_unknown();
|
||||||
|
m_context.register_plugin(alloc(smt::theory_jobscheduler, m_manager));
|
||||||
|
}
|
||||||
|
|
||||||
void setup::setup_unknown() {
|
void setup::setup_unknown() {
|
||||||
static_features st(m_manager);
|
static_features st(m_manager);
|
||||||
ptr_vector<expr> fmls;
|
ptr_vector<expr> fmls;
|
||||||
|
|
|
@ -81,6 +81,7 @@ namespace smt {
|
||||||
void setup_QF_FPBV();
|
void setup_QF_FPBV();
|
||||||
void setup_QF_S();
|
void setup_QF_S();
|
||||||
void setup_LRA();
|
void setup_LRA();
|
||||||
|
void setup_CSP();
|
||||||
void setup_AUFLIA(bool simple_array = true);
|
void setup_AUFLIA(bool simple_array = true);
|
||||||
void setup_AUFLIA(static_features const & st);
|
void setup_AUFLIA(static_features const & st);
|
||||||
void setup_AUFLIRA(bool simple_array = true);
|
void setup_AUFLIRA(bool simple_array = true);
|
||||||
|
|
|
@ -44,6 +44,7 @@ Features:
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
|
#include "ast/ast_pp.h"
|
||||||
#include "smt/theory_jobscheduler.h"
|
#include "smt/theory_jobscheduler.h"
|
||||||
#include "smt/smt_context.h"
|
#include "smt/smt_context.h"
|
||||||
#include "smt/smt_arith_value.h"
|
#include "smt/smt_arith_value.h"
|
||||||
|
@ -100,6 +101,7 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool theory_jobscheduler::internalize_atom(app * atom, bool gate_ctx) {
|
bool theory_jobscheduler::internalize_atom(app * atom, bool gate_ctx) {
|
||||||
|
TRACE("csp", tout << mk_pp(atom, m) << "\n";);
|
||||||
SASSERT(u.is_model(atom));
|
SASSERT(u.is_model(atom));
|
||||||
for (expr* arg : *atom) {
|
for (expr* arg : *atom) {
|
||||||
internalize_cmd(arg);
|
internalize_cmd(arg);
|
||||||
|
@ -112,77 +114,108 @@ namespace smt {
|
||||||
void theory_jobscheduler::internalize_cmd(expr* cmd) {
|
void theory_jobscheduler::internalize_cmd(expr* cmd) {
|
||||||
symbol key, val;
|
symbol key, val;
|
||||||
rational r;
|
rational r;
|
||||||
if (u.is_kv(cmd, key, r)) {
|
if (u.is_kv(cmd, key, r) && key == ":set-preemptable" && r.is_unsigned()) {
|
||||||
if (key == ":set-preemptable" && r.is_unsigned()) {
|
|
||||||
set_preemptable(r.get_unsigned(), true);
|
set_preemptable(r.get_unsigned(), true);
|
||||||
return;
|
|
||||||
}
|
|
||||||
warning_msg("command not recognized");
|
|
||||||
}
|
}
|
||||||
else if (u.is_alist(cmd, key) && key == ":add-job-resource") {
|
else if (u.is_alist(cmd, key) && key == ":add-job-resource") {
|
||||||
properties ps;
|
properties ps;
|
||||||
unsigned j = 0, res = 0, cap = 0, loadpct = 100;
|
unsigned j = 0, res = 0, cap = 0, loadpct = 100;
|
||||||
time_t end = std::numeric_limits<time_t>::max();
|
time_t end = std::numeric_limits<time_t>::max();
|
||||||
|
bool has_j = false, has_res = false, has_cap = false, has_load = false, has_end = false;
|
||||||
for (expr* arg : *to_app(cmd)) {
|
for (expr* arg : *to_app(cmd)) {
|
||||||
if (u.is_kv(arg, key, r)) {
|
if (u.is_kv(arg, key, r)) {
|
||||||
if (key == ":job") {
|
if (key == ":job" && r.is_unsigned()) {
|
||||||
j = r.get_unsigned();
|
j = r.get_unsigned();
|
||||||
|
has_j = true;
|
||||||
}
|
}
|
||||||
else if (key == ":resource") {
|
else if (key == ":resource" && r.is_unsigned()) {
|
||||||
res = r.get_unsigned();
|
res = r.get_unsigned();
|
||||||
|
has_res = true;
|
||||||
}
|
}
|
||||||
else if (key == ":capacity") {
|
else if (key == ":capacity" && r.is_unsigned()) {
|
||||||
cap = r.get_unsigned();
|
cap = r.get_unsigned();
|
||||||
|
has_cap = true;
|
||||||
}
|
}
|
||||||
else if (key == ":loadpct") {
|
else if (key == ":loadpct" && r.is_unsigned()) {
|
||||||
loadpct = r.get_unsigned();
|
loadpct = r.get_unsigned();
|
||||||
|
has_load = true;
|
||||||
}
|
}
|
||||||
else if (key == ":end") {
|
else if (key == ":end" && r.is_uint64()) {
|
||||||
end = r.get_uint64();
|
end = r.get_uint64();
|
||||||
|
has_end = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unrecognized_argument(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (u.is_alist(arg, key) && key == ":properties") {
|
else if (u.is_alist(arg, key) && key == ":properties") {
|
||||||
// TBD
|
// TBD
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (cap > 0) {
|
|
||||||
add_job_resource(j, res, cap, loadpct, end, ps);
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
warning_msg("no job capacity provided");
|
unrecognized_argument(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!has_j) invalid_argument(":job argument expected ", cmd);
|
||||||
|
if (!has_res) invalid_argument(":resource argument expected ", cmd);
|
||||||
|
if (!has_cap) invalid_argument(":capacity argument expected ", cmd);
|
||||||
|
if (!has_load) invalid_argument(":loadpct argument expected ", cmd);
|
||||||
|
if (!has_end) invalid_argument(":end argument expected ", cmd);
|
||||||
|
if (cap == 0) invalid_argument(":capacity should be positive ", cmd);
|
||||||
|
add_job_resource(j, res, cap, loadpct, end, ps);
|
||||||
|
}
|
||||||
else if (u.is_alist(cmd, key) && key == ":add-resource-available") {
|
else if (u.is_alist(cmd, key) && key == ":add-resource-available") {
|
||||||
properties ps;
|
properties ps;
|
||||||
unsigned res = 0, loadpct = 100;
|
unsigned res = 0, loadpct = 100;
|
||||||
time_t start = 0, end = 0;
|
time_t start = 0, end = 0;
|
||||||
|
bool has_start = false, has_res = false, has_load = false, has_end = false;
|
||||||
|
|
||||||
for (expr* arg : *to_app(cmd)) {
|
for (expr* arg : *to_app(cmd)) {
|
||||||
if (u.is_kv(arg, key, r)) {
|
if (u.is_kv(arg, key, r)) {
|
||||||
if (key == ":resource") {
|
if (key == ":resource" && r.is_unsigned()) {
|
||||||
res = r.get_unsigned();
|
res = r.get_unsigned();
|
||||||
|
has_res = true;
|
||||||
}
|
}
|
||||||
else if (key == ":start") {
|
else if (key == ":start" && r.is_uint64()) {
|
||||||
start = r.get_unsigned();
|
start = r.get_uint64();
|
||||||
|
has_start = true;
|
||||||
}
|
}
|
||||||
else if (key == ":end") {
|
else if (key == ":end" && r.is_uint64()) {
|
||||||
end = r.get_unsigned();
|
end = r.get_uint64();
|
||||||
|
has_end = true;
|
||||||
}
|
}
|
||||||
else if (key == ":loadpct") {
|
else if (key == ":loadpct" && r.is_unsigned()) {
|
||||||
loadpct = r.get_unsigned();
|
loadpct = r.get_unsigned();
|
||||||
|
has_load = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unrecognized_argument(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (u.is_alist(arg, key) && key == ":properties") {
|
else if (u.is_alist(arg, key) && key == ":properties") {
|
||||||
// TBD
|
// TBD
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
unrecognized_argument(arg);
|
||||||
|
}
|
||||||
|
if (!has_res) invalid_argument(":resource argument expected ", cmd);
|
||||||
|
if (!has_load) invalid_argument(":loadpct argument expected ", cmd);
|
||||||
|
if (!has_end) invalid_argument(":end argument expected ", cmd);
|
||||||
|
if (!has_start) invalid_argument(":start argument expected ", cmd);
|
||||||
add_resource_available(res, loadpct, start, end, ps);
|
add_resource_available(res, loadpct, start, end, ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
warning_msg("command not recognized");
|
invalid_argument("command not recognized ", cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void theory_jobscheduler::invalid_argument(char const* msg, expr* arg) {
|
||||||
|
std::ostringstream strm;
|
||||||
|
strm << msg << mk_pp(arg, m);
|
||||||
|
throw default_exception(strm.str());
|
||||||
|
}
|
||||||
|
|
||||||
void theory_jobscheduler::push_scope_eh() {
|
void theory_jobscheduler::push_scope_eh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,29 +289,34 @@ namespace smt {
|
||||||
* iterator of job overlaps.
|
* iterator of job overlaps.
|
||||||
*/
|
*/
|
||||||
theory_jobscheduler::job_overlap::job_overlap(vector<job_time>& starts, vector<job_time>& ends):
|
theory_jobscheduler::job_overlap::job_overlap(vector<job_time>& starts, vector<job_time>& ends):
|
||||||
m_starts(starts), m_ends(ends), s_idx(0), e_idx(0) {
|
m_start(0), m_starts(starts), m_ends(ends), s_idx(0), e_idx(0) {
|
||||||
job_time::compare cmp;
|
job_time::compare cmp;
|
||||||
std::sort(starts.begin(), starts.end(), cmp);
|
std::sort(starts.begin(), starts.end(), cmp);
|
||||||
std::sort(ends.begin(), ends.end(), cmp);
|
std::sort(ends.begin(), ends.end(), cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool theory_jobscheduler::job_overlap::next(time_t& start) {
|
bool theory_jobscheduler::job_overlap::next() {
|
||||||
if (s_idx == m_starts.size()) {
|
if (s_idx == m_starts.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while (s_idx < m_starts.size() && m_starts[s_idx].m_time <= start) {
|
do {
|
||||||
|
m_start = std::max(m_start, m_starts[s_idx].m_time);
|
||||||
|
|
||||||
|
// add jobs that start before or at m_start
|
||||||
|
while (s_idx < m_starts.size() && m_starts[s_idx].m_time <= m_start) {
|
||||||
m_jobs.insert(m_starts[s_idx].m_job);
|
m_jobs.insert(m_starts[s_idx].m_job);
|
||||||
++s_idx;
|
++s_idx;
|
||||||
}
|
}
|
||||||
// remove jobs that end before start.
|
// remove jobs that end before m_start.
|
||||||
while (e_idx < m_ends.size() && m_ends[s_idx].m_time < start) {
|
while (e_idx < m_ends.size() && m_ends[s_idx].m_time < m_start) {
|
||||||
m_jobs.remove(m_ends[e_idx].m_job);
|
m_jobs.remove(m_ends[e_idx].m_job);
|
||||||
++e_idx;
|
++e_idx;
|
||||||
}
|
}
|
||||||
// TBD: check logic
|
|
||||||
if (s_idx < m_starts.size()) {
|
|
||||||
start = m_starts[s_idx].m_time;
|
|
||||||
}
|
}
|
||||||
|
// as long as set of jobs increments, add to m_start
|
||||||
|
while (s_idx < m_starts.size() && e_idx < m_ends.size() &&
|
||||||
|
m_starts[s_idx].m_time <= m_ends[e_idx].m_time);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,11 +398,13 @@ namespace smt {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
literal_vector lits;
|
literal_vector lits;
|
||||||
lits.push_back(~mk_eq(u.mk_job2resource(j), u.mk_resource(r), false));
|
expr* start_e = m_jobs[j].m_start->get_owner();
|
||||||
lits.push_back(~mk_ge_lit(u.mk_start(j), t0));
|
expr* end_e = m_jobs[j].m_end->get_owner();
|
||||||
lits.push_back(~mk_le_lit(u.mk_start(j), t1));
|
lits.push_back(~mk_eq(m_jobs[j].m_job2resource->get_owner(), u.mk_resource(r), false));
|
||||||
expr_ref rhs(a.mk_add(u.mk_start(j), a.mk_int(rational(delta, rational::ui64()))), m);
|
lits.push_back(~mk_ge_lit(start_e, t0));
|
||||||
lits.push_back(mk_eq(u.mk_end(j), rhs, false));
|
lits.push_back(~mk_le_lit(start_e, t1));
|
||||||
|
expr_ref rhs(a.mk_add(start_e, a.mk_int(rational(delta, rational::ui64()))), m);
|
||||||
|
lits.push_back(mk_eq(end_e, rhs, false));
|
||||||
context& ctx = get_context();
|
context& ctx = get_context();
|
||||||
ctx.mk_clause(lits.size(), lits.c_ptr(), nullptr, CLS_AUX_LEMMA, nullptr);
|
ctx.mk_clause(lits.size(), lits.c_ptr(), nullptr, CLS_AUX_LEMMA, nullptr);
|
||||||
return true;
|
return true;
|
||||||
|
@ -388,8 +428,7 @@ namespace smt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
job_overlap overlap(starts, ends);
|
job_overlap overlap(starts, ends);
|
||||||
time_t start = 0;
|
while (overlap.next()) {
|
||||||
while (overlap.next(start)) {
|
|
||||||
unsigned cap = 0;
|
unsigned cap = 0;
|
||||||
auto const& jobs = overlap.jobs();
|
auto const& jobs = overlap.jobs();
|
||||||
for (auto j : jobs) {
|
for (auto j : jobs) {
|
||||||
|
@ -478,6 +517,7 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void theory_jobscheduler::display(std::ostream & out) const {
|
void theory_jobscheduler::display(std::ostream & out) const {
|
||||||
|
out << "jobscheduler:\n";
|
||||||
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
for (unsigned j = 0; j < m_jobs.size(); ++j) {
|
||||||
display(out << "job " << j << ":\n", m_jobs[j]);
|
display(out << "job " << j << ":\n", m_jobs[j]);
|
||||||
}
|
}
|
||||||
|
@ -685,7 +725,7 @@ namespace smt {
|
||||||
// resource(j) == r => start(j) >= available[0].m_start;
|
// resource(j) == r => start(j) >= available[0].m_start;
|
||||||
app_ref job(u.mk_job(j), m);
|
app_ref job(u.mk_job(j), m);
|
||||||
expr_ref eq(m.mk_eq(job, res), m);
|
expr_ref eq(m.mk_eq(job, res), m);
|
||||||
expr_ref ge(mk_ge(u.mk_start(j), available[0].m_start), m);
|
expr_ref ge(mk_ge(m_jobs[j].m_start, available[0].m_start), m);
|
||||||
expr_ref fml(m.mk_implies(eq, ge), m);
|
expr_ref fml(m.mk_implies(eq, ge), m);
|
||||||
ctx.assert_expr(fml);
|
ctx.assert_expr(fml);
|
||||||
}
|
}
|
||||||
|
@ -698,16 +738,16 @@ namespace smt {
|
||||||
// resource(j) == r => start(j) <= available[i].m_end || start(j) >= available[i + 1].m_start;
|
// resource(j) == r => start(j) <= available[i].m_end || start(j) >= available[i + 1].m_start;
|
||||||
app_ref job(u.mk_job(j), m);
|
app_ref job(u.mk_job(j), m);
|
||||||
expr_ref eq(m.mk_eq(job, res), m);
|
expr_ref eq(m.mk_eq(job, res), m);
|
||||||
expr_ref ge(mk_ge(u.mk_start(j), available[i + 1].m_start), m);
|
expr_ref ge(mk_ge(m_jobs[j].m_start, available[i + 1].m_start), m);
|
||||||
expr_ref le(mk_le(u.mk_start(j), available[i].m_end), m);
|
expr_ref le(mk_le(m_jobs[j].m_start, available[i].m_end), m);
|
||||||
fml = m.mk_implies(eq, m.mk_or(le, ge));
|
fml = m.mk_implies(eq, m.mk_or(le, ge));
|
||||||
ctx.assert_expr(fml);
|
ctx.assert_expr(fml);
|
||||||
|
|
||||||
// if job is not pre-emptable, start and end have to align within contiguous interval.
|
// 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
|
// 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) {
|
if (!m_jobs[j].m_is_preemptable && available[i].m_end + 1 < available[i+1].m_start) {
|
||||||
le = mk_le(u.mk_end(j), available[i].m_end);
|
le = mk_le(m_jobs[j].m_end, available[i].m_end);
|
||||||
ge = mk_ge(u.mk_start(j), available[i+1].m_start);
|
ge = mk_ge(m_jobs[j].m_start, available[i+1].m_start);
|
||||||
fml = m.mk_implies(eq, m.mk_or(le, ge));
|
fml = m.mk_implies(eq, m.mk_or(le, ge));
|
||||||
ctx.assert_expr(fml);
|
ctx.assert_expr(fml);
|
||||||
}
|
}
|
||||||
|
@ -742,9 +782,8 @@ namespace smt {
|
||||||
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
for (unsigned r = 0; r < m_resources.size(); ++r) {
|
||||||
// order jobs running on r by start, end-time intervals
|
// order jobs running on r by start, end-time intervals
|
||||||
// then consume ordered list to find jobs in scope.
|
// then consume ordered list to find jobs in scope.
|
||||||
time_t start = 0;
|
|
||||||
job_overlap overlap(start_times[r], end_times[r]);
|
job_overlap overlap(start_times[r], end_times[r]);
|
||||||
while (overlap.next(start)) {
|
while (overlap.next()) {
|
||||||
// check that sum of job loads does not exceed 100%
|
// check that sum of job loads does not exceed 100%
|
||||||
unsigned cap = 0;
|
unsigned cap = 0;
|
||||||
for (auto j : overlap.jobs()) {
|
for (auto j : overlap.jobs()) {
|
||||||
|
|
|
@ -187,12 +187,13 @@ namespace smt {
|
||||||
void block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job);
|
void block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job);
|
||||||
|
|
||||||
class job_overlap {
|
class job_overlap {
|
||||||
|
time_t m_start;
|
||||||
vector<job_time> & m_starts, &m_ends;
|
vector<job_time> & m_starts, &m_ends;
|
||||||
unsigned s_idx, e_idx; // index into starts/ends
|
unsigned s_idx, e_idx; // index into starts/ends
|
||||||
uint_set m_jobs;
|
uint_set m_jobs;
|
||||||
public:
|
public:
|
||||||
job_overlap(vector<job_time>& starts, vector<job_time>& ends);
|
job_overlap(vector<job_time>& starts, vector<job_time>& ends);
|
||||||
bool next(time_t& start);
|
bool next();
|
||||||
uint_set const& jobs() const { return m_jobs; }
|
uint_set const& jobs() const { return m_jobs; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -207,6 +208,8 @@ namespace smt {
|
||||||
literal mk_literal(expr* e);
|
literal mk_literal(expr* e);
|
||||||
|
|
||||||
void internalize_cmd(expr* cmd);
|
void internalize_cmd(expr* cmd);
|
||||||
|
void unrecognized_argument(expr* arg) { invalid_argument("unrecognized argument ", arg); }
|
||||||
|
void invalid_argument(char const* msg, expr* arg);
|
||||||
|
|
||||||
std::ostream& display(std::ostream & out, res_info const& 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, res_available const& r) const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue