Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove high-priority, soft weight constraint from distributions #194

Merged
merged 1 commit into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 31 additions & 3 deletions src/vsc/model/constraint_dist_scope_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand All @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions src/vsc/model/rand_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/vsc/model/rand_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 17 additions & 8 deletions src/vsc/model/solvegroup_swizzler_partsel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,24 @@
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


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")

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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()
Expand All @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion src/vsc/model/solvegroup_swizzler_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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:
Expand Down
55 changes: 10 additions & 45 deletions src/vsc/visitors/dist_constraint_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


12 changes: 6 additions & 6 deletions ve/unit/test_constraint_soft.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -90,18 +90,18 @@ 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()
for i in range(100):
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):
Expand Down