Skip to content

Commit ef898a0

Browse files
authored
T-wise combinatorial (Solved Concerns from Lewis) (#838)
* Solved Concerns from Lewis: + new allow twise number be 1 + unify twise number valid condition + add unit test * add new test case * unit test for ParamAssignment.IterateAssignments
1 parent c7e86ab commit ef898a0

File tree

7 files changed

+126
-22
lines changed

7 files changed

+126
-22
lines changed

Src/PCompiler/CompilerCore/DefaultTranslationErrorHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ public Exception ModifyGlobalParam(ParserRuleContext location, IPDecl existing)
7272
$"try to modify a global param '{existing.Name}' at {locationResolver.GetLocation(existing.SourceLocation)}");
7373
}
7474

75-
public Exception InvalidTwise(ParserRuleContext location, IPDecl testDecl, int twiseNum, int paramNum)
75+
public Exception InvalidTwise(ParserRuleContext location, IPDecl testDecl, string errMsg)
7676
{
7777
return IssueError(location,
78-
$"invalid twise: wise-number({twiseNum}) should great equal than 2 and less equal than number of parameters ({paramNum}) at {locationResolver.GetLocation(testDecl.SourceLocation)}");
78+
$"invalid twise number at {locationResolver.GetLocation(testDecl.SourceLocation)}: {errMsg}");
7979
}
8080

8181
public Exception IncorrectArgumentCount(ParserRuleContext location, int actualCount, int expectedCount)

Src/PCompiler/CompilerCore/ITranslationErrorHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Exception DuplicateStartState(
3131

3232
Exception ModifyGlobalParam(ParserRuleContext location, IPDecl existing);
3333

34-
Exception InvalidTwise(ParserRuleContext location, IPDecl testDecl, int twiseNum, int paramNum);
34+
Exception InvalidTwise(ParserRuleContext location, IPDecl testDecl, string errMsg);
3535

3636
Exception IncorrectArgumentCount(ParserRuleContext location, int actualCount, int expectedCount);
3737

Src/PCompiler/CompilerCore/TypeChecker/AST/Declarations/Variable.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public Variable(string name, ParserRuleContext sourceNode, VariableRole role)
1212
SourceLocation = sourceNode;
1313
Role = role;
1414
}
15+
16+
public Variable(string name)
17+
{
18+
Name = name;
19+
}
1520

1621
public VariableRole Role { get; }
1722
public PLanguageType Type { get; set; }

Src/PCompiler/CompilerCore/TypeChecker/ModuleExprVisitor.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,12 @@ public static void PopulateAllModuleExprs(
6060
if (context.globalParam != null)
6161
{
6262
var expr = (NamedTupleExpr)paramExprVisitor.Visit(context.globalParam);
63-
test.ParamExprMap = EnumerateParamAssignments (handler, globalScope, context.globalParam, expr);
63+
test.ParamExprMap = EnumerateParamAssignments(handler, globalScope, context.globalParam, expr);
6464
}
6565
if (context.assumeExpr != null)
6666
{
6767
test.AssumeExpr = exprVisitor.Visit(context.assumeExpr);
6868
}
69-
7069
if (context.twise() == null)
7170
{
7271
test.Twise = test.ParamExprMap.Count;
@@ -76,12 +75,14 @@ public static void PopulateAllModuleExprs(
7675
if (context.twise().PAIRWISE() != null)
7776
{
7877
test.Twise = 2;
79-
} else if (context.twise().WISE() != null)
78+
}
79+
else if (context.twise().WISE() != null)
8080
{
8181
var t = int.Parse(context.twise().IntLiteral().GetText());
82-
if (!(2 <= t && t <= test.ParamExprMap.Count))
82+
var (isValid, errMsg) = ParamAssignment.TwiseNumWellFormednessCheck(t, test.ParamExprMap.Count);
83+
if (!isValid)
8384
{
84-
throw handler.InvalidTwise(context.twise(), test, t, test.ParamExprMap.Count);
85+
throw handler.InvalidTwise(context.twise(), test, errMsg);
8586
}
8687
test.Twise = t;
8788
}

Src/PCompiler/CompilerCore/TypeChecker/ParamAssignment.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,28 @@ namespace Plang.Compiler.TypeChecker;
88

99
public abstract class ParamAssignment
1010
{
11-
private static IEnumerable<IEnumerable<T>> DifferentCombinations<T>(IEnumerable<T> elements, int k)
11+
private static IEnumerable<IEnumerable<T>> DifferentCombinationsAux<T>(IEnumerable<T> elements, int k)
1212
{
1313
var enumerable = elements as T[] ?? elements.ToArray();
1414
return k == 0
1515
? [Array.Empty<T>()]
1616
: enumerable.SelectMany((e, i) =>
17-
DifferentCombinations(enumerable.Skip(i + 1), k - 1).Select(c => new[] { e }.Concat(c)));
17+
DifferentCombinationsAux(enumerable.Skip(i + 1), k - 1).Select(c => new[] { e }.Concat(c)));
18+
}
19+
20+
public static HashSet<HashSet<T>> DifferentCombinations<T>(IEnumerable<T> elements, int k)
21+
{
22+
var res = DifferentCombinationsAux(elements, k);
23+
return new HashSet<HashSet<T>>(res.Select(innerEnumerable => new HashSet<T>(innerEnumerable)),
24+
HashSet<T>.CreateSetComparer());
25+
}
26+
27+
// Should use sum type (Option) instead of product type
28+
public static (bool, string) TwiseNumWellFormednessCheck(int twise, int numParams)
29+
{
30+
return twise < 1
31+
? (false, $"twise number {twise} is less than 1.")
32+
: (twise > numParams ? (false, $"twise number {twise} is greater than {numParams}.") : (true, ""));
1833
}
1934

2035
private static Dictionary<int, T> EnumerableToIndexDict<T>(IEnumerable<T> l, Func<T, T> f)
@@ -37,19 +52,15 @@ private static Dictionary<int, Dictionary<string, int>> MakeVectorMap(Dictionary
3752
foreach (var vector in resultCopy) vector.Add(name, i);
3853
newResult.AddRange(resultCopy);
3954
}
40-
4155
result = newResult;
4256
}
43-
4457
vectorSet.UnionWith(result);
4558
}
46-
4759
return EnumerableToIndexDict(vectorSet, x => x);
4860
}
4961

