Skip to content

Commit 035f2c2

Browse files
committed
add cli options for the generation model + add tests #34
1 parent 12802aa commit 035f2c2

File tree

8 files changed

+132
-93
lines changed

8 files changed

+132
-93
lines changed

src/main/java/cryptator/config/CryptagenConfig.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
public class CryptagenConfig extends CryptaCmdConfig {
1515

16-
@Option(name="-d",handler=ExplicitBooleanOptionHandler.class,usage="dry run (generate but do not solve candidate cryptarithms)")
16+
@Option(name="-d",handler=ExplicitBooleanOptionHandler.class, usage="dry run (generate but do not solve candidate cryptarithms)")
1717
private boolean dryRun;
1818

1919
@Option(name="-ctry",usage="country code for doubly true cryptarithms)")
@@ -31,6 +31,11 @@ public class CryptagenConfig extends CryptaCmdConfig {
3131
@Option(name="-maxop", usage="maximum number of left operands")
3232
private int maxLeftOperands= -1;
3333

34+
@Option(name="-lightM", handler=ExplicitBooleanOptionHandler.class, usage="use less auxiliary variables")
35+
private boolean lightModel;
36+
37+
@Option(name="-lightP", handler=ExplicitBooleanOptionHandler.class, usage="use weak consistency")
38+
private boolean lightPropagation;
3439

3540
public final boolean isDryRun() {
3641
return dryRun;
@@ -56,9 +61,26 @@ public final int getMaxLeftOperands() {
5661
return maxLeftOperands;
5762
}
5863

64+
65+
public final boolean isLightModel() {
66+
return lightModel;
67+
}
68+
69+
public final void setLightModel(boolean lightModel) {
70+
this.lightModel = lightModel;
71+
}
72+
73+
public final boolean isLightPropagation() {
74+
return lightPropagation;
75+
}
76+
77+
public final void setLightPropagation(boolean lightPropagation) {
78+
this.lightPropagation = lightPropagation;
79+
}
80+
5981
@Override
6082
public String toString() {
61-
return super.toString() + "\nc LANG " + langCode + "\nc THREADS " + nthreads;
83+
return super.toString() + "\nc LANG " + langCode + "\nc THREADS " + nthreads+"\nc LIGHT_MOD " + lightModel+"\nc LIGHT_PROPAG " + lightPropagation;
6284
}
6385

6486

src/main/java/cryptator/gen/CryptaGenModel.java

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,17 @@
2020

2121
public class CryptaGenModel extends WordsListModel {
2222

23-
private final ICryptaGenModel left;
23+
//private final ICryptaGenModel left;
24+
//private final CryptaMemberScalar left;
25+
private final CryptaMemberLen left;
2426

2527
private final ICryptaGenModel right;
2628

27-
public CryptaGenModel(String[] words) {
29+
public CryptaGenModel(String[] words, boolean lenOrCardModel) {
2830
super(new Model("Generate"), words);
29-
//left = new CryptaMemberScalar(model, words, "L_");
30-
left = new CryptaMemberScalar(model, words, "L_");
31-
//right = new CryptaMemberScalar(model, words, "R_");
32-
right = new CryptaMemberScalar(model, words, "R_");
31+
left = lenOrCardModel ? new CryptaMemberLen(model, words, "L_") : new CryptaMemberCard(model, words, "L_");
32+
right = new CryptaMemberElt(model, words, "R_");
3333
this.buildModel();
34-
// model.getSolver().showDecisions();
35-
// model.getSolver().limitNode(20);
36-
3734
}
3835

3936

@@ -42,86 +39,88 @@ public void buildModel() {
4239
super.buildModel();
4340
left.buildModel();
4441
right.buildModel();
45-
postLeftOrRightMemberConstraints();
46-
postMemberMaxLenConstraint();
42+
postLeftOrRightConstraints();
43+
postSymBreakLengthLenConstraint();
4744
}
48-
4945

50-
private void postLeftOrRightMemberConstraints() {
46+
47+
@Override
48+
protected void postMaxLengthConstraints() {
49+
model.max(maxLength, left.getMaxLength(), right.getMaxLength()).post();
50+
}
51+
52+
53+
private void postLeftOrRightConstraints() {
5154
final BoolVar[] l = left.getWordVars();
5255
final BoolVar[] r = right.getWordVars();
5356
for (int i = 0; i < vwords.length; i++) {
5457
l[i].add(r[i]).eq(vwords[i]).post();
5558
}
5659
}
5760

58-
59-
private void postMemberMaxLenConstraint() {
60-
right.getMaxLength().ge(left.getMaxLength()).post();
61-
61+
62+
private void postSymBreakLengthLenConstraint() {
63+
left.getMaxLength().le(right.getMaxLength());
64+
6265
}
6366

64-
public void postMemberCardConstraints(int min, int max) {
65-
if(min > 1) left.getWordCount().ge(min).post();
66-
else left.getWordCount().ge(2).post();
67-
// FIXMW why ?
67+
public void postLeftCountConstraints(int min, int max) {
68+
min = Math.max(min, 2);
69+
left.getWordCount().ge(min).post();
6870
if(max >= min) left.getWordCount().le(max).post();
69-
70-
// TODO Option to relax the constraint (allow subtractions in the bignum model)
71-
// Would need to break reflexion symmetry
72-
right.getWordCount().eq(1).post();
71+
7372
}
74-
75-
public void postLeftMinCardConstraints(int base) {
76-
IntVar diff = right.getMaxLength().sub(left.getMaxLength()).intVar();
77-
final int n = getN();
73+
74+
public static void postMinLeftCountConstraints(int maxWordCount, IntVar leftCount, IntVar leftMaxLen,IntVar rightMaxLen, int base) {
75+
IntVar diff = rightMaxLen.sub(leftMaxLen).intVar();
7876
int prod = base;
7977
int i = 2;
80-
while(prod <= n) {
81-
diff.ge(i).imp(left.getWordCount().ge(prod)).post();
78+
while(prod <= maxWordCount) {
79+
diff.ge(i).imp(leftCount.ge(prod + 1)).post();
8280
prod *= base;
8381
i++;
8482
}
8583
diff.lt(i).post();
8684
}
87-
88-
public void postRightMemberConstraint() {
85+
86+
public void postMinLeftCountConstraints(int base) {
87+
left.postLentghSumConstraints(right.getMaxLength(), base);
88+
}
89+
90+
public void postFixedRightMemberConstraint() {
8991
final BoolVar[] vars = right.getWordVars();
9092
vars[vars.length - 1].eq(1).post();
9193
}
9294

9395
public void postDoublyTrueConstraint(int lb) {
9496
final int n = getN();
9597
final IntVar sum = model.intVar("SUM", lb, n - 1);
96-
98+
9799
final IntVar[] lvars = new IntVar[n+1];
98100
System.arraycopy(left.getWordVars(), 0, lvars, 0, n);
99101
lvars[n] = sum;
100-
102+
101103
final IntVar[] rvars = new IntVar[n+1];
102104
System.arraycopy(right.getWordVars(), 0, rvars, 0, n);
103105
rvars[n] = sum;
104-
106+
105107
final int[] coeffs = ArrayUtils.array(0, n);
106108
coeffs[n] = -1;
107-
109+
108110
model.scalar(lvars, coeffs, "=", 0).post();
109111
model.scalar(rvars, coeffs, "=", 0).post();
110112
}
111113

112-
public final ICryptaNode recordCryptarithm() {
113-
// TODO Check that members are non-null.
114-
return new CryptaNode(CryptaOperator.EQ, recordAddition(left), recordAddition(right));
114+
public final ICryptaNode recordCryptarithm() {
115+
final ICryptaNode l = recordAddition(left);
116+
final ICryptaNode r = recordAddition(right);
117+
return r == null || l == null ? null : new CryptaNode(CryptaOperator.EQ, l, r);
115118
}
116119

117120

118121
@Override
119122
public String toString() {
120123
return left.toString() + " = " + right.toString();
121124
}
122-
123-
124-
125-
126125

127126
}

src/main/java/cryptator/gen/CryptaListGenerator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ public final int getErrorCount() {
5757
}
5858

5959
private CryptaGenModel buildModel() {
60-
final CryptaGenModel gen = new CryptaGenModel(words.getWords());
61-
gen.postMemberCardConstraints(config.getMinLeftOperands(), config.getMaxLeftOperands());
60+
final CryptaGenModel gen = new CryptaGenModel(words.getWords(), config.isLightModel());
61+
gen.postLeftCountConstraints(config.getMinLeftOperands(), config.getMaxLeftOperands());
6262
gen.postMaxSymbolCountConstraint(config.getArithmeticBase());
63-
gen.postLeftMinCardConstraints(config.getArithmeticBase());
64-
if(words.hasRightMember()) gen.postRightMemberConstraint();
63+
if(!config.isLightPropagation()) gen.postMinLeftCountConstraints(config.getArithmeticBase());
64+
if(words.hasRightMember()) gen.postFixedRightMemberConstraint();
6565
if(words.isDoublyTrue()) gen.postDoublyTrueConstraint(words.getLB());
6666
return gen;
6767
}

src/main/java/cryptator/gen/CryptaMemberCard.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.stream.IntStream;
1313

1414
import org.chocosolver.solver.Model;
15+
import org.chocosolver.solver.constraints.extension.Tuples;
1516
import org.chocosolver.solver.variables.IntVar;
1617

1718
public class CryptaMemberCard extends CryptaMemberLen {
@@ -41,6 +42,30 @@ private void postGlobalCardLengthConstraint() {
4142
model.globalCardinality(lengths, values, cardLengths, true).post();
4243
}
4344

45+
public void postLentghSumConstraints(IntVar sumLength, int base) {
46+
47+
int[] maxCardLengths = getLengthCounts(words);
48+
final int maxCard = IntStream.of(maxCardLengths).max().orElse(0);
49+
50+
IntVar[] vars = Arrays.copyOf(cardLengths, maxCard);
51+
vars[0] = model.intVar(0);
52+
53+
IntVar x = model.intVar("Xk", 0, maxCard,false);
54+
model.element(x, cardLengths, maxLength, 0).post();
55+
56+
IntVar y = model.intVar("Yk", 0, maxCard,false);
57+
model.element(y, cardLengths, maxLength.sub(1).intVar(), 0).post();
58+
59+
IntVar z = model.intVar("Zk", 0, maxCard,false);
60+
z.eq(wordCount.sub(x).sub(y)).decompose().post();
61+
62+
WordSumTuplesBuilder builder = new WordSumTuplesBuilder(base);
63+
Tuples tuples = builder.buildTuples(maxCardLengths);
64+
65+
model.table(new IntVar[] {maxLength, x, y, z, sumLength}, tuples).post();
66+
}
67+
68+
4469
@Override
4570
public void buildModel() {
4671
super.buildModel();

src/main/java/cryptator/gen/CryptaMemberLen.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,20 @@ protected void postMaxLengthConstraints() {
4040
model.max(maxLength, lengths).post();
4141
}
4242

43+
public void postLentghSumConstraints(IntVar sumLength, int base) {
44+
IntVar diff = sumLength.sub(maxLength).intVar();
45+
final int n = getN();
46+
int prod = base;
47+
int i = 2;
48+
while(prod <= n) {
49+
//System.err.println(i + " " + (prod + 1));
50+
diff.ge(i).imp(wordCount.ge(prod + 1)).post();
51+
prod *= base;
52+
i++;
53+
}
54+
diff.lt(i).post();
55+
}
56+
57+
58+
4359
}

src/main/java/cryptator/gen/CryptaMemberScalar.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/test/java/cryptator/GenModelTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import cryptator.gen.CryptaMemberCard;
1818
import cryptator.gen.CryptaMemberElt;
1919
import cryptator.gen.CryptaMemberLen;
20-
import cryptator.gen.CryptaMemberScalar;
2120
import cryptator.gen.WordsListModel;
2221
import cryptator.specs.ICryptaGenModel;
2322

@@ -30,7 +29,6 @@ public class GenModelTest {
3029
@Before
3130
public void buildGenModels() {
3231
models = new ICryptaGenModel[] {
33-
new CryptaMemberScalar(new Model(), words, ""),
3432
new CryptaMemberLen(new Model(), words, ""),
3533
new CryptaMemberCard(new Model(), words, ""),
3634
new WordsListModel(new Model(), words)

src/test/java/cryptator/GenerateTest.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,49 +27,59 @@ public class GenerateTest {
2727
public static void configureTestLoggers() {
2828
JULogUtil.configureTestLoggers();
2929
}
30-
31-
public long testGenerate(WordArray wordArray) throws CryptaModelException {
30+
31+
public long testGenerate(WordArray wordArray, boolean lightModel, boolean lightPropagation) throws CryptaModelException {
3232
final CryptagenConfig config = new CryptagenConfig();
33+
config.setLightModel(lightModel);
34+
config.setLightPropagation(lightPropagation);
3335
final CryptaListGenerator gen = new CryptaListGenerator(wordArray, config, Cryptagen.LOGGER);
3436
CryptaBiConsumer cons = new CryptaBiConsumer(Cryptagen.LOGGER);
3537
cons.withSolutionCheck(config.getArithmeticBase());
3638
gen.generate(cons);
3739
assertEquals(0, cons.getErrorCount());
3840
return cons.getSolutionCount();
3941
}
42+
43+
44+
public void testGenerate(int expectedSolCount, WordArray wordArray) throws CryptaModelException {
45+
assertEquals( expectedSolCount,testGenerate(wordArray, false, false));
46+
assertEquals( expectedSolCount,testGenerate(wordArray, false, true));
47+
assertEquals( expectedSolCount,testGenerate(wordArray, true, false));
48+
assertEquals( expectedSolCount,testGenerate(wordArray, true, true));
49+
}
4050

41-
public long testGenerate(String rightMember, String... words) throws CryptaModelException {
42-
return testGenerate(new WordArray(Arrays.asList(words), rightMember));
51+
public void testGenerate(int expectedSolCount, String rightMember, String... words) throws CryptaModelException {
52+
testGenerate(expectedSolCount, new WordArray(Arrays.asList(words), rightMember));
4353
}
4454

4555
@Test
4656
public void testSendMoreMoney() throws CryptaModelException {
47-
assertEquals(1, testGenerate(null, "send", "more", "money"));
57+
testGenerate(1, null, "send", "more", "money");
4858
}
4959

5060
@Test
5161
public void testPlanets1() throws CryptaModelException {
52-
assertEquals(2, testGenerate(null, "venus", "earth", "uranus", "saturn"));
62+
testGenerate(2, null, "venus", "earth", "uranus", "saturn");
5363
}
5464

5565
@Test
5666
public void testPlanets2() throws CryptaModelException {
57-
assertEquals(1, testGenerate("planets", "venus", "earth", "uranus", "saturn"));
67+
testGenerate(1, "planets", "venus", "earth", "uranus", "saturn");
5868
}
5969

6070
@Test
6171
public void testDoublyTrue1() throws CryptaModelException {
62-
assertEquals(0, testGenerate(new WordArray("FR", "fr", 0, 10)));
72+
testGenerate(0, new WordArray("FR", "fr", 0, 10));
6373
}
6474

6575
@Test
6676
public void testDoublyTrue2() throws CryptaModelException {
67-
assertEquals(1, testGenerate(new WordArray("FR", "fr", 30, 30)));
77+
testGenerate(1, new WordArray("FR", "fr", 30, 30));
6878
}
6979

7080
@Test
7181
public void testDoublyTrue3() throws CryptaModelException {
72-
assertEquals(3, testGenerate(new WordArray("IT", "it", 20, 30)));
82+
testGenerate(3, new WordArray("IT", "it", 20, 30));
7383
}
7484

7585

0 commit comments

Comments
 (0)