Skip to content

Commit e209470

Browse files
committed
document finc2
1 parent 7d74ac0 commit e209470

1 file changed

Lines changed: 39 additions & 27 deletions

File tree

pycona/find_constraint/findc2.py

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010

1111
class FindC2(FindCBase):
1212
"""
13-
This is the version of the FindC function that was presented in
14-
Bessiere, Christian, et al., "Learning constraints through partial queries", AIJ 2023
15-
13+
Implementation of the FindC algorithm from Bessiere et al., "Learning constraints through partial queries" (AIJ 2023).
14+
1615
This function works also for non-normalised target networks!
1716
"""
1817

@@ -32,7 +31,8 @@ def findscope(self):
3231
"""
3332
Get the findscope function to be used.
3433
35-
:return: The findscope function.
34+
Returns:
35+
callable: The function used to determine constraint scopes
3636
"""
3737
return self._findscope
3838

@@ -41,32 +41,39 @@ def findscope(self, findscope):
4141
"""
4242
Set the findscope function to be used.
4343
44-
:param findscope: The findscope function.
44+
Args:
45+
findscope (callable): The function to be used for determining constraint scopes
4546
"""
4647
self._findscope = findscope
4748

4849
def run(self, scope):
4950
"""
50-
Run the FindC2 algorithm.
51+
Execute the FindC2 algorithm to learn constraints within a given scope.
52+
53+
Args:
54+
scope (list): Variables defining the scope in which to search for constraints
5155
52-
:param scope: The scope in which we search for a constraint.
53-
:return: The constraint found.
56+
Returns:
57+
list: The constraint(s) found in the given scope.
58+
59+
Raises:
60+
Exception: If the target constraint is not in the bias (search space).
5461
"""
5562
assert self.ca is not None
5663

5764
scope_values = [x.value() for x in scope]
5865

59-
# Initialize delta
66+
# Initialize delta with constraints from bias that match the scope
6067
delta = get_con_subset(self.ca.instance.bias, scope)
6168
kappaD = [c for c in delta if check_value(c) is False]
69+
# Join the constraints in delta with the violated constraints in kappaD
6270
delta = join_con_net(delta, kappaD)
6371

64-
# We need to take into account only the constraints in the scope we search on
72+
# Get subset of learned constraints in the current scope
6573
sub_cl = get_con_subset(self.ca.instance.cl, scope)
6674

6775
while True:
68-
69-
# Try to generate a counter example to reduce the candidates
76+
# Generate a query to distinguish between candidate constraints
7077
if self.generate_findc_query(sub_cl, delta) is False:
7178

7279
# If no example could be generated
@@ -76,7 +83,7 @@ def run(self, scope):
7683

7784
restore_scope_values(scope, scope_values)
7885

79-
# Unravel delta nested ands
86+
# Unravel nested AND constraints
8087
delta_unraveled = []
8188
for c in delta:
8289
if c.name == 'and':
@@ -87,8 +94,7 @@ def run(self, scope):
8794
else:
8895
delta_unraveled.append([c])
8996

90-
# Return random c in delta otherwise (if more than one, they are equivalent w.r.t. C_l)
91-
# Choose the constraint with the smallest number of conjunctions
97+
# Return the smallest equivalent conjunction (if more than one, they are equivalent w.r.t. C_l)
9298
delta_unraveled = sorted(delta_unraveled, key=lambda x: len(x))
9399
return delta_unraveled[0]
94100

@@ -103,10 +109,10 @@ def run(self, scope):
103109
# delta <- joint(delta,K_{delta}(e))
104110

105111
kappaD = [c for c in delta if check_value(c) is False]
106-
107112
scope2 = self.ca.run_find_scope(list(scope))
108113

109114
if len(scope2) < len(scope):
115+
# Recursively learn constraint in sub-scope
110116
c = self.run(scope2)
111117
self.ca.add_to_cl(c)
112118
sub_cl.append(c)
@@ -115,25 +121,30 @@ def run(self, scope):
115121

116122
def generate_findc_query(self, L, delta):
117123
"""
118-
Changes directly the values of the variables
124+
Generate a query that helps distinguish between candidate constraints.
119125
120-
:param L: learned network in the given scope
121-
:param delta: candidate constraints in the given scope
122-
:return: Boolean value representing a success or failure on the generation
123-
"""
126+
Args:
127+
L (list): Currently learned constraints in the scope
128+
delta (list): Candidate constraints to distinguish between
129+
130+
Returns:
131+
bool: True if a query was generated successfully, False otherwise
124132
133+
Note:
134+
The method directly modifies variable values in the constraint network
135+
"""
125136
tmp = cp.Model(L)
126137

127138
satisfied_delta = sum([c for c in delta]) # get the amount of satisfied constraints from B
128139

129140
scope = get_scope(delta[0])
141+
130142
# at least 1 violated and at least 1 satisfied
131143
# we want this to assure that each answer of the user will reduce
132144
# the set of candidates
133145
tmp += satisfied_delta < len(delta)
134146
tmp += satisfied_delta > 0
135147

136-
137148
max_conj_size = get_max_conjunction_size(delta)
138149
delta_p = get_delta_p(delta)
139150

@@ -143,23 +154,24 @@ def generate_findc_query(self, L, delta):
143154
kappa_delta_p = sum([c for c in delta_p[p]])
144155
s += kappa_delta_p < len(delta_p[p])
145156

146-
147-
if not s.solve(): # if a solution is found
157+
# Solve without objective for start
158+
if not s.solve(): # if a solution is not found
148159
continue
149160

150161
# Next solve will change the values of the variables in lY
151162
# so we need to return them to the original ones to continue if we don't find a solution next
152163
values = [x.value() for x in scope]
153164

154-
155165
p_soft_con = (kappa_delta_p > 0)
156166

157-
# run with the objective
167+
# So a solution was found, try to find a better one now
168+
# set the objective
158169
s.maximize(p_soft_con)
159170

160-
# So a solution was found, try to find a better one now
171+
# Give hint with previous solution to the solver
161172
s.solution_hint(scope, values)
162173

174+
# Solve with objective
163175
flag = s.solve(time_limit=self.time_limit, num_workers=8)
164176
if not flag:
165177
restore_scope_values(scope, values)

0 commit comments

Comments
 (0)