5062
private static Dictionary<int, HashSet<int>> MakeAssignmentCoverageMap(
51-
Dictionary<int, Dictionary<string, int>> assignmentMap,
52-
Dictionary<int, Dictionary<string, int>> vectorMap,
63+
Dictionary<int, Dictionary<string, int>> assignmentMap, Dictionary<int, Dictionary<string, int>> vectorMap,
5364
List<int> assignments)
5465
{
5566
var assignmentCoverageMap = new Dictionary<int, HashSet<int>>();
@@ -59,7 +70,6 @@ private static Dictionary<int, HashSet<int>> MakeAssignmentCoverageMap(
5970
foreach (var kv in vectorMap.Where(kv => kv.Value.All(assignmentMap[assignment].Contains)))
6071
assignmentCoverageMap[assignment].Add(kv.Key);
6172
}
62-
6373
return assignmentCoverageMap;
6474
}
6575

@@ -83,7 +93,6 @@ private static List<int> GreedyCoverageExplore(Dictionary<string, int> universe,
8393
foreach (var kv in assignmentCoverageMap) assignmentCoverageMap[kv.Key].ExceptWith(coverage);
8494
result.Add(ass);
8595
}
86-
8796
return result;
8897
}
8998

@@ -105,7 +114,6 @@ private static Dictionary<Variable, IPExpr> IndexDic2Dic(List<Variable> globalPa
105114
if (i >= values.Count) throw new ArgumentException("Index out of range in global variable config.");
106115
dic[variable] = values[i];
107116
}
108-
109117
return dic;
110118
}
111119

@@ -123,12 +131,11 @@ private static bool Next((string, int)[] indexArr, IDictionary<string, List<IPEx
123131
if (indexArr[i].Item2 < globalParams[indexArr[i].Item1].Count) return true;
124132
indexArr[i] = (indexArr[i].Item1, 0);
125133
}
126-
127134
return false;
128135
}
129136

130137
public static void IterateAssignments(SafetyTest safety, List<Variable> globalParams,
131-
Action<Dictionary<Variable, IPExpr>> f)
138+
Action<Dictionary<Variable, IPExpr>> generateTestCode)
132139
{
133140
var indexArr = safety.ParamExprMap.ToList()
134141
.Zip(Enumerable.Repeat(0, safety.ParamExprMap.Count), (x, y) => (x.Key, y)).ToArray();
@@ -141,8 +148,7 @@ public static void IterateAssignments(SafetyTest safety, List<Variable> globalPa
141148
if (!SimpleExprEval.ForceBool(SimpleExprEval.Eval(dic, safety.AssumeExpr))) continue;
142149
assignmentIndices.Add(indexDic);
143150
} while (Next(indexArr, safety.ParamExprMap));
144-
145151
foreach (var i in GreedyCoverageExplore(universe, assignmentIndices, safety.Twise))
146-
f(IndexDic2Dic(globalParams, safety.ParamExprMap, assignmentIndices[i]));
152+
generateTestCode(IndexDic2Dic(globalParams, safety.ParamExprMap, assignmentIndices[i]));
147153
}
148154
}

