/*
* Kodkod -- Copyright (c) 2005-2012, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.examples.xpose;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.IntConstant;
import kodkod.ast.Relation;
import kodkod.engine.Evaluator;
import kodkod.engine.Solution;
import kodkod.engine.Solver;
import kodkod.engine.config.Options;
import kodkod.engine.satlab.SATFactory;
import kodkod.instance.Bounds;
import kodkod.instance.Tuple;
import kodkod.instance.TupleFactory;
import kodkod.instance.TupleSet;
import kodkod.instance.Universe;
/**
* A Kodkod "unary" encoding of the Transpose4x4 synthesis problem with
* holes on the left hand side.
*
* @author Emina Torlak
*/
public final class Transpose4x4UnaryL {
final Relation[] mx1 = new Relation[4], mx2 = new Relation[4];
final Relation[] sx1 = new Relation[4], sx2 = new Relation[4];
final Relation[][] mi = new Relation[4][4], si = new Relation[4][4];
final Relation succ;
static final Expression[] ints = new Expression[16];
public Transpose4x4UnaryL() {
for(int i = 0; i < 4; i++) {
mx1[i] = Relation.unary("mx1[" + i + "]");
mx2[i] = Relation.unary("mx2[" + i + "]");
sx1[i] = Relation.unary("sx1[" + i + "]");
sx2[i] = Relation.unary("sx2[" + i + "]");
for(int j = 0; j < 4; j++) {
mi[i][j] = Relation.unary("mi[" + i + ", " + j + "]");
si[i][j] = Relation.unary("si[" + i + ", " + j + "]");
}
}
this.succ = Relation.binary("succ");
for(int i = 0; i < 16; i++) {
ints[i] = IntConstant.constant(i).toExpression();
}
}
/**
* Representation invariants which ensure that every relation
* representing a hole is a singleton.
* @return an encoding of representation invariants
*/
final Formula invariants() {
final List<Formula> inv = new ArrayList<Formula>(32);
for(int i = 0; i < 4; i++) {
inv.add(mx1[i].one());
inv.add(mx2[i].one());
inv.add(sx1[i].one());
inv.add(sx2[i].one());
for(int j = 0; j < 4; j++) {
inv.add(mi[i][j].one());
inv.add(si[i][j].one());
}
}
return Formula.and( inv );
}
/**
* Returns an expression that represents the transpose of m, as implemented by the {@link Transpose4x4#transposeShufps(int[]) } method.
* @return an expression that represents the transpose of m, as implemented by the {@link Transpose4x4#transposeShufps(int[]) } method.
*/
final Expression transposeShufps(Expression m) {
final Expression s = Expression.UNIV.product(ints[0]); // s = new int[16];
final Expression t = Expression.UNIV.product(ints[0]); // t = new int[16];
final Expression s0 = wr4(s, shufps(rd4(m, mx1[0]), rd4(m, mx2[0]), mi[0]), 0); // s0
final Expression s1 = wr4(s0, shufps(rd4(m, mx1[1]), rd4(m, mx2[1]), mi[1]), 4); // s1
final Expression s2 = wr4(s1, shufps(rd4(m, mx1[2]), rd4(m, mx2[2]), mi[2]), 8); // s2
final Expression s3 = wr4(s2, shufps(rd4(m, mx1[3]), rd4(m, mx2[3]), mi[3]), 12); // s3
final Expression t0 = wr4(t, shufps(rd4(s3, sx1[0]), rd4(s3, sx2[0]), si[0]), 0); // t0
final Expression t1 = wr4(t0, shufps(rd4(s3, sx1[1]), rd4(s3, sx2[1]), si[1]), 4); // t1
final Expression t2 = wr4(t1, shufps(rd4(s3, sx1[2]), rd4(s3, sx2[2]), si[2]), 8); // t2
final Expression t3 = wr4(t2, shufps(rd4(s3, sx1[3]), rd4(s3, sx2[3]), si[3]), 12); // t3
return t3;
}
/**
* Encodes the shufps SIMD instruction.
* @requires xmm1.arity = 2 and xmm2.arity = 2 and imm8.length = 4
* @requires all i: [0..3] | imm8[i].arity = 1
* @return 0->xmm1[imm8[0]] + 1->xmm1[imm8[1]] + 2->xmm2[imm8[2]] + 3->xmm2[imm8[3]]
*/
final Expression shufps(Expression xmm1, Expression xmm2, Expression[] imm8) {
return Expression.union(ints[0].product(get(xmm1, imm8[0])),
ints[1].product(get(xmm1, imm8[1])),
ints[2].product(get(xmm2, imm8[2])),
ints[3].product(get(xmm2, imm8[3])));
}
/**
* Encodes the result of reading a subarray of length 4 from the given array, starting at the given index.
* @requires m.arity = 2 and pos.arity = 1
* @return 0->m[pos] + 1->m[pos+1] + 2->[pos+2] + 3->[pos+3]
*/
final Expression rd4(Expression m, Expression pos) {
return Expression.union(ints[0].product(get(m, pos)),
ints[1].product(get(m, add(pos, 1))),
ints[2].product(get(m, add(pos, 2))),
ints[3].product(get(m, add(pos, 3))));
}
/**
* Encodes the result of writing the four elements from the source into the destination array at the specified position.
* @requires dst.arity = 2 and pos.arity = 1 and src.arity = 2
* @return old(dst) ++ (pos->src[0] + (pos+1)->src[1] + (pos+2)->src[2] + (pos+3)->src[3])
*/
final Expression wr4(Expression dst, Expression src, int pos) {
return dst.override(Expression.union( ints[pos].product(get(src, ints[0])),
ints[pos + 1].product(get(src, ints[1])),
ints[pos + 2].product(get(src, ints[2])),
ints[pos + 3].product(get(src, ints[3])) ));
}
/**
* Returns an encoding of sequence lookup using relational join, where
* seq is a sequence (binary relation from integers to values) and idx is an integer.
* @return seq[idx]
*/
final Expression get(Expression seq, Expression idx) {
return idx.join(seq);
}
/**
* Returns an encoding of array update using relational override, where
* seq is a sequence (binary relation from integers to values), idx is an integer, and
* val is the new value for seq at idx.
* @return seq ++ (idx -> val)
*/
final Expression set(Expression seq, Expression idx, Expression val) {
return seq.override(idx.product(val));
}
/**
* Returns the result of adding the non-negative given constant to the given index.
* @requires idx.arity = 1 and k >= 0
* @return idx + k
*/
final Expression add(Expression idx, int k) {
Expression ret = idx;
for(int i = 0; i < k; i++) {
ret = get(succ, ret);
}
return ret;
}
/**
* Returns relation bounds over a universe of interpretation consisting of integers 0 - 15, inclusive.
* @return relation bounds over a universe of interpretation consisting of integers 0 - 15, inclusive.
*/
final Bounds bounds() {
final Universe u = new Universe(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
final TupleFactory f = u.factory();
final Bounds b = new Bounds(u);
// tell the solver to interpret integer objects as their corresponding integer values
for (int i = 0; i < 16; i++)
b.boundExactly(i, f.setOf(i));
final TupleSet s3 = f.setOf(0, 1, 2, 3); // { 0, 1, 2, 3 }
final TupleSet s12 = f.setOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); // { 0, ..., 12 }
for(int i = 0; i < 4; i++) {
b.bound(mx1[i], s12);
b.bound(mx2[i], s12);
b.bound(sx1[i], s12);
b.bound(sx2[i], s12);
for(int j = 0; j < 4; j++) {
b.bound(mi[i][j], s3);
b.bound(si[i][j], s3);
}
}
final TupleSet ord = f.noneOf(2); // { [0, 1], [1, 2], [2, 3], ..., [14, 15] }
for(int i = 0; i < 15; i++)
ord.add(f.tuple((Object)i, i+1));
b.boundExactly(succ, ord);
return b;
}
/**
* Returns the options used for solving.
* @return options used for solving.
*/
final Options options() {
final Options opt = new Options();
opt.setSolver(SATFactory.MiniSat);
opt.setBitwidth(5);
return opt;
}
/**
* Synthesizes selector and mask values for the given input, using {@link Transpose4x4#transpose(int[])}
* as the spec.
* @requires m.length = 16 && (all i: [0..15] | 0 <= m[i] <= 15)
* @return a solution for relations mx1, mx2, mi, sx1, sx2, and si
*/
final Solution solve(int[] m) {
final Solver s = new Solver(options());
return s.solve(invariants().and(toExpr(Transpose4x4.transpose(m)).eq(transposeShufps(toExpr(m)))), bounds());
}
/**
* Converts the given integer array to a binary relation representation.
* @return 0->val[0] + 1->val[1] + ... + (val.length-1)->val[val.length-1]
*/
final static Expression toExpr(int[] val) {
final Expression[] exprs = new Expression[val.length];
for(int i = 0; i < val.length; i++)
exprs[i] = ints[i].product(ints[val[i]]);
return Expression.union(exprs);
}
/** Converts the given tupleset to an array of ints. */
private final static int[] toArray(TupleSet a) {
assert a.arity() == 2;
final int[] ret = new int[a.size()];
final Iterator<Tuple> itr = a.iterator();
for(int i = 0; i < ret.length; i++) {
final Tuple t = itr.next();
assert t.atom(0).equals(i);
ret[i] = (Integer) t.atom(1);
}
return ret;
}
/** Converts the given array of singleton integer relations to an array of ints. */
private final static int[] toArray(Evaluator eval, Expression... r) {
final int[] ret = new int[r.length];
for(int i = 0; i < r.length; i++) {
final TupleSet ts = eval.evaluate(r[i]);
assert ts.arity() == 1 && ts.size() == 1;
ret[i] = (Integer) ts.iterator().next().atom(0);
}
return ret;
}
/** Converts the given array of singleton integer relations to an array of ints. */
private final static int[][] toArray2D(Evaluator eval, Expression[][] r) {
final int[][] ret = new int[r.length][];
for(int i = 0; i < r.length; i++) {
ret[i] = toArray(eval, r[i]);
}
return ret;
}
public static void main(String[] args) {
final int[] a = new int[] { 0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15}; // 4.5 sec
final int[] b = new int[] { 0, 4, 8, 12,
1, 5, 9, 13,
2, 6, 10, 14,
3, 7, 11, 15}; // 6.4 sec
final int[] c = new int[] { 4, 15, 2, 9,
12, 13, 11, 8,
14, 6, 0, 3,
5, 7, 10, 1}; // 1.6 sec
final int[] d = new int[] { 9, 12, 11, 2,
8, 6, 13, 15,
4, 1, 7, 14,
5, 10, 0, 3 }; // 3 sec
final int[] e = new int[] { 2, 9, 5, 11,
15, 4, 3, 6,
13, 10, 1, 14,
0, 12, 8, 7}; // 2 sec
final Transpose4x4UnaryL enc = new Transpose4x4UnaryL();
final Solution sol = enc.solve(a);
System.out.println(sol);
if (sol.instance()==null) {
return;
}
final Evaluator eval = new Evaluator(sol.instance(), enc.options());
System.out.println("\nmx1 = " + Arrays.toString(toArray(eval, enc.mx1)));
System.out.println("mx2 = " + Arrays.toString(toArray(eval, enc.mx2)));
System.out.println("mi = " + Arrays.deepToString(toArray2D(eval, enc.mi)));
System.out.println("sx1 = " + Arrays.toString(toArray(eval, enc.sx1)));
System.out.println("sx2 = " + Arrays.toString(toArray(eval, enc.sx2)));
System.out.println("si = " + Arrays.deepToString(toArray2D(eval, enc.si)));
for(int[] in : new int[][] {a, b, c, d, e}) {
System.out.println("\na = " + Arrays.toString(in));
final int[] expected = Transpose4x4.transpose(in);
final int[] evalTS = toArray(eval.evaluate(enc.transposeShufps(toExpr(in))));
System.out.println("expected(a) = " + Arrays.toString(expected));
System.out.println("transposeShufps(a) = " + Arrays.toString(evalTS));
assert Arrays.equals(expected, evalTS);
}
}
}