/* ----------------------------------------------------------------- Mal'tsev constraints in C++ by Albert Atserias. Version 1.0: 17/01/2006. Version 1.1: 23/01/2006. Description: Implementation of the polynomial-time algorithm due to Bulatov and Dalmau for solving constraint-satisfaction problems whose constraints are all closed under the same Mal'tsev operation. The running time is cubic (perhaps quartic) on the number of variables and the size of the domain. The implementation is not particularly tuned for efficiency. The only motivation I had for implementing the algorithm was to understand it better. This software is written exclusively for academic purposes. ----------------------------------------------------------------- */ #include #include #include using namespace std; typedef vector< vector< vector > > ternary_fun; typedef vector tuple; typedef set relation; typedef set::iterator iter; struct constraint { int arity; vector variables; relation tuples; }; /* ----------------------------------------------------------------- Class: csp_maltsev Description: Main class implementing a csp-solver when all constraints are closed under the same Mal'tsev operation. It implements the polynomial-time algorithm by Bulatov and Dalmau. Fields: n: number of variables of the instance m: size of the domain constraints: the vector of constraints maltsev_op: the Mal'tsev operation under which all constraints are closed. ----------------------------------------------------------------- */ class csp_maltsev { int n; int m; vector constraints; ternary_fun maltsev_op; public: /* ----------------------------------------------------------------- Constructor: csp_maltsev() Description: This constructor is to be used when the instance is obtained from the standard input using the method read_from_stdin(). ----------------------------------------------------------------- */ csp_maltsev() { } /* ----------------------------------------------------------------- Constructor: csp_maltsev(n,m,op) Description: This constructor is to be used when the instance is given explicitely, except for the constraints that are given one by one using the method impose_new_constraint(constraint& c). ----------------------------------------------------------------- */ csp_maltsev(int n, int m, ternary_fun& op) { this->n = n; this->m = m; maltsev_op = op; constraints=vector(0); solution = fullrelation(n,m); } /* ----------------------------------------------------------------- Constructor: csp_maltsev(n,m,c,op) Description: This constructor is to be used when the whole instance is given explicitely. It does not solve the instance. It does not initialize the current solution. Use solve() to solve the instance. ----------------------------------------------------------------- */ csp_maltsev(int n, int m, vector& c, ternary_fun& op) { this->n = n; this->m = m; maltsev_op = op; constraints=c; } /* ----------------------------------------------------------------- Method: void solve() Description: Solves the instance by initializing the current solution to the full relation and by imposing all constraints one after the other in the order they appear in the vector constraints. ----------------------------------------------------------------- */ void solve() { solution = fullrelation(n,m); int q = constraints.size(); for (int i=0; i(j+1); vector v = vector(j+1); for (int i=0; i 0); return U; } /* ----------------------------------------------------------------- Method: read_from_stdin() Description: Reads an instance from the standard input stream. It expects the following format (without the separating comas): number of variables, size of the domain, the maltsev operation given by its table (a sequence op 4-tuples a,b,c,op(a,b,c)), number of constraints, sequence of constraints each given by the following data: arity, tuple of variables of the constraint, number of valid tuples, sequence of valid tuples. ----------------------------------------------------------------- */ void read_from_stdin() { int aa,bb,cc,q,v,p; cin >> n >> m; maltsev_op = vector< vector< vector > >(3, vector< vector >(3,vector(3))); for (int a=0; a> aa >> bb >> cc >> v; maltsev_op[aa][bb][cc] = v; } cin >> q; constraints = vector(q); for (int i=0; i> c.arity; c.variables = vector(c.arity); for (int j=0; j> c.variables[j]; c.tuples = relation(); cin >> p; for (int j=0; j> t[k]; c.tuples.insert(t); } constraints[i] = c; } } private: /* ----------------------------------------------------------------- Private data structure: struct compact Description: Data structure for describing the compact representation of a relation that is closed under a Mal'tsev operation. Fields: arity: self-explanatory. tuples: a subset of the tuples of the true relation. W1[i][a][b]: first witness for (i,a,b), if any. W2[i][a][b]: second witness for (i,a,b), if any. ----------------------------------------------------------------- */ struct compact { typedef vector< vector< vector > > witnesses; int arity; relation tuples; witnesses W1; witnesses W2; compact() {} compact(int n, int m) { arity = n; tuples = relation(); W1 = vector< vector< vector > >(n,vector< vector >(m,vector(m,tuple(n)))); W2 = vector< vector< vector > >(n,vector< vector >(m,vector(m,tuple(n)))); } }; /* ----------------------------------------------------------------- Private field: solution Description: Current partial solution in compact representation. ----------------------------------------------------------------- */ compact solution; /* ----------------------------------------------------------------- Private function: apply_op(t1,t2,t3) Description: Applies the Mal'tsev operation component-wise to t1,t2, t3. Precondition: all tuples must be of the same length. ----------------------------------------------------------------- */ tuple apply_op(tuple& t1, tuple& t2, tuple& t3) { int n = t1.size(); tuple t = vector(n); for (int i=0; i& I) { int n = I.size(); tuple tt = vector(n); for (int i=0; i& I) { relation B = relation(); for (iter it=A.begin(); it!=A.end(); it++) { tuple t = *it; tuple tt = tuple_projection(t,I); B.insert(tt); } return B; } /* ----------------------------------------------------------------- Private function: extend(c,i,a) Description: Extends the constraint c with one more variable numbered i, and extends the set of valid tuples by appending them a to the end. Precondition: the obvious one. ----------------------------------------------------------------- */ constraint extend(constraint& c, int i, int a) { constraint cc; cc.arity = c.arity+1; cc.variables = c.variables; cc.variables.push_back(i); cc.tuples = relation(); for (iter it=c.tuples.begin(); it!=c.tuples.end(); it++) { tuple t = *it; t.push_back(a); cc.tuples.insert(t); } return cc; } /* ----------------------------------------------------------------- Private function: fullrelation(n,m) Description: Returns a compact representation of the full relation, that is, {0,...,m-1}^n. ----------------------------------------------------------------- */ compact fullrelation(int n, int m) { compact r(n,m); for (int i=0; i(n,0); tuple t2 = vector(n,0); t1[i] = a; t2[i] = b; r.tuples.insert(t1); r.W1[i][a][b] = t1; if (a != b) { r.tuples.insert(t2); r.W2[i][a][b] = t2; } } return r; } /* ----------------------------------------------------------------- Private function: non_empty(r,c) Description: If the relation represented by r contains a tuple that satisfies the constraint c, it returns it. Otherwise it returns an empty tuple. ----------------------------------------------------------------- */ tuple non_empty(compact& r, constraint& c) { relation U = r.tuples; int q; do { q = 0; relation P = projection(U,c.variables); relation N = U; for (iter it1=U.begin(); it1!=U.end(); it1++) for (iter it2=U.begin(); it2!=U.end(); it2++) for (iter it3=U.begin(); it3!=U.end(); it3++) { tuple t1 = *it1; tuple t2 = *it2; tuple t3 = *it3; tuple t = apply_op(t1,t2,t3); tuple tt = tuple_projection(t,c.variables); if (c.tuples.find(tt) != c.tuples.end()) return t; if (P.find(tt) == P.end() and N.find(t) == N.end()) { N.insert(t); q++; } } U = N; } while (q > 0); return vector(); } /* ----------------------------------------------------------------- Private function: fix_values(r,t,l) Description: Given a compact representation r of a relation A, given a tuple t, and given an index l, it returns a compact representation of the set of all tuples in A that agree with t in their first l positions. ----------------------------------------------------------------- */ compact fix_values(compact& r, tuple& t, int l) { compact s = r; for (int j=0; j(2); c.variables[0] = j; c.tuples = relation(); tuple tt = vector(2); tt[0] = t[j]; for (int i=j+1; i0 and t2.size()>0 and t3.size()>0) { ss.tuples.insert(t1); ss.W1[i][a][b] = t1; if (a != b) { tuple t4 = apply_op(t1,t2,t3); ss.tuples.insert(t4); ss.W2[i][a][b] = t4; } } } s = ss; } return s; } /* ----------------------------------------------------------------- Private function: next_beta(r,cc) Description: Given a compact representation r of a relation A and given a constraint cc, it returns a compact representation of the result of imposing the constraint cc on A. It is called next_beta because it is used as an auxiliary function in the method impose_constraint. ----------------------------------------------------------------- */ compact next_beta(compact& r, constraint& cc) { compact s(n,m); for (int i=0; i0) { rr = fix_values(rr,t1,i-1); tuple t2 = non_empty(rr,cc2); if (t2.size()>0) { s.tuples.insert(t1); s.W1[i][a][b] = t1; if (a != b) { s.tuples.insert(t2); s.W2[i][a][b] = t2; } } } } return s; } }; /* ----------------------------------------------------------------- A test program. Description: Reads an instance from the standard input, it solves it, it obtains the collection of all solutions, and it prints them out in the standard output. ----------------------------------------------------------------- */ int main() { csp_maltsev instance = csp_maltsev(); instance.read_from_stdin(); instance.solve(); relation r = instance.all_solutions(); for (iter it=r.begin(); it!=r.end(); it++) { for (int i=0; i<(int)(*it).size(); ++i) cout << (*it)[i] << " "; cout << endl; } cout << endl; }