Skip to content

Commit 5fbde0e

Browse files
GiggleLiuclaude
andauthored
fix: CLI QA improvements — creation, aliases, help, schemas (#190)
* 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> * fix: address PR #190 review — test exit codes, ILP/CircuitSAT ordering, edge trim - Update 3 tests to expect non-zero exit when showing help (issue #189 item 9) - Move ILP/CircuitSAT check before empty-flags help so they get the "via reduction" message instead of generic schema help - Trim each side of edge pairs in parse_edge_pairs for robustness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add --bounds flag for CVP creation, consistent with schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 92cdd0a commit 5fbde0e

11 files changed

Lines changed: 375 additions & 58 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: 53 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 [--bounds]
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,42 @@ 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>,
326+
/// Variable bounds for CVP as "lower,upper" (e.g., "-10,10") [default: -10,10]
327+
#[arg(long, allow_hyphen_values = true)]
328+
pub bounds: Option<String>,
284329
}
285330

286331
#[derive(clap::Args)]
@@ -315,7 +360,7 @@ pub struct SolveArgs {
315360
/// Solver: ilp (default) or brute-force
316361
#[arg(long, default_value = "ilp")]
317362
pub solver: String,
318-
/// Timeout in seconds (0 = no limit) [default: 0]
363+
/// Timeout in seconds (0 = no limit)
319364
#[arg(long, default_value = "0")]
320365
pub timeout: u64,
321366
}
@@ -367,7 +412,13 @@ pub struct EvaluateArgs {
367412
}
368413

369414
/// Print the after_help text for a subcommand on parse error.
415+
///
416+
/// Only matches the first line of the error message. Without this,
417+
/// bare `pred` (no subcommand) would match "pred solve" in the
418+
/// top-level workflow examples and incorrectly append the solve
419+
/// subcommand's help text.
370420
pub fn print_subcommand_help_hint(error_msg: &str) {
421+
let first_line = error_msg.lines().next().unwrap_or("");
371422
let subcmds = [
372423
("pred solve", "solve"),
373424
("pred reduce", "reduce"),
@@ -382,7 +433,7 @@ pub fn print_subcommand_help_hint(error_msg: &str) {
382433
];
383434
let cmd = Cli::command();
384435
for (pattern, name) in subcmds {
385-
if error_msg.contains(pattern) {
436+
if first_line.contains(pattern) {
386437
if let Some(sub) = cmd.find_subcommand(name) {
387438
if let Some(help) = sub.get_after_help() {
388439
eprintln!("\n{help}");

0 commit comments

Comments
 (0)