#ifdef NDEBUG #undef NDEBUG #endif #include "localsat.hpp" #include "config.hpp" #include "dedge.hpp" #include "field-math.hpp" #include #include #include #include #include namespace qflow { const int max_depth = 0; using namespace Eigen; SolverStatus RunCNF(const std::string &fin_name, int n_variable, int timeout, const std::vector> &sat_clause, std::vector &value) { int n_sat_variable = 3 * n_variable; auto fout_name = fin_name + ".result.txt"; FILE *fout = fopen(fin_name.c_str(), "w"); fprintf(fout, "p cnf %d %d\n", n_sat_variable, (int)sat_clause.size()); for (auto &c : sat_clause) { for (auto e : c) fprintf(fout, "%d ", e); fputs("0\n", fout); } fclose(fout); char cmd[100]; snprintf(cmd, 99, "rm %s > /dev/null 2>&1", fout_name.c_str()); system(cmd); snprintf(cmd, 99, "timeout %d minisat %s %s > /dev/null 2>&1", timeout, fin_name.c_str(), fout_name.c_str()); int exit_code = system(cmd); FILE *fin = fopen(fout_name.c_str(), "r"); char buf[16] = {0}; fscanf(fin, "%15s", buf); lprintf(" MiniSAT:"); if (strcmp(buf, "SAT") != 0) { fclose(fin); if (exit_code == 124) { lprintf(" Timeout! "); return SolverStatus::Timeout; } lprintf(" Unsatisfiable! "); return SolverStatus::Unsat; }; lprintf(" Satisfiable! "); for (int i = 0; i < n_variable; ++i) { int sign[3]; fscanf(fin, "%d %d %d", sign + 0, sign + 1, sign + 2); int nvalue = -2; for (int j = 0; j < 3; ++j) { assert(abs(sign[j]) == 3 * i + j + 1); if ((sign[j] > 0) == (value[i] != j - 1)) { assert(nvalue == -2); nvalue = j - 1; } } value[i] = nvalue; } fclose(fin); return SolverStatus::Sat; } SolverStatus SolveSatProblem(int n_variable, std::vector &value, const std::vector flexible, // NOQA const std::vector &variable_eq, const std::vector &constant_eq, const std::vector &variable_ge, const std::vector &constant_ge, int timeout) { for (int v : value) assert(-1 <= v && v <= +1); auto VAR = [&](int i, int v) { int index = 1 + 3 * i + v + 1; // We initialize the SAT problem by setting all the variable to false. // This is because minisat by default will try false first. if (v == value[i]) index = -index; return index; }; int n_flexible = 0; std::vector> sat_clause; std::vector sat_ishard; auto add_clause = [&](const std::vector &clause, bool hard) { sat_clause.push_back(clause); sat_ishard.push_back(hard); }; for (int i = 0; i < n_variable; ++i) { add_clause({-VAR(i, -1), -VAR(i, 0)}, true); add_clause({-VAR(i, +1), -VAR(i, 0)}, true); add_clause({-VAR(i, -1), -VAR(i, +1)}, true); add_clause({VAR(i, -1), VAR(i, 0), VAR(i, +1)}, true); if (!flexible[i]) { add_clause({VAR(i, value[i])}, true); } else { ++n_flexible; } } for (int i = 0; i < (int)variable_eq.size(); ++i) { auto &var = variable_eq[i]; auto &cst = constant_eq[i]; for (int v0 = -1; v0 <= 1; ++v0) for (int v1 = -1; v1 <= 1; ++v1) for (int v2 = -1; v2 <= 1; ++v2) if (cst[0] * v0 + cst[1] * v1 + cst[2] * v2 != 0) { add_clause({-VAR(var[0], v0), -VAR(var[1], v1), -VAR(var[2], v2)}, true); } } for (int i = 0; i < (int)variable_ge.size(); ++i) { auto &var = variable_ge[i]; auto &cst = constant_ge[i]; for (int v0 = -1; v0 <= 1; ++v0) for (int v1 = -1; v1 <= 1; ++v1) for (int v2 = -1; v2 <= 1; ++v2) for (int v3 = -1; v3 <= 1; ++v3) if (cst[0] * v0 * v1 - cst[1] * v2 * v3 < 0) { add_clause({-VAR(var[0], v0), -VAR(var[1], v1), -VAR(var[2], v2), -VAR(var[3], v3)}, false); } } int nflip_before = 0, nflip_after = 0; for (int i = 0; i < (int)variable_ge.size(); ++i) { auto &var = variable_ge[i]; auto &cst = constant_ge[i]; if (value[var[0]] * value[var[1]] * cst[0] - value[var[2]] * value[var[3]] * cst[1] < 0) nflip_before++; } lprintf(" [SAT] nvar: %6d nflip: %3d ", n_flexible * 2, nflip_before); auto rcnf = RunCNF("test.out", n_variable, timeout, sat_clause, value); for (int i = 0; i < (int)variable_eq.size(); ++i) { auto &var = variable_eq[i]; auto &cst = constant_eq[i]; assert(cst[0] * value[var[0]] + cst[1] * value[var[1]] + cst[2] * value[var[2]] == 0); } for (int i = 0; i < (int)variable_ge.size(); ++i) { auto &var = variable_ge[i]; auto &cst = constant_ge[i]; int area = value[var[0]] * value[var[1]] * cst[0] - value[var[2]] * value[var[3]] * cst[1]; if (area < 0) ++nflip_after; } lprintf("nflip: %3d\n", nflip_after); return rcnf; } void ExportLocalSat(std::vector &edge_diff, const std::vector &face_edgeIds, const std::vector &face_edgeOrients, const MatrixXi &F, const VectorXi &V2E, const VectorXi &E2E) { int flip_count = 0; int flip_count1 = 0; std::vector value(2 * edge_diff.size()); for (int i = 0; i < (int)edge_diff.size(); ++i) { value[2 * i + 0] = edge_diff[i][0]; value[2 * i + 1] = edge_diff[i][1]; } std::deque> Q; std::vector mark_vertex(V2E.size(), false); assert(F.cols() == (int)face_edgeIds.size()); std::vector variable_eq(face_edgeIds.size() * 2); std::vector constant_eq(face_edgeIds.size() * 2); std::vector variable_ge(face_edgeIds.size()); std::vector constant_ge(face_edgeIds.size()); VectorXd face_area(F.cols()); for (int i = 0; i < (int)face_edgeIds.size(); ++i) { Vector2i diff[3]; Vector2i var[3]; Vector2i cst[3]; for (int j = 0; j < 3; ++j) { int edgeid = face_edgeIds[i][j]; diff[j] = rshift90(edge_diff[edgeid], face_edgeOrients[i][j]); var[j] = rshift90(Vector2i(edgeid * 2 + 1, edgeid * 2 + 2), face_edgeOrients[i][j]); cst[j] = var[j].array().sign(); var[j] = var[j].array().abs() - 1; } assert(diff[0] + diff[1] + diff[2] == Vector2i::Zero()); variable_eq[2 * i + 0] = Vector3i(var[0][0], var[1][0], var[2][0]); constant_eq[2 * i + 0] = Vector3i(cst[0][0], cst[1][0], cst[2][0]); variable_eq[2 * i + 1] = Vector3i(var[0][1], var[1][1], var[2][1]); constant_eq[2 * i + 1] = Vector3i(cst[0][1], cst[1][1], cst[2][1]); face_area[i] = diff[0][0] * diff[1][1] - diff[0][1] * diff[1][0]; if (face_area[i] < 0) { printf("[SAT] Face %d's area < 0\n", i); for (int j = 0; j < 3; ++j) { int v = F(j, i); if (mark_vertex[v]) continue; Q.push_back(std::make_pair(v, 0)); mark_vertex[v] = true; } flip_count += 1; } variable_ge[i] = Vector4i(var[0][0], var[1][1], var[0][1], var[1][0]); constant_ge[i] = Vector2i(cst[0][0] * cst[1][1], cst[0][1] * cst[1][0]); } for (int i = 0; i < (int)variable_eq.size(); ++i) { auto &var = variable_eq[i]; auto &cst = constant_eq[i]; assert((0 <= var.array()).all()); assert((var.array() < value.size()).all()); assert(cst[0] * value[var[0]] + cst[1] * value[var[1]] + cst[2] * value[var[2]] == 0); } for (int i = 0; i < (int)variable_ge.size(); ++i) { auto &var = variable_ge[i]; auto &cst = constant_ge[i]; assert((0 <= variable_ge[i].array()).all()); assert((variable_ge[i].array() < value.size()).all()); if (value[var[0]] * value[var[1]] * cst[0] - value[var[2]] * value[var[3]] * cst[1] < 0) { assert(face_area[i] < 0); flip_count1++; } } assert(flip_count == flip_count1); // BFS printf("[SAT] Start BFS: Q.size() = %d\n", (int)Q.size()); int mark_count = Q.size(); while (!Q.empty()) { int vertex = Q.front().first; int depth = Q.front().second; Q.pop_front(); mark_count++; int e0 = V2E(vertex); for (int e = e0;;) { int v = F((e + 1) % 3, e / 3); if (!mark_vertex[v]) { int undirected_edge_id = face_edgeIds[e / 3][e % 3]; int undirected_edge_length = edge_diff[undirected_edge_id].array().abs().sum() > 0; int ndepth = depth + undirected_edge_length; if (ndepth <= max_depth) { if (undirected_edge_length == 0) Q.push_front(std::make_pair(v, ndepth)); else Q.push_back(std::make_pair(v, ndepth)); mark_vertex[v] = true; } } e = dedge_next_3(E2E(e)); if (e == e0) break; } } printf("[SAT] Mark %d vertices out of %d\n", mark_count, (int)V2E.size()); std::vector flexible(value.size(), false); for (int i = 0; i < (int)face_edgeIds.size(); ++i) { for (int j = 0; j < 3; ++j) { int edgeid = face_edgeIds[i][j]; if (mark_vertex[F(j, i)] || mark_vertex[F((j + 1) % 3, i)]) { flexible[edgeid * 2 + 0] = true; flexible[edgeid * 2 + 1] = true; } else { assert(face_area[i] >= 0); } } } SolveSatProblem(value.size(), value, flexible, variable_eq, constant_eq, variable_ge, constant_ge); for (int i = 0; i < edge_diff.size(); ++i) { edge_diff[i][0] = value[2 * i + 0]; edge_diff[i][1] = value[2 * i + 1]; } } } // namespace qflow