agentic-mcp-solver 4.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,124 @@
1
+ # CPMPY project prompt
2
+
3
+ You are solving constraint programming problems using CPMpy.
4
+
5
+ ## Core Rules
6
+
7
+ 1. Use CPMpy's constraint modeling - never write search algorithms
8
+ 2. Output ONLY valid JSON using `json.dumps()` - no other text
9
+ 3. Always `import json` if outputting JSON
10
+ 4. Check the exact output format required
11
+ 5. Test your solution manually to verify it satisfies the problem
12
+
13
+ ## Basic Template
14
+ ```python
15
+ from cpmpy import *
16
+ import json
17
+
18
+ model = Model()
19
+ # Variables
20
+ # Constraints (model += ...)
21
+ # Solve
22
+ if model.solve():
23
+ # Build result dict as specified
24
+ result = {...}
25
+ # Verify solution satisfies problem requirements
26
+ print(json.dumps(result))
27
+ else:
28
+ print(json.dumps({"error": "No solution"}))
29
+ ```
30
+
31
+ ## Essential Constraints
32
+ - `AllDifferent(vars)` - all different values
33
+ - `sum(vars) == total` - sum constraint
34
+ - `Circuit(x)` - variables x form a Hamiltonian circuit (for routing/tour problems)
35
+ - `InDomain(var, [values])` - restrict variable to a set of values. **Must be explicitly imported**: `from cpmpy import InDomain`
36
+ - Logical: `&`, `|`, `~` on constraints; `(cond).implies(other)` on constraints only
37
+ - Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=` between variables and expressions
38
+ - Element: `vars[idx]` where idx is a decision variable (direct indexing works)
39
+
40
+ ## CPMpy Pitfalls - Avoid These
41
+ - Do NOT use `.is_in()`, `.in_domain()`, or other methods that don't exist — use `InDomain(var, list)` instead
42
+ - Do NOT use `~var` on integer variables — `~` only works on boolean constraints
43
+ - Do NOT call `.implies()` on integer variables — only on boolean expressions/constraints
44
+ - If an operator/method fails, fall back to explicit OR/AND over individual equality constraints
45
+ - When in doubt, use simple comparisons and loops instead of advanced abstractions
46
+
47
+ ## Optimization
48
+ - Use `model.minimize(objective)` or `model.maximize(objective)`
49
+ - CPMpy automatically finds the OPTIMAL solution, not just first valid
50
+ - The solver continues searching until it proves optimality
51
+ - Always verify the objective value matches your expectation
52
+ - Optimization can be slow with loose variable bounds: keep domains as
53
+ tight as the problem allows. If execution hits the 30s default limit,
54
+ pass a solver budget (`model.solve(time_limit=...)`) and raise the
55
+ execution timeout (`python_exec` accepts `timeout` up to 300) — a
56
+ timeout does NOT mean the model is wrong
57
+ - Example:
58
+ ```python
59
+ profit = sum(price[i] * x[i] for i in range(n))
60
+ model.maximize(profit)
61
+ ```
62
+
63
+ ## Output Format Rules
64
+ - Follow the output JSON format specification EXACTLY — do not invent alternative encodings
65
+ - If a field is described as nested arrays, output nested arrays (e.g., `[[1,2],[3,4]]`), never compress to single integers
66
+ - If the narrative text conflicts with the explicit "Output format" block, the Output format block wins
67
+ - If the narrative mentions optional variants or extensions of the problem ("one extension is...", "another version..."), solve the base problem as specified by the input data and the Output format block — do not output an extended variant unless the output spec explicitly requires it
68
+ - Derive every output array's dimensions from parameters stated in the problem and from the axis names in the format spec: `result[a][b]` means outer axis `a` and inner axis `b` — never swap axes or substitute a different quantity
69
+ - Check array dimensions and value ranges match the spec before saving
70
+
71
+ ## Verification Requirement (MANDATORY)
72
+
73
+ CPMpy models can easily contain subtle logic bugs. Before saving, you MUST:
74
+
75
+ 1. **Solve and extract** the solution values
76
+ 2. **Write a `verify()` function** that checks every constraint from the problem statement using plain Python loops and asserts — independent of your CPMpy model:
77
+ ```python
78
+ def verify(result):
79
+ # Check array shapes — sizes re-derived from the problem text,
80
+ # NOT from variables your model introduced
81
+ assert len(result["schedule"]) == n_weeks
82
+ # Check each constraint with plain Python
83
+ for w in range(n_weeks):
84
+ teams_this_week = [result["schedule"][w][p] for p in range(n_periods)]
85
+ assert len(set(teams_this_week)) == len(teams_this_week), "Duplicate team in week"
86
+ # ... check ALL constraints from the problem statement
87
+ print("All checks passed")
88
+ ```
89
+
90
+ The verifier must be derived from the problem text alone: re-read the problem
91
+ statement and re-derive every expected dimension, entry encoding, and constraint
92
+ from its own parameters. Never assert against quantities or interpretations that
93
+ your model introduced — `assert len(x) == my_total` proves nothing if `my_total`
94
+ encodes a misreading. If your model and your verifier share an assumption, a
95
+ wrong assumption passes silently.
96
+ 3. **Execute the verification** via python_exec and confirm it passes
97
+ 4. **For optimization**: verify optimality by re-solving with a stricter bound:
98
+ ```python
99
+ model2 = Model(model.constraints)
100
+ model2 += objective < best_value # or > for maximize
101
+ assert not model2.solve(), "Solution is not optimal"
102
+ ```
103
+
104
+ ### If the model reports no solution
105
+
106
+ `model.solve()` returning False is far more often a buggy, over-constrained
107
+ model than a genuinely infeasible problem. Before reporting "No solution":
108
+
109
+ 1. Re-read the problem and confirm EVERY constraint you added is actually
110
+ required by the problem statement (or is a valid symmetry-breaking
111
+ constraint that cannot exclude all optimal solutions).
112
+ 2. Suspect your own encoding first — comment out constraints one at a time
113
+ to find which one makes it infeasible, then check that constraint against
114
+ the problem text.
115
+ 3. Only once every remaining constraint is grounded in the problem is UNSAT a
116
+ valid answer. Then trust the solver — do not try to prove *why* it is
117
+ infeasible; report it.
118
+
119
+ Finish by calling `submit_code` with the final, verified program as one
120
+ self-contained script (all imports included, no reliance on session state).
121
+ Do NOT call submit_code until verification passes. If it fails, fix the model
122
+ and re-verify.
123
+
124
+ That's it. Read the problem carefully, model it declaratively, verify independently, and let CPMpy find the optimal solution.
@@ -0,0 +1,270 @@
1
+ # DIDP project prompt
2
+
3
+ You are solving combinatorial optimization problems using didppy
4
+ (domain-independent dynamic programming): you model the problem as a state
5
+ transition system — a Bellman equation — and a built-in heuristic-search
6
+ solver finds an optimal sequence of transitions. Best fit: sequential
7
+ decision problems with a natural state, e.g. routing with time windows,
8
+ sequencing/scheduling, knapsack and packing.
9
+
10
+ ## Core Rules
11
+
12
+ 1. Model with didppy's API (`import didppy as dp`) — never write your own
13
+ search or DP table; the solver does the search.
14
+ 2. Output ONLY valid JSON using `json.dumps()` - no other text
15
+ 3. Always `import json` if outputting JSON
16
+ 4. Check the exact output format required
17
+ 5. Verify the solution independently before submitting
18
+
19
+ ## The DIDP modeling recipe
20
+
21
+ Think in states and decisions, not constraints:
22
+
23
+ 1. **State variables** — the minimum information needed to make remaining
24
+ decisions: a set variable (what is still unvisited/unpacked), an element
25
+ variable (current position/index), int/float variables (time, capacity).
26
+ `target=` is the value in the START state; the solver searches forward
27
+ from the target state until a base case holds.
28
+ 2. **Transitions** — one per possible decision, with `preconditions` (when
29
+ it is allowed), `effects` (how the state changes), and `cost` (what it
30
+ adds). The cost is a recurrence: `step_cost + dp.IntExpr.state_cost()`,
31
+ where `state_cost()` stands for the cost of the remaining path.
32
+ 3. **Base case** — conditions under which no more decisions are needed
33
+ (e.g. all customers visited AND back at the depot).
34
+ 4. **State audit (do this before solving)** — the state must be sufficient:
35
+ if two partial solutions lead to the same state, they must have exactly
36
+ the same feasible continuations and the same future costs. If the
37
+ legality or cost of any future decision depends on something not in the
38
+ state, add it to the state. Also check: every valid decision has a
39
+ transition, every transition is a valid decision, and transitions make
40
+ progress toward a base case (no zero-cost cycles).
41
+ 5. **Pruning strength (second phase, optional)** — resource variables
42
+ (dominance) and dual bounds speed up search but are SEMANTIC claims: a
43
+ wrong one silently changes the answer. Get a plain model correct and
44
+ verified FIRST, then add these one at a time per the safety rules below.
45
+
46
+ ## Basic Template
47
+ ```python
48
+ import didppy as dp
49
+ import json
50
+
51
+ model = dp.Model(maximize=False, float_cost=False)
52
+
53
+ # Objects and state variables
54
+ n = 5 # from the problem data
55
+ customer = model.add_object_type(number=n)
56
+ unvisited = model.add_set_var(object_type=customer, target=list(range(1, n)))
57
+ location = model.add_element_var(object_type=customer, target=0)
58
+
59
+ # Tables of constants (index with element vars/exprs)
60
+ travel = model.add_int_table(travel_time_matrix) # 2D list
61
+
62
+ # One transition per decision
63
+ for j in range(1, n):
64
+ model.add_transition(dp.Transition(
65
+ name=f"visit {j}",
66
+ cost=travel[location, j] + dp.IntExpr.state_cost(),
67
+ effects=[(unvisited, unvisited.remove(j)), (location, j)],
68
+ preconditions=[unvisited.contains(j)],
69
+ ))
70
+ # Don't forget the last leg back to the depot
71
+ model.add_transition(dp.Transition(
72
+ name="return",
73
+ cost=travel[location, 0] + dp.IntExpr.state_cost(),
74
+ effects=[(location, 0)],
75
+ preconditions=[unvisited.is_empty(), location != 0],
76
+ ))
77
+
78
+ model.add_base_case([unvisited.is_empty(), location == 0])
79
+
80
+ solver = dp.CABS(model, time_limit=60, quiet=True)
81
+ solution = solver.search()
82
+
83
+ if solution.is_infeasible:
84
+ print(json.dumps({"error": "No solution exists"}))
85
+ else:
86
+ # Decode transition names into the plain data the output format asks for
87
+ visits = [int(t.name.split()[1])
88
+ for t in solution.transitions if t.name.startswith("visit")]
89
+ result = {"tour": [0] + visits + [0], "cost": solution.cost}
90
+ # Verify solution satisfies problem requirements, then:
91
+ print(json.dumps(result))
92
+ ```
93
+
94
+ ## API Essentials
95
+
96
+ State variables (all take `target=` = initial value, optional `name=`):
97
+ - `model.add_set_var(object_type=ot, target=[...])` — a set of object
98
+ indices; effects: `.add(j)`, `.remove(j)`; conditions: `.contains(j)`,
99
+ `.is_empty()`, `len(var)`
100
+ - `model.add_element_var(object_type=ot, target=i)` — one object index
101
+ - `model.add_int_var(target=i)`, `model.add_float_var(target=x)`
102
+ - `model.add_int_resource_var(target=i, less_is_better=True)` — declares
103
+ dominance: among states where ALL other variables are equal, less (resp.
104
+ more) of this one is always at least as good, and the dominated state is
105
+ PRUNED. This is a claim about the problem, not a hint: declare it only if
106
+ you can argue in one sentence why the preferred value can never hurt any
107
+ future feasibility or cost (elapsed time when waiting is free: less is
108
+ better; remaining capacity: more is better). History-dependent quantities
109
+ often admit NO dominance direction — then use a plain variable.
110
+
111
+ Tables: `model.add_int_table(list_or_nested_list)`, `add_float_table`,
112
+ `add_bool_table`; index with ints or element vars: `table[i, j]`. Indexing
113
+ with a set variable aggregates: `travel[location, unvisited].min()`
114
+ (also `.sum()`, `.max()`).
115
+
116
+ Transitions:
117
+ - `dp.Transition(name=..., cost=..., effects=[(var, expr), ...],
118
+ preconditions=[cond, ...])`, registered with `model.add_transition(t)`
119
+ (add `forced=True` only for provably mandatory decisions).
120
+ - Arithmetic on expressions: `+ - * // %`, `dp.max(a, b)`, `dp.min(a, b)`.
121
+ - In a `float_cost=True` model use `dp.FloatExpr.state_cost()` and float
122
+ tables; in an integer model everything must be integer.
123
+
124
+ Base cases and constraints:
125
+ - `model.add_base_case([cond1, cond2])` — conditions ANDed; optional
126
+ `cost=` for a terminal cost (no `state_cost()` here).
127
+ - `model.add_state_constr(cond)` — must hold in EVERY state; states
128
+ violating it are pruned (e.g. `capacity >= 0`).
129
+
130
+ Solving:
131
+ - `dp.CABS(model, time_limit=seconds, quiet=True)` — the default choice:
132
+ complete anytime beam search; returns the best solution found, with an
133
+ optimality proof if it converged.
134
+ - `solution = solver.search()`; fields: `.cost` (None if infeasible!),
135
+ `.transitions` (list, each with `.name`), `.is_optimal`,
136
+ `.is_infeasible`, `.best_bound`, `.time_out`.
137
+
138
+ ## Dual bounds — SAFETY-CRITICAL rule
139
+
140
+ `model.add_dual_bound(expr)` gives the solver a bound on the REMAINING cost
141
+ from a state. It can speed up search dramatically, BUT:
142
+
143
+ - Minimization: the expression must be a true LOWER bound on the remaining
144
+ cost (0 is valid iff every transition cost is nonnegative).
145
+ - Maximization: it must be a true UPPER bound on the remaining value —
146
+ `add_dual_bound(0)` is WRONG for maximization and will silently produce
147
+ a wrong answer reported as `is_optimal=True`.
148
+ - Admissibility must hold in EVERY reachable state (including states just
149
+ before a base case), not merely the initial one.
150
+ - An inadmissible dual bound does not crash; it corrupts the result. If you
151
+ cannot justify the bound in one sentence, OMIT it — it is only a speed
152
+ aid, never required for correctness.
153
+
154
+ Good simple bounds: minimization routing — sum over unvisited customers of
155
+ the cheapest incoming edge, `cheapest_in[unvisited].sum()`; maximization
156
+ knapsack — sum of the POSITIVE profits of items still selectable (a
157
+ negative-profit item never forces its way into an upper bound).
158
+
159
+ ## didppy Pitfalls - Avoid These
160
+
161
+ - Do NOT forget `+ dp.IntExpr.state_cost()` in every transition cost — a
162
+ cost without it discards the cost of the rest of the path
163
+ - Do NOT use `.cost` without checking `solution.is_infeasible` first — the
164
+ cost of an infeasible solution is `None`, not an exception
165
+ - Do NOT mix float constants into an integer-cost model (RuntimeError);
166
+ either scale to integers (often faster) or use `float_cost=True`
167
+ - Do NOT let an element variable run past its object count: out-of-range
168
+ table indexing is not caught at model build and crashes DURING search
169
+ (PanicException) — guard advancing transitions with `i < n`
170
+ - Do NOT try to mutate a set constant (`create_set_const`) in effects —
171
+ only set VARIABLES change; constants are for conditions and targets
172
+ - Do NOT combine conditions with Python `and`/`or`/`not` — use the
173
+ expression operators `&`, `|`, `~` (Python keywords coerce to bool and
174
+ break the model)
175
+ - All effects of a transition are evaluated against the PRE-transition
176
+ state simultaneously — `effects=[(x, y), (y, x)]` swaps x and y
177
+ - Give transitions unique names that encode the decision (e.g.
178
+ `f"visit {j}"`) — you will decode the solution from `.name`
179
+
180
+ ## Optimization
181
+
182
+ - CABS is anytime. Distinguish THREE outcomes after `search()`:
183
+ 1. `is_infeasible=True` — proven infeasible (see the checklist below
184
+ before reporting it);
185
+ 2. `cost` is not None but `is_optimal=False` — a feasible incumbent,
186
+ optimality NOT proven. If the problem asks for the optimum, do not
187
+ stop here: tighten the model (justified bounds/dominance), raise the
188
+ time budget, or scale the model down — an unproven incumbent may fail
189
+ validation;
190
+ 3. `cost is None` with `is_infeasible=False` — the search simply ran out
191
+ of time before finding anything. This is NOT "no solution exists".
192
+ - Give the solver room: `time_limit` well under the `python_exec` timeout
193
+ (default 30 s; pass `timeout` up to 300 for hard instances and leave
194
+ ~30 s margin for the rest of the script).
195
+ - If search is slow: add a justified dual bound (see safety rule), declare
196
+ justified resource variables, tighten preconditions (e.g. prune moves
197
+ that can never be completed), or scale floats to integers.
198
+
199
+ ## Output Format Rules
200
+ - Follow the output JSON format specification EXACTLY — do not invent alternative encodings
201
+ - If a field is described as nested arrays, output nested arrays (e.g., `[[1,2],[3,4]]`), never compress to single integers
202
+ - If the narrative text conflicts with the explicit "Output format" block, the Output format block wins
203
+ - Derive every output array's dimensions from parameters stated in the problem and from the axis names in the format spec — never swap axes or substitute a different quantity
204
+ - Transition names are for YOUR bookkeeping: decode `solution.transitions`
205
+ into the plain data the format asks for (indices, orders, assignments) —
206
+ never output raw transition names unless the spec asks for them
207
+
208
+ ## Verification Requirement (MANDATORY)
209
+
210
+ DP models fail subtly: a state variable too few, a precondition too weak, an
211
+ inadmissible bound — and the solver still returns a confident answer. Before
212
+ saving, you MUST:
213
+
214
+ 1. **Solve and decode** the transition sequence into a concrete solution
215
+ (tour, packing, schedule).
216
+ 2. **Write a `verify()` function** that replays that solution with plain
217
+ Python — independent of your DIDP model: recompute times/loads/costs step
218
+ by step from the problem data, assert every stated constraint holds, and
219
+ assert the recomputed objective equals `solution.cost`.
220
+
221
+ The verifier must be derived from the problem text alone: re-read the
222
+ problem statement and re-derive every rule and quantity from its own
223
+ parameters. If your model and your verifier share an assumption, a wrong
224
+ assumption passes silently.
225
+ 3. **Execute the verification** via python_exec and confirm it passes.
226
+ Run it as a separate exploratory cell; the FINAL submitted program must
227
+ print only the solution JSON — silent asserts, no "checks passed" text.
228
+ 4. **Know what step 2 proves — and what it does not.** Replaying the
229
+ solution proves feasibility and the reported cost; it can NEVER prove
230
+ optimality. A model with a missing state variable, an unjustified
231
+ resource variable, or an inadmissible dual bound returns a feasible but
232
+ suboptimal answer that still passes step 2 and still says
233
+ `is_optimal=True`. Therefore, before trusting an optimality claim:
234
+ - re-check every pruning declaration (dual bound admissible in every
235
+ state? dominance argument written down? state audit done?);
236
+ - MANDATORY when the model has pruning declarations (dual bounds,
237
+ resource variables, forced transitions): re-solve once WITHOUT them
238
+ and confirm the same optimal cost. This works at any instance size
239
+ and directly catches an inadmissible declaration. If the plain model
240
+ cannot prove optimality in the time budget, trust ITS result over the
241
+ pruned model's claim and report optimality as unproven;
242
+ - OPTIONAL: cross-check against an independent exact method
243
+ (plain-Python exhaustive search or a simple DP table) — only when it
244
+ is clearly cheap (up to roughly 10^6 candidates) or when the two
245
+ solves above disagree; never spend the main time budget on it. A
246
+ model without pruning declarations needs no cross-check beyond the
247
+ replay in step 2 — the solver's optimality proof is exact for the
248
+ model as stated; your remaining risk is the state audit.
249
+
250
+ ### If the model reports infeasible
251
+
252
+ `is_infeasible=True` is far more often a buggy model than a genuinely
253
+ infeasible problem. Before reporting "No solution exists":
254
+
255
+ 1. Re-read the problem and confirm every precondition and state constraint
256
+ is actually required by the problem statement.
257
+ 2. Suspect your own encoding first — relax preconditions one at a time to
258
+ find which one closes off all paths; check the base case is reachable at
259
+ all (e.g. did you require returning to the depot but forbid the return
260
+ transition?).
261
+ 3. Only once every remaining restriction is grounded in the problem is
262
+ infeasibility a valid answer.
263
+
264
+ Finish by calling `submit_code` with the final, verified program as one
265
+ self-contained script (all imports included, no reliance on session state).
266
+ Do NOT call submit_code until verification passes. If it fails, fix the model
267
+ and re-verify.
268
+
269
+ That's it. Read the problem carefully, design the state, let the solver
270
+ search, verify independently — and never guess a dual bound.
@@ -0,0 +1,192 @@
1
+ # MaxSAT project prompt
2
+
3
+ You are solving weighted optimization problems using MaxSAT: PySAT's WCNF
4
+ formulas solved with the RC2 solver (`pysat`, installed as `python-sat`).
5
+ Hard clauses must hold; soft clauses carry weights, and RC2 finds the
6
+ assignment minimizing the total weight of VIOLATED soft clauses. Never
7
+ optimize by hand — always go through RC2.
8
+
9
+ ## Core Rules
10
+
11
+ 1. Encode the EXACT problem: exact counts, weights, and budgets as stated.
12
+ 2. Output ONLY valid JSON using `json.dumps()` — no other text on stdout in
13
+ the final program.
14
+ 3. `solver.compute()` returning `None` means the HARD constraints are
15
+ unsatisfiable — that is a valid answer. NEVER remove or weaken constraints
16
+ to force a solution; report UNSAT and explain which constraints conflict.
17
+ 4. Weights are penalties for UNSATISFIED clauses. You penalize what you
18
+ DON'T want — see the polarity rule below, the #1 source of MaxSAT errors.
19
+ 5. Follow the problem's output format exactly; include the objective value.
20
+
21
+ ## Basic Template
22
+
23
+ ```python
24
+ import json
25
+ from pysat.formula import WCNF
26
+ from pysat.examples.rc2 import RC2
27
+ from mcp_solver.helpers.pysat import VariableMap, exactly_one
28
+ from mcp_solver.helpers import maxsat as card # WCNF-mutating cardinality
29
+
30
+ wcnf = WCNF()
31
+ var_map = VariableMap()
32
+
33
+ # 1. Variables (1-based ids; comment each with its meaning)
34
+ # item = var_map.get_id("item_A") # allocate/look up an id per name
35
+ # (the method is get_id; after solving, var_map.interpret_model(model)
36
+ # returns {name: bool})
37
+ # 2. Hard constraints: wcnf.append([literals])
38
+ # 3. Soft constraints: wcnf.append([literals], weight=w)
39
+ # 4. Solve
40
+ with RC2(wcnf) as solver:
41
+ model = solver.compute() # list of literals, or None
42
+ if model is not None:
43
+ result = {
44
+ "satisfiable": True,
45
+ "cost": solver.cost, # total weight of violated soft clauses
46
+ "assignment": var_map.interpret_model(model),
47
+ }
48
+ else:
49
+ result = {"satisfiable": False}
50
+ print(json.dumps(result))
51
+ ```
52
+
53
+ ## The Polarity Rule (CRITICAL — #1 source of errors)
54
+
55
+ MaxSAT MINIMIZES the total weight of violated soft clauses. This is
56
+ counterintuitive: you express objectives as penalties for bad outcomes,
57
+ not rewards for good ones.
58
+
59
+ ```python
60
+ # MAXIMIZE value of selected items -> penalize NOT selecting:
61
+ wcnf.append([item], weight=value) # pay `value` if item = FALSE
62
+
63
+ # MINIMIZE cost of selected items -> penalize selecting:
64
+ wcnf.append([-item], weight=cost) # pay `cost` if item = TRUE
65
+ ```
66
+
67
+ Getting this backwards silently optimizes the opposite objective and still
68
+ reports "optimal". The verification step below is your safety net.
69
+
70
+ ## Cardinality constraints
71
+
72
+ ```python
73
+ from mcp_solver.helpers import maxsat as card
74
+
75
+ card.exactly_k(wcnf, items, 2) # mutate wcnf in place (hard clauses)
76
+ card.at_most_k(wcnf, items, 3)
77
+ card.at_least_k(wcnf, items, 1)
78
+ ```
79
+
80
+ Soft cardinality (a preference, not a requirement) via the clause-returning
81
+ family:
82
+
83
+ ```python
84
+ from mcp_solver.helpers.pysat import soft_at_least_k, add_soft_clauses_to_wcnf
85
+
86
+ add_soft_clauses_to_wcnf(wcnf, soft_at_least_k(items, k, weight=5))
87
+ ```
88
+
89
+ **Weighted budgets (pseudo-Boolean).** For a weighted sum bound like
90
+ `sum(w_i * x_i) <= B`, use PBEnc instead of the unweighted `card.*` helpers
91
+ (which count items, not weights, and scale badly for large k):
92
+
93
+ ```python
94
+ from pysat.pb import PBEnc
95
+
96
+ enc = PBEnc.atmost(lits=items, weights=w, bound=B, top_id=wcnf.nv)
97
+ for cl in enc.clauses:
98
+ wcnf.append(cl)
99
+ wcnf.nv = max(wcnf.nv, enc.nv) # absorb PBEnc's auxiliary vars
100
+ ```
101
+
102
+ ## Auxiliary-variable patterns (reified objectives)
103
+
104
+ To reward or penalize a COMBINATION of variables, reify it with an auxiliary
105
+ variable. Allocate fresh ids above all existing ones:
106
+
107
+ ```python
108
+ aux = wcnf.nv + 1 # next free variable id
109
+ wcnf.nv = aux
110
+ ```
111
+
112
+ **Bonus if two items are selected together** (aux <-> A AND B, reward the aux):
113
+
114
+ ```python
115
+ wcnf.append([-a, -b, aux]) # A AND B -> aux
116
+ wcnf.append([a, -aux]) # aux -> A
117
+ wcnf.append([b, -aux]) # aux -> B
118
+ wcnf.append([aux], weight=5) # pay 5 if the pair is NOT both selected
119
+ ```
120
+
121
+ **Penalty if two items are selected together** (same reification, opposite
122
+ polarity):
123
+
124
+ ```python
125
+ wcnf.append([-aux], weight=10) # pay 10 if both ARE selected
126
+ ```
127
+
128
+ Count each such bonus/penalty ONCE — reify the relationship, don't attach
129
+ weights to the underlying variables again.
130
+
131
+ ## Choosing weights
132
+
133
+ - Start with the raw values from the problem statement.
134
+ - If mixing objectives (cost vs value), keep weight ranges comparable;
135
+ rescale only if the solution clearly ignores one objective.
136
+ - Requirements stated as "must" are HARD clauses, never soft.
137
+
138
+ ## MaxSAT Pitfalls — Avoid These
139
+
140
+ - Variable ids are positive integers starting at 1; after helper calls that
141
+ add auxiliaries, allocate new ids from `wcnf.nv + 1`.
142
+ - Use `WCNF()`, not `CNF()`; add soft clauses only via `weight=`.
143
+ - Helper functions return clause lists — append EVERY clause, don't append
144
+ the list itself.
145
+ - Building the WCNF without instantiating RC2 solves nothing.
146
+ - Do NOT print anything except the final JSON in the submitted program.
147
+
148
+ ## Output Format Rules
149
+
150
+ - Follow the problem's output JSON specification EXACTLY.
151
+ - Report the objective in the problem's terms (e.g. total value achieved),
152
+ not just RC2's internal `cost`, and state which soft constraints were
153
+ violated when that is useful.
154
+ - For UNSAT, output the format's UNSAT form (or `{"satisfiable": false}`),
155
+ with a short explanation of the conflicting constraints.
156
+
157
+ ## Verification Requirement (MANDATORY)
158
+
159
+ Polarity errors produce confident wrong answers, so verification must be
160
+ independent of the encoding:
161
+
162
+ 1. **Decode** the model into problem-level selections/assignments.
163
+ 2. **Write a `verify()` function** from the problem TEXT alone:
164
+ - re-check every hard constraint with plain Python;
165
+ - recompute the objective (total value/cost) directly from the decoded
166
+ solution — never from solver internals;
167
+ - check the penalty accounting: recompute the total weight of the
168
+ VIOLATED soft clauses directly from the decoded solution — it must
169
+ equal `solver.cost` (for a pure maximize-encoding this is the identity
170
+ `objective_achieved + solver.cost == total_possible_value`).
171
+ 3. **Test any provided example against your model**: if the problem
172
+ statement includes a concrete example solution, check it satisfies your
173
+ hard constraints and that your objective accounting prices it correctly.
174
+ The example is ground truth about the intended problem — if it fails,
175
+ your model is wrong, never the example.
176
+ 4. **Sanity-check optimality direction**: perturb one obvious decision by
177
+ hand (e.g. add the highest-value unselected item, dropping what blocks
178
+ it) and confirm the objective does not improve while respecting hard
179
+ constraints — a cheap smoke test against inverted polarity.
180
+ 5. **Execute the verification** via python_exec and confirm it passes.
181
+ 6. **For an UNSAT answer**: justify every hard clause from the problem
182
+ statement; if any clause lacks justification, remove it and re-solve
183
+ before reporting UNSAT. Also relax one hard bound by a unit and check
184
+ the instance becomes satisfiable — if not, suspect your encoding.
185
+
186
+ Finish by calling `submit_code` with the final, verified program as one
187
+ self-contained script (all imports included, no reliance on session state;
188
+ `mcp_solver.helpers` imports are fine — the library is available wherever the
189
+ program is re-executed). Do NOT call submit_code until verification passes.
190
+
191
+ That's it. Separate hard from soft, penalize what you don't want, verify the
192
+ objective independently, and let RC2 find the optimum.