Skip to content

Commit 63e818b

Browse files
GiggleLiuclaude
andcommitted
fix: CLI QA improvements — creation support, aliases, help, schemas (#189)
Closes #189 - Add CLI creation for 8 previously unsupported problem types: MaximalIS, BinPacking, PaintShop, MaximumSetPacking, MinimumSetCovering, BicliqueCover, BMF, ClosestVectorProblem - Add MaxMatching alias for MaximumMatching - Fix bare `pred` appending unrelated solve help text (match first line only) - Fix `pred create <PROBLEM>` exiting 0 when showing help (now exits 2) - Fix `--timeout` default shown twice in help - Fix Makefile using wrong flag names (--edges → --graph, --bits-m → --m) - Align schemas to constructor params (BMF, PaintShop, CircuitSAT) - Add CLI creation instructions to add-model skill (Step 4.5) - Add parse_comma_list and parse_edge_pairs utilities Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 92cdd0a commit 63e818b

11 files changed

Lines changed: 351 additions & 49 deletions

File tree

.claude/skills/add-model/SKILL.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@ Update the CLI dispatch table so `pred` can load, solve, and serialize the new p
124124
- Add a lowercase alias mapping in `resolve_alias()` (e.g., `"newproblem" => "NewProblem".to_string()`)
125125
- Optionally add short aliases to `ALIASES` array (e.g., `("NP", "NewProblem")`)
126126

127+
## Step 4.5: Add CLI creation support
128+
129+
Update `problemreductions-cli/src/commands/create.rs` so `pred create <ProblemName>` works:
130+
131+
1. **Add a match arm** in the `create()` function's main `match canonical.as_str()` block. Parse CLI flags and construct the problem:
132+
- Graph-based problems with vertex weights: add to the `"MaximumIndependentSet" | ... | "MaximalIS"` arm
133+
- Problems with unique fields: add a new arm that parses the required flags and calls the constructor
134+
- See existing arms for patterns (e.g., `"BinPacking"` for simple fields, `"MaximumSetPacking"` for set-based)
135+
136+
2. **Add CLI flags** in `problemreductions-cli/src/cli.rs` (`CreateArgs` struct) if the problem needs flags not already present. Update `all_data_flags_empty()` accordingly.
137+
138+
3. **Update help text** in `CreateArgs`'s `after_help` to document the new problem's flags.
139+
140+
4. **Schema alignment**: The `ProblemSchemaEntry` fields should list **constructor parameters** (what the user provides), not internal derived fields. For example, if `m` and `n` are derived from a matrix, only list `matrix` and `k` in the schema.
141+
127142
## Step 5: Write unit tests
128143

129144
Create `src/unit_tests/models/<category>/<name>.rs`:
@@ -168,3 +183,5 @@ If running standalone (not inside `make run-plan`), invoke [review-implementatio
168183
| Forgetting `declare_variants!` | Required for variant complexity metadata used by the paper's auto-generated table |
169184
| Forgetting CLI dispatch | Must add match arms in `dispatch.rs` (`load_problem` + `serialize_any_problem`) |
170185
| Forgetting CLI alias | Must add lowercase entry in `problem_name.rs` `resolve_alias()` |
186+
| Forgetting CLI create | Must add creation handler in `commands/create.rs` and flags in `cli.rs` |
187+
| Schema lists derived fields | Schema should list constructor params, not internal fields (e.g., `matrix, k` not `matrix, m, n, k`) |

Makefile

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,18 @@ cli-demo: cli
270270
\
271271
echo ""; \
272272
echo "--- 8. create: build problem instances ---"; \
273-
$$PRED create MIS --edges 0-1,1-2,2-3,3-4,4-0 -o $(CLI_DEMO_DIR)/mis.json; \
274-
$$PRED create MIS --edges 0-1,1-2,2-3 --weights 2,1,3,1 -o $(CLI_DEMO_DIR)/mis_weighted.json; \
273+
$$PRED create MIS --graph 0-1,1-2,2-3,3-4,4-0 -o $(CLI_DEMO_DIR)/mis.json; \
274+
$$PRED create MIS --graph 0-1,1-2,2-3 --weights 2,1,3,1 -o $(CLI_DEMO_DIR)/mis_weighted.json; \
275275
$$PRED create SAT --num-vars 3 --clauses "1,2;-1,3;2,-3" -o $(CLI_DEMO_DIR)/sat.json; \
276276
$$PRED create 3SAT --num-vars 4 --clauses "1,2,3;-1,2,-3;1,-2,3" -o $(CLI_DEMO_DIR)/3sat.json; \
277277
$$PRED create QUBO --matrix "1,-0.5;-0.5,2" -o $(CLI_DEMO_DIR)/qubo.json; \
278-
$$PRED create KColoring --k 3 --edges 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/kcol.json; \
279-
$$PRED create SpinGlass --edges 0-1,1-2 -o $(CLI_DEMO_DIR)/sg.json; \
280-
$$PRED create MaxCut --edges 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/maxcut.json; \
281-
$$PRED create MVC --edges 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/mvc.json; \
282-
$$PRED create MaximumMatching --edges 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/matching.json; \
283-
$$PRED create Factoring --target 15 --bits-m 4 --bits-n 4 -o $(CLI_DEMO_DIR)/factoring.json; \
284-
$$PRED create Factoring --target 21 --bits-m 3 --bits-n 3 -o $(CLI_DEMO_DIR)/factoring2.json; \
278+
$$PRED create KColoring --k 3 --graph 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/kcol.json; \
279+
$$PRED create SpinGlass --graph 0-1,1-2 -o $(CLI_DEMO_DIR)/sg.json; \
280+
$$PRED create MaxCut --graph 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/maxcut.json; \
281+
$$PRED create MVC --graph 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/mvc.json; \
282+
$$PRED create MaximumMatching --graph 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/matching.json; \
283+
$$PRED create Factoring --target 15 --m 4 --n 4 -o $(CLI_DEMO_DIR)/factoring.json; \
284+
$$PRED create Factoring --target 21 --m 3 --n 3 -o $(CLI_DEMO_DIR)/factoring2.json; \
285285
\
286286
echo ""; \
287287
echo "--- 9. evaluate: test configurations ---"; \
@@ -330,7 +330,7 @@ cli-demo: cli
330330
echo ""; \
331331
echo "--- 18. closed-loop: create → reduce → solve → verify ---"; \
332332
echo "Creating a 6-vertex graph..."; \
333-
$$PRED create MIS --edges 0-1,1-2,2-3,3-4,4-5,0-5,1-4 -o $(CLI_DEMO_DIR)/big.json; \
333+
$$PRED create MIS --graph 0-1,1-2,2-3,3-4,4-5,0-5,1-4 -o $(CLI_DEMO_DIR)/big.json; \
334334
echo "Solving with ILP..."; \
335335
$$PRED solve $(CLI_DEMO_DIR)/big.json -o $(CLI_DEMO_DIR)/big_sol.json; \
336336
echo "Reducing to QUBO and solving with brute-force..."; \

problemreductions-cli/src/cli.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,20 @@ TIP: Run `pred create <PROBLEM>` (no other flags) to see problem-specific help.
203203
Flags by problem type:
204204
MIS, MVC, MaxClique, MinDomSet --graph, --weights
205205
MaxCut, MaxMatching, TSP --graph, --edge-weights
206+
MaximalIS --graph, --weights
206207
SAT, 3SAT/KSAT --num-vars, --clauses [--k]
207208
QUBO --matrix
208209
SpinGlass --graph, --couplings, --fields
209210
KColoring --graph, --k
210211
Factoring --target, --m, --n
212+
BinPacking --sizes, --capacity
213+
PaintShop --sequence
214+
MaximumSetPacking --sets [--weights]
215+
MinimumSetCovering --universe, --sets [--weights]
216+
BicliqueCover --left, --right, --biedges, --k
217+
BMF --matrix (0/1), --rank
218+
CVP --basis, --target-vec
219+
ILP, CircuitSAT (via reduction only)
211220
212221
Geometry graph variants (use slash notation, e.g., MIS/KingsSubgraph):
213222
KingsSubgraph, TriangularSubgraph --positions (integer x,y pairs)
@@ -281,6 +290,39 @@ pub struct CreateArgs {
281290
/// Radius for UnitDiskGraph [default: 1.0]
282291
#[arg(long)]
283292
pub radius: Option<f64>,
293+
/// Item sizes for BinPacking (comma-separated, e.g., "3,3,2,2")
294+
#[arg(long)]
295+
pub sizes: Option<String>,
296+
/// Bin capacity for BinPacking
297+
#[arg(long)]
298+
pub capacity: Option<String>,
299+
/// Car paint sequence for PaintShop (comma-separated, each label appears exactly twice, e.g., "a,b,a,c,c,b")
300+
#[arg(long)]
301+
pub sequence: Option<String>,
302+
/// Sets for SetPacking/SetCovering (semicolon-separated, e.g., "0,1;1,2;0,2")
303+
#[arg(long)]
304+
pub sets: Option<String>,
305+
/// Universe size for MinimumSetCovering
306+
#[arg(long)]
307+
pub universe: Option<usize>,
308+
/// Bipartite graph edges for BicliqueCover (e.g., "0-0,0-1,1-2" for left-right pairs)
309+
#[arg(long)]
310+
pub biedges: Option<String>,
311+
/// Left partition size for BicliqueCover
312+
#[arg(long)]
313+
pub left: Option<usize>,
314+
/// Right partition size for BicliqueCover
315+
#[arg(long)]
316+
pub right: Option<usize>,
317+
/// Rank for BMF
318+
#[arg(long)]
319+
pub rank: Option<usize>,
320+
/// Lattice basis for CVP (semicolon-separated column vectors, e.g., "1,0;0,1")
321+
#[arg(long)]
322+
pub basis: Option<String>,
323+
/// Target vector for CVP (comma-separated, e.g., "0.5,0.5")
324+
#[arg(long)]
325+
pub target_vec: Option<String>,
284326
}
285327

286328
#[derive(clap::Args)]
@@ -315,7 +357,7 @@ pub struct SolveArgs {
315357
/// Solver: ilp (default) or brute-force
316358
#[arg(long, default_value = "ilp")]
317359
pub solver: String,
318-
/// Timeout in seconds (0 = no limit) [default: 0]
360+
/// Timeout in seconds (0 = no limit)
319361
#[arg(long, default_value = "0")]
320362
pub timeout: u64,
321363
}
@@ -367,7 +409,13 @@ pub struct EvaluateArgs {
367409
}
368410

369411
/// Print the after_help text for a subcommand on parse error.
412+
///
413+
/// Only matches the first line of the error message. Without this,
414+
/// bare `pred` (no subcommand) would match "pred solve" in the
415+
/// top-level workflow examples and incorrectly append the solve
416+
/// subcommand's help text.
370417
pub fn print_subcommand_help_hint(error_msg: &str) {
418+
let first_line = error_msg.lines().next().unwrap_or("");
371419
let subcmds = [
372420
("pred solve", "solve"),
373421
("pred reduce", "reduce"),
@@ -382,7 +430,7 @@ pub fn print_subcommand_help_hint(error_msg: &str) {
382430
];
383431
let cmd = Cli::command();
384432
for (pattern, name) in subcmds {
385-
if error_msg.contains(pattern) {
433+
if first_line.contains(pattern) {
386434
if let Some(sub) = cmd.find_subcommand(name) {
387435
if let Some(help) = sub.get_after_help() {
388436
eprintln!("\n{help}");

0 commit comments

Comments
 (0)