Tst/TestParamTester/1_ClientServer/PTst/Testscript.p

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ test param (nClients in [2, 3, 4], g1 in [1,2], g2 in [4, 5], b1 in [true, false
4747
assert BankBalanceIsAlwaysCorrect, GuaranteedWithDrawProgress in
4848
(union Client, Bank, { TestWithConfig });
4949

50+
test param (nClients in [2, 3, 4], g1 in [1,2], g2 in [4, 5], b1 in [true, false])
51+
assume (b1 == (nClients + g1 > g2))
52+
(1 wise) testTWise1 [main=TestWithConfig]:
53+
assert BankBalanceIsAlwaysCorrect, GuaranteedWithDrawProgress in
54+
(union Client, Bank, { TestWithConfig });
55+
5056

5157
// Syntax error
5258
// paramtest () wrong1 [main=TestWithSingleClient]:
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
using Plang.Compiler.TypeChecker;
5+
using Plang.Compiler.TypeChecker.AST;
6+
using Plang.Compiler.TypeChecker.AST.Declarations;
7+
using Plang.Compiler.TypeChecker.AST.Expressions;
8+
9+
namespace UnitTests.TypeChecker;
10+
11+
[TestFixture]
12+
[TestOf(typeof(ParamAssignment))]
13+
public class ParamAssignmentTest
14+
{
15+
[Test]
16+
public void TestDifferentCombinations()
17+
{
18+
var input = new List<int> { 1, 2, 3 };
19+
var expected = new HashSet<HashSet<int>> { new() { 1, 2 }, new() { 1, 3 }, new() { 2, 3 } };
20+
Assert.AreEqual(ParamAssignment.DifferentCombinations(input, 2), expected);
21+
expected = [new HashSet<int> { 1 }, new HashSet<int> { 2 }, new HashSet<int> { 3 }];
22+
Assert.AreEqual(ParamAssignment.DifferentCombinations(input, 1), expected);
23+
expected = [new HashSet<int> { 1, 2, 3 }];
24+
Assert.AreEqual(ParamAssignment.DifferentCombinations(input, 3), expected);
25+
}
26+
27+
private static Dictionary<string, int> MakeSingleAssignment(int a, int b, int c)
28+
{
29+
return new Dictionary<string, int> { { "a", a }, { "b", b }, { "c", c } };
30+
}
31+
32+
private static HashSet<Dictionary<string, int>> MakeExpectAssignments(int i)
33+
{
34+
return i switch
35+
{
36+
1 => // Total 3 obligations
37+
[
38+
MakeSingleAssignment(1, 1, 1), MakeSingleAssignment(2, 2, 2), MakeSingleAssignment(1, 3, 1)
39+
],
40+
2 => // Total 2*3 + 2*2 + 3*2 = 16 obligations
41+
[
42+
MakeSingleAssignment(1, 1, 1), // (a = 1, b = 1) (a = 1, c = 1) (b = 1, c = 1)
43+
MakeSingleAssignment(2, 2, 1), // (a = 2, b = 2) (a = 2, c = 1) (b = 2, c = 1)
44+
MakeSingleAssignment(2, 1, 2), // (a = 2, b = 1) (a = 2, c = 2) (b = 1, c = 2)
45+
MakeSingleAssignment(1, 2, 2), // (a = 1, b = 2) (a = 1, c = 2) (b = 2, c = 2)
46+
MakeSingleAssignment(1, 3, 1), // (a = 1, b = 3) (b = 3, c = 1)
47+
MakeSingleAssignment(2, 3, 2) // (a = 2, b = 3) (b = 3, c = 2)
48+
],
49+
3 => // Total 2*3*2 = 12 obligations, need all combinations
50+
[
51+
MakeSingleAssignment(1, 1, 1), MakeSingleAssignment(2, 1, 1), MakeSingleAssignment(1, 2, 1),
52+
MakeSingleAssignment(2, 2, 1), MakeSingleAssignment(1, 3, 1), MakeSingleAssignment(2, 3, 1),
53+
MakeSingleAssignment(1, 1, 2), MakeSingleAssignment(2, 1, 2), MakeSingleAssignment(1, 2, 2),
54+
MakeSingleAssignment(2, 2, 2), MakeSingleAssignment(1, 3, 2), MakeSingleAssignment(2, 3, 2),
55+
],
56+
_ => []
57+
};
58+
}
59+
60+
[Test]
61+
public void TestIterateAssignments()
62+
{
63+
var safety = new SafetyTest(null, "test")
64+
{
65+
ParamExprMap = new Dictionary<string, List<IPExpr>>
66+
{
67+
{ "a", [new IntLiteralExpr(1), new IntLiteralExpr(2)] },
68+
{ "b", [new IntLiteralExpr(1), new IntLiteralExpr(2), new IntLiteralExpr(3)] },
69+
{ "c", [new IntLiteralExpr(1), new IntLiteralExpr(2)] },
70+
},
71+
AssumeExpr = new BoolLiteralExpr(true),
72+
Twise = 0
73+
};
74+
var globalParams = safety.ParamExprMap.Select(kv => new Variable(kv.Key)).ToList();
75+
for (var i = 1; i <= 3; i++)
76+
{
77+
safety.Twise = i;
78+
var nativeRecord = new HashSet<Dictionary<Variable, IPExpr>>();
79+
ParamAssignment.IterateAssignments(safety, globalParams, dict => nativeRecord.Add(dict));
80+
var record = nativeRecord
81+
.Select(dict => dict.ToDictionary(kv => kv.Key.Name, kv => ((IntLiteralExpr)kv.Value).Value))
82+
.ToHashSet();
83+
Assert.AreEqual(record, MakeExpectAssignments(i));
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)