diff --git a/src/vsc/model/constraint_dist_scope_model.py b/src/vsc/model/constraint_dist_scope_model.py index e3bff0f..22e384c 100644 --- a/src/vsc/model/constraint_dist_scope_model.py +++ b/src/vsc/model/constraint_dist_scope_model.py @@ -3,9 +3,11 @@ @author: mballance ''' +from typing import List, Tuple from vsc.model.constraint_inline_scope_model import ConstraintInlineScopeModel from vsc.model.constraint_dist_model import ConstraintDistModel from vsc.model.constraint_soft_model import ConstraintSoftModel +from vsc.model.rand_state import RandState class ConstraintDistScopeModel(ConstraintInlineScopeModel): """Holds implementation data about dist constraint""" @@ -16,10 +18,36 @@ def __init__(self, dist_c, constraints=None): self.dist_c : ConstraintDistModel = dist_c self.dist_soft_c : ConstraintSoftModel = None - - # Indicates the current-target range. This is - # updated during the weight-selection process + + # List of (weight, index) tuples + self.weight_list : List[Tuple[int, int]] = [] + self.total_weight = 0 + + # Indicates the current-target range. This is used to + # by solvegroup_swizzler_range. self.target_range = 0 + + def next_target_range(self, randstate : RandState) -> int: + """Select the next target range from the weight list""" + + seed_v = randstate.rng.randint(1, self.total_weight) + + # Find the first range + i = 0 + while i < len(self.weight_list): + seed_v -= self.weight_list[i][0] + + if seed_v <= 0: + break + + i += 1 + + if i >= len(self.weight_list): + i = len(self.weight_list)-1 + + self.target_range = self.weight_list[i][1] + + return self.target_range def set_dist_soft_c(self, c : ConstraintSoftModel): self.addConstraint(c) diff --git a/src/vsc/model/rand_set.py b/src/vsc/model/rand_set.py index d688795..7323d85 100644 --- a/src/vsc/model/rand_set.py +++ b/src/vsc/model/rand_set.py @@ -21,11 +21,13 @@ # @author: ballance from builtins import set -from typing import Set, List +from typing import Set, List, Dict +from vsc.model.constraint_dist_scope_model import ConstraintDistScopeModel from vsc.model.constraint_model import ConstraintModel from vsc.model.field_model import FieldModel from vsc.model.constraint_soft_model import ConstraintSoftModel +from vsc.model.field_scalar_model import FieldScalarModel from vsc.visitors.model_pretty_printer import ModelPrettyPrinter @@ -43,7 +45,7 @@ def __init__(self, order=-1): self.soft_constraint_s : Set[ConstraintModel] = set() self.soft_constraint_l : List[ConstraintModel] = [] self.soft_priority = 0 - self.dist_field_m = {} + self.dist_field_m : Dict[FieldScalarModel, List[ConstraintDistScopeModel]] = {} # List of fields in each ordered set # Only non-none if order constraints impact this randset diff --git a/src/vsc/model/rand_state.py b/src/vsc/model/rand_state.py index 0c07500..f563a54 100644 --- a/src/vsc/model/rand_state.py +++ b/src/vsc/model/rand_state.py @@ -29,7 +29,7 @@ def rand_s(self): return val - def randint(self, low, high): + def randint(self, low, high) -> int: low = int(low) high = int(high) diff --git a/src/vsc/model/solvegroup_swizzler_partsel.py b/src/vsc/model/solvegroup_swizzler_partsel.py index b05d7c5..2ba0ba4 100644 --- a/src/vsc/model/solvegroup_swizzler_partsel.py +++ b/src/vsc/model/solvegroup_swizzler_partsel.py @@ -10,7 +10,10 @@ from vsc.model.expr_literal_model import ExprLiteralModel from vsc.model.expr_model import ExprModel from vsc.model.expr_partselect_model import ExprPartselectModel +from vsc.model.field_model import FieldModel from vsc.model.field_scalar_model import FieldScalarModel +from vsc.model.rand_set import RandSet +from vsc.model.rand_state import RandState from vsc.model.variable_bound_model import VariableBoundModel @@ -18,13 +21,13 @@ class SolveGroupSwizzlerPartsel(object): def __init__(self, randstate, solve_info, debug=0): self.debug = debug - self.randstate = randstate + self.randstate : RandState = randstate self.solve_info = solve_info def swizzle(self, - btor, - rs, - bound_m): + btor, + rs : RandSet, + bound_m : VariableBoundModel): if self.debug > 0: print("--> swizzle_randvars") @@ -51,7 +54,7 @@ def swizzle(self, if self.debug > 0: print("<-- swizzle_randvars") - def swizzle_field_l(self, field_l, rs, bound_m, btor): + def swizzle_field_l(self, field_l, rs : RandSet, bound_m, btor): e = None if len(field_l) > 0: # Make a copy of the field list so we don't @@ -96,7 +99,10 @@ def swizzle_field_l(self, field_l, rs, bound_m, btor): else: return False - def swizzle_field(self, f, rs, bound_m) -> ExprModel: + def swizzle_field(self, + f : FieldScalarModel, + rs : RandSet, + bound_m : VariableBoundModel)->ExprModel: ret = None if self.debug > 0: @@ -106,14 +112,15 @@ def swizzle_field(self, f, rs, bound_m) -> ExprModel: if self.debug > 0: print("Note: field %s is in dist map" % f.name) for d in rs.dist_field_m[f]: - print(" Target interval %d" % d.target_range) + print(" Weight list %s" % d.weight_list) if len(rs.dist_field_m[f]) > 1: target_d = self.randstate.randint(0, len(rs.dist_field_m[f])-1) dist_scope_c = rs.dist_field_m[f][target_d] else: dist_scope_c = rs.dist_field_m[f][0] - target_w = dist_scope_c.dist_c.weights[dist_scope_c.target_range] + target_range = dist_scope_c.next_target_range(self.randstate) + target_w = dist_scope_c.dist_c.weights[target_range] if target_w.rng_rhs is not None: # Dual-bound range val_l = target_w.rng_lhs.val() @@ -129,6 +136,8 @@ def swizzle_field(self, f, rs, bound_m) -> ExprModel: else: # Single value val = target_w.rng_lhs.val() + if self.debug > 0: + print("Select dist-weight value %d" % (int(val))) ret = [ExprBinModel( ExprFieldRefModel(f), BinExprType.Eq, diff --git a/src/vsc/model/solvegroup_swizzler_range.py b/src/vsc/model/solvegroup_swizzler_range.py index b675f8a..9df3166 100644 --- a/src/vsc/model/solvegroup_swizzler_range.py +++ b/src/vsc/model/solvegroup_swizzler_range.py @@ -11,6 +11,7 @@ from vsc.model.expr_literal_model import ExprLiteralModel from vsc.model.expr_model import ExprModel from vsc.model.field_scalar_model import FieldScalarModel +from vsc.model.rand_set import RandSet from vsc.model.variable_bound_model import VariableBoundModel @@ -103,7 +104,10 @@ def swizzle_field_l(self, field_l, rs, bound_m, btor): else: return False - def swizzle_field(self, f, rs, bound_m) -> ExprModel: + def swizzle_field(self, + f : FieldScalarModel, + rs : RandSet, + bound_m : VariableBoundModel)->ExprModel: ret = None if self.debug > 0: diff --git a/src/vsc/visitors/dist_constraint_builder.py b/src/vsc/visitors/dist_constraint_builder.py index 0d47ed6..a215503 100644 --- a/src/vsc/visitors/dist_constraint_builder.py +++ b/src/vsc/visitors/dist_constraint_builder.py @@ -96,56 +96,21 @@ def visit_constraint_dist(self, c): # Form a list of non-zero weighted tuples of weight/range # Sort in ascending order - weight_l = [] - total_w = 0 + weight_list = [] + total_weight = 0 for i,w in enumerate(c.weights): weight = int(w.weight.val()) - total_w += weight + total_weight += weight if weight > 0: - weight_l.append((weight, i)) - weight_l.sort(key=lambda w:w[0]) - - seed_v = self.rng.randint(1, total_w) - - # Find the first range - i = 0 - while i < len(weight_l): - seed_v -= weight_l[i][0] - - if seed_v <= 0: - break - - i += 1 + weight_list.append((weight, i)) + weight_list.sort(key=lambda w:w[0]) - if i >= len(weight_l): - i = len(weight_l)-1 + scope.weight_list = weight_list + scope.total_weight = total_weight + + # Call next_target_range for solvegroup_swizzler_range to use + _ = scope.next_target_range(self.rng) - scope.target_range = weight_l[i][1] - target_w = c.weights[weight_l[i][1]] - dist_soft_c = None - if target_w.rng_rhs is not None: - dist_soft_c = ConstraintSoftModel( - ExprBinModel( - ExprBinModel( - c.lhs, - BinExprType.Ge, - target_w.rng_lhs), - BinExprType.And, - ExprBinModel( - c.lhs, - BinExprType.Le, - target_w.rng_rhs))) - else: - dist_soft_c = ConstraintSoftModel( - ExprBinModel( - c.lhs, - BinExprType.Eq, - target_w.rng_lhs)) - # Give dist constraints a high priority to allow - # them to override all user-defined soft constraints - dist_soft_c.priority = 1000000 - scope.set_dist_soft_c(dist_soft_c) - self.override_constraint(scope) \ No newline at end of file diff --git a/ve/unit/test_constraint_soft.py b/ve/unit/test_constraint_soft.py index c2d875f..fc0d82b 100644 --- a/ve/unit/test_constraint_soft.py +++ b/ve/unit/test_constraint_soft.py @@ -70,7 +70,7 @@ def dist_a(self): it.a == 1 #B def test_soft_dist_priority(self): - """Ensures that dist constraints take priority over soft constraints""" + """Ensures that soft constraints take priority over dist constraints""" @vsc.randobj class my_item(object): @@ -90,7 +90,7 @@ def dist_a(self): vsc.weight(1, 10), vsc.weight(2, 10), vsc.weight(4, 10), - vsc.weight(8, 10)]) + vsc.weight(8, 10)]) hist = [0]*9 item = my_item() @@ -98,10 +98,10 @@ def dist_a(self): item.randomize() hist[item.a] += 1 - self.assertGreater(hist[0], 0) - self.assertGreater(hist[1], 0) - self.assertGreater(hist[2], 0) - self.assertGreater(hist[4], 0) + self.assertEqual(hist[0], 0) + self.assertEqual(hist[1], 0) + self.assertEqual(hist[2], 0) + self.assertEqual(hist[4], 0) self.assertGreater(hist[8], 0) def test_compound_array(self):