Conflicting soft constraint behavior
I have a test file where the behavior seems to be wrong. I see that in all generations, num_of_red_shoes is high, while I expect a more even distribution. What is the reason for this behavior and how can it be corrected?
<'
struct closet {
kind:[SMALL,BIG];
num_of_shoes:uint;
num_of_red_shoes:uint;
num_of_black_shoes:uint;
num_of_yellow_shoes:uint;
keep soft num_of_red_shoes < 10;
keep soft num_of_black_shoes < 10;
keep soft num_of_yellow_shoes < 10;
keep num_of_yellow_shoes + num_of_black_shoes + num_of_red_shoes == num_of_shoes;
when BIG closet {
keep num_of_shoes in [50..100];
};
};
extend sys {
closets[100]:list of BIG closet;
};
'>
Generation results:
item type kind num_of_sh* num_of_re* num_of_bl* num_of_ye*
---------------------------------------------------------------------------
0. BIG closet BIG 78 73 1 4
1. BIG closet BIG 67 50 8 9
2. BIG closet BIG 73 68 0 5
3. BIG closet BIG 73 66 3 4
4. BIG closet BIG 51 50 0 1
5. BIG closet BIG 78 76 1 1
6. BIG closet BIG 55 43 7 5
7. BIG closet BIG 88 87 1 0
8. BIG closet BIG 99 84 6 9
9. BIG closet BIG 92 92 0 0
10. BIG closet BIG 63 55 3 5
11. BIG closet BIG 59 50 9 0
12. BIG closet BIG 51 44 2 5
13. BIG closet BIG 82 76 1 5
14. BIG closet BIG 81 74 2 5
15. BIG closet BIG 97 93 2 2
16. BIG closet BIG 54 41 8 5
17. BIG closet BIG 55 44 5 6
18. BIG closet BIG 70 55 9 6
19. BIG closet BIG 63 57 1 5
source to share
When conflicting soft constraints exist, Specman does not randomize the soft programs that apply, but rather prioritizes the constraints that were most recently written. Since the soft red shoe was the first in the test, it was always redefined.
If the software is known to be mutually exclusive (which is not the case here), you can use a simple flag to randomly choose which software to hold. for example the code will look like this:
flag:uint[0..2];
keep soft read_only(flag==0) => num_of_red_shoes < 10;
keep soft read_only(flag==1) => num_of_black_shoes < 10;
keep soft read_only(flag==2) => num_of_yellow_shoes < 10;
However, since it is not known beforehand how many soft will be held (and it is possible, and two or all three will be satisfied), a more complex decision must be made. Here's the code that does this randomization:
struct closet {
kind:[SMALL,BIG];
num_of_shoes:uint;
num_of_red_shoes:uint;
num_of_black_shoes:uint;
num_of_yellow_shoes:uint;
//replaces the original soft constraints (if a flag is true the correlating
//right-side implication will be enforced
soft_flags[3]:list of bool;
keep for each in soft_flags {
soft it == TRUE;
};
//this list is used to shuffle the flags so their enforcement will be done
//with even distribution
soft_indices:list of uint;
keep soft_indices.is_a_permutation({0;1;2});
keep soft_flags[soft_indices[0]] => num_of_red_shoes < 10;
keep soft_flags[soft_indices[1]] => num_of_black_shoes < 10;
keep soft_flags[soft_indices[2]] => num_of_yellow_shoes < 10;
keep num_of_yellow_shoes + num_of_black_shoes + num_of_red_shoes == num_of_shoes;
};
source to share
I am not with Cadence, so I cannot give you a definite answer. I think the solver will try to break as few constraints as possible and it just picks the first one if it finds one (in your case, the one that fits red shoes). Try changing the order and see if that changes (if the black constraint comes first, I think you'll always get more black boots).
As a solution, you can simply remove soft constraints when you have a large closet:
when BIG closet {
keep num_of_red_shoes.reset_soft();
keep num_of_black_shoes.reset_soft();
keep num_of_yellow_shoes.reset_soft();
keep num_of_shoes in [50..100];
};
If you want to randomly choose which one to disable (sometimes more than 10 red boots, sometimes more than 10 black boots, etc.), then you need an auxiliary field:
when BIG closet {
more_shoes : [ RED, BLACK, YELLOW ];
keep more_shoes == RED => num_of_red_shoes.reset_soft();
keep more_shoes == BLACK => num_of_black_shoes.reset_soft();
keep more_shoes == YELLOW => num_of_yellow_shoes.reset_soft();
keep num_of_shoes in [50..100];
};
It depends on what you mean by "more even distribution".
source to share
It is impossible to satisfy all your hard and soft limits for a BIG cabinet. So Speckman tries to find a solution by ignoring some of your soft constraints. IntelliGen's constraint solver does not ignore all soft constraints, but tries to find a solution while still using a subset. This is explained in the "Specman Generator User Guide" (sn_igenuser.pdf):
[S] oft restrictions that are loaded later are considered higher priority than previously loaded soft restrictions.
In this case, it means that Specman is dropping the soft constraint on red shoes, and since it can find a solution that still obeys other soft constraints, it does not drop them.
If you combine all your soft constraints into one, then you are likely to get the result you were hoping for:
keep soft ((num_of_red_shoes < 10) and (num_of_black_shoes < 10) and
(num_of_yellow_shoes < 10));
There are advantages to prioritizing later constraints: this means that with AOP you can add new soft constraints and they will be given the highest priority.
source to share
For more distributed values, I would suggest the following. I'm sure you can follow the intended logic too.
var1, var2 : uint;
keep var1 in [0..30];
keep var2 in [0..30];
when BIG closet {
keep num_of_shoes in [50..100];
keep num_of_yellow_shoes == (num_of_shoes/3) - 15 + var1;
keep num_of_black_shoes == (num_of_shoes - num_of_yellow_shoes)/2 - 15 + var2;
keep num_of_red_shoes == num_of_shoes - (num_of_yellow_shoes - num_of_black_shoes);
keep gen (var1, var2) before (num_of_shoes);
keep gen (num_of_shoes) before (num_of_yellow_shoes, num_of_black_shoes, num_of_red_shoes);
keep gen (num_of_yellow_shoes) before (num_of_black_shoes, num_of_red_shoes);
keep gen (num_of_black_shoes) before (num_of_red_shoes);
};
source